|
@@ -2,13 +2,17 @@
|
|
|
import minimist from 'minimist'
|
|
import minimist from 'minimist'
|
|
|
import logger from '@overleaf/logger'
|
|
import logger from '@overleaf/logger'
|
|
|
import { db } from '../app/src/infrastructure/mongodb.mjs'
|
|
import { db } from '../app/src/infrastructure/mongodb.mjs'
|
|
|
-import { buildSearchFields } from '../modules/library/app/src/LibraryReferenceRepository.mts'
|
|
|
|
|
|
|
+import {
|
|
|
|
|
+ buildSearchTokens,
|
|
|
|
|
+ docSchema,
|
|
|
|
|
+} from '../modules/library/app/src/LibraryReferenceRepository.mts'
|
|
|
|
|
+import { tokenize } from '../modules/library/app/src/bibtex-search-tokens.mts'
|
|
|
import { scriptRunner } from './lib/ScriptRunner.mjs'
|
|
import { scriptRunner } from './lib/ScriptRunner.mjs'
|
|
|
|
|
|
|
|
/** @typedef {import('mongodb').AnyBulkWriteOperation} AnyBulkWriteOperation */
|
|
/** @typedef {import('mongodb').AnyBulkWriteOperation} AnyBulkWriteOperation */
|
|
|
|
|
|
|
|
const argv = minimist(process.argv.slice(2), {
|
|
const argv = minimist(process.argv.slice(2), {
|
|
|
- boolean: ['commit', 'rollback', 'all', 'help'],
|
|
|
|
|
|
|
+ boolean: ['commit', 'all', 'help'],
|
|
|
default: { 'batch-size': 1000 },
|
|
default: { 'batch-size': 1000 },
|
|
|
})
|
|
})
|
|
|
|
|
|
|
@@ -17,16 +21,15 @@ function usage() {
|
|
|
{},
|
|
{},
|
|
|
`Usage: node backfill_library_references_search.mjs [options]
|
|
`Usage: node backfill_library_references_search.mjs [options]
|
|
|
|
|
|
|
|
-Populates searchKey and fields.searchValue on libraryReferences so the
|
|
|
|
|
-account-level library search can index them. Safe to rerun; picks up only
|
|
|
|
|
-un-indexed rows by default.
|
|
|
|
|
|
|
+Populates searchKey and searchTokens on libraryReferences so the
|
|
|
|
|
+account-level library search can index them. Also unsets the obsolete
|
|
|
|
|
+fields.$[].searchValue. Safe to rerun; picks up only un-indexed rows
|
|
|
|
|
+by default.
|
|
|
|
|
|
|
|
Options:
|
|
Options:
|
|
|
--commit Apply changes. Without this, runs as a dry run.
|
|
--commit Apply changes. Without this, runs as a dry run.
|
|
|
- --rollback Unset searchKey and fields.searchValue on all rows that
|
|
|
|
|
- have them. Mirrors the original migration's rollback.
|
|
|
|
|
- --all Re-index every row, not just rows where searchKey is null.
|
|
|
|
|
- Use when the tokenization format has changed.
|
|
|
|
|
|
|
+ --all Re-index every row, not just rows where searchTokens
|
|
|
|
|
+ is null. Use when the tokenization format has changed.
|
|
|
--batch-size <n> bulkWrite batch size (default 1000).
|
|
--batch-size <n> bulkWrite batch size (default 1000).
|
|
|
`
|
|
`
|
|
|
)
|
|
)
|
|
@@ -41,11 +44,10 @@ const BATCH_SIZE = Number(argv['batch-size'])
|
|
|
|
|
|
|
|
/** @param {(message: string) => Promise<void>} trackProgress */
|
|
/** @param {(message: string) => Promise<void>} trackProgress */
|
|
|
async function backfill(trackProgress) {
|
|
async function backfill(trackProgress) {
|
|
|
- const filter = argv.all ? {} : { searchKey: null }
|
|
|
|
|
|
|
+ const filter = argv.all ? {} : { searchTokens: null }
|
|
|
const cursor = db.libraryReferences
|
|
const cursor = db.libraryReferences
|
|
|
.find(filter)
|
|
.find(filter)
|
|
|
- .hint({ userId: 1, searchKey: 1 })
|
|
|
|
|
- .project({ key: 1, fields: 1 })
|
|
|
|
|
|
|
+ .project({ key: 1, type: 1, fields: 1, updatedAt: 1 })
|
|
|
|
|
|
|
|
let processed = 0
|
|
let processed = 0
|
|
|
/** @type {AnyBulkWriteOperation[]} */
|
|
/** @type {AnyBulkWriteOperation[]} */
|
|
@@ -64,19 +66,20 @@ async function backfill(trackProgress) {
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
for await (const doc of cursor) {
|
|
for await (const doc of cursor) {
|
|
|
- const { searchKey, fields } = buildSearchFields({
|
|
|
|
|
- key: doc.key,
|
|
|
|
|
- fields: (doc.fields ?? []).map(
|
|
|
|
|
- (/** @type {{ name: string; editableValue?: string }} */ f) => ({
|
|
|
|
|
- name: f.name,
|
|
|
|
|
- editableValue: f.editableValue ?? '',
|
|
|
|
|
- })
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ const entry = docSchema.parse({
|
|
|
|
|
+ ...doc,
|
|
|
|
|
+ type: doc.type ?? 'misc',
|
|
|
|
|
+ updatedAt: doc.updatedAt ?? new Date(0),
|
|
|
})
|
|
})
|
|
|
|
|
+ const searchKey = tokenize(doc.key)
|
|
|
|
|
+ const searchTokens = buildSearchTokens(entry)
|
|
|
ops.push({
|
|
ops.push({
|
|
|
updateOne: {
|
|
updateOne: {
|
|
|
filter: { _id: doc._id },
|
|
filter: { _id: doc._id },
|
|
|
- update: { $set: { searchKey, fields } },
|
|
|
|
|
|
|
+ update: {
|
|
|
|
|
+ $set: { searchKey, searchTokens },
|
|
|
|
|
+ $unset: { 'fields.$[].searchValue': 1 },
|
|
|
|
|
+ },
|
|
|
},
|
|
},
|
|
|
})
|
|
})
|
|
|
if (ops.length >= BATCH_SIZE) {
|
|
if (ops.length >= BATCH_SIZE) {
|
|
@@ -87,39 +90,17 @@ async function backfill(trackProgress) {
|
|
|
await trackProgress(`done; processed ${processed} docs`)
|
|
await trackProgress(`done; processed ${processed} docs`)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-/** @param {(message: string) => Promise<void>} trackProgress */
|
|
|
|
|
-async function rollback(trackProgress) {
|
|
|
|
|
- if (!argv.commit) {
|
|
|
|
|
- const count = await db.libraryReferences.countDocuments({
|
|
|
|
|
- searchKey: { $ne: null },
|
|
|
|
|
- })
|
|
|
|
|
- await trackProgress(`[dry-run] would unset search fields on ${count} docs`)
|
|
|
|
|
- return
|
|
|
|
|
- }
|
|
|
|
|
- const result = await db.libraryReferences.updateMany(
|
|
|
|
|
- { searchKey: { $ne: null } },
|
|
|
|
|
- { $unset: { searchKey: 1, 'fields.$[].searchValue': 1 } },
|
|
|
|
|
- { hint: { userId: 1, searchKey: 1 } }
|
|
|
|
|
- )
|
|
|
|
|
- await trackProgress(`unset search fields on ${result.modifiedCount} docs`)
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
/** @param {(message: string) => Promise<void>} trackProgress */
|
|
/** @param {(message: string) => Promise<void>} trackProgress */
|
|
|
async function main(trackProgress) {
|
|
async function main(trackProgress) {
|
|
|
if (!argv.commit) {
|
|
if (!argv.commit) {
|
|
|
await trackProgress('DRY RUN. Pass --commit to apply changes.')
|
|
await trackProgress('DRY RUN. Pass --commit to apply changes.')
|
|
|
}
|
|
}
|
|
|
- if (argv.rollback) {
|
|
|
|
|
- await rollback(trackProgress)
|
|
|
|
|
- } else {
|
|
|
|
|
- await backfill(trackProgress)
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ await backfill(trackProgress)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
try {
|
|
|
await scriptRunner(main, {
|
|
await scriptRunner(main, {
|
|
|
commit: Boolean(argv.commit),
|
|
commit: Boolean(argv.commit),
|
|
|
- rollback: Boolean(argv.rollback),
|
|
|
|
|
all: Boolean(argv.all),
|
|
all: Boolean(argv.all),
|
|
|
batchSize: BATCH_SIZE,
|
|
batchSize: BATCH_SIZE,
|
|
|
})
|
|
})
|