diff --git a/server/Database.js b/server/Database.js index 316880191..4259c4668 100644 --- a/server/Database.js +++ b/server/Database.js @@ -211,34 +211,56 @@ class Database { } /** - * Rebuild all author rows so derived fields stay in sync with the model logic. + * Rebuild rows for a model so hooks can recompute derived fields. + * + * @param {import('sequelize').ModelStatic} model + * @param {{ + * label?: string, + * pageSize?: number, + * attributes?: string[], + * order?: import('sequelize').Order, + * prepareRow?: (row: any) => void | Promise + * }} [options] */ - async rebuildAuthorRows() { - const pageSize = 500 + async rebuildComputedTableRows(model, options = {}) { + const { label = model?.name || 'rows', pageSize = 500, attributes = ['id'], order = [['id', 'ASC']], prepareRow = async () => {} } = options + let offset = 0 + let totalChange = 0 while (true) { - const authors = await this.authorModel.findAll({ - attributes: ['id', 'name', 'libraryId'], - order: [['id', 'ASC']], + const rows = await model.findAll({ + attributes, + order, limit: pageSize, offset }) - if (!authors.length) break + if (!rows.length) break - for (const authorRef of authors) { - const author = await this.authorModel.findByPk(authorRef.id) - if (!author) continue - author.changed('name', true) - await author.save({ silent: true }) + for (const row of rows) { + await prepareRow(row) + if (!row.changed()) continue + totalChange++ + await row.save({ silent: true }) } - offset += authors.length - if (authors.length < pageSize) break + offset += rows.length + if (rows.length < pageSize) break } - Logger.debug(`Sanitized ${offset} author rows`) + Logger.debug(`${totalChange} out of ${offset} ${label} rows required recalculation of derived columns`) + } + + /** + * Rebuild all author rows so derived fields stay in sync with the model logic. + */ + async rebuildAuthorRows() { + await this.rebuildComputedTableRows(this.authorModel, { + label: 'author', + attributes: ['id', 'name', 'lastFirst', 'searchName', 'libraryId'], + prepareRow: (author) => this.authorModel.isDerivedFieldChange(author) + }) } /** diff --git a/server/models/Author.js b/server/models/Author.js index aedeb8777..4d680f27c 100644 --- a/server/models/Author.js +++ b/server/models/Author.js @@ -66,6 +66,25 @@ class Author extends Model { return this.normalizeSearchName(leftName) === this.normalizeSearchName(rightName) } + static isDerivedFieldChange(author) { + const derivedFields = this.buildAuthorDerivedFields(author.name) + let changed = false + + if (author.lastFirst !== derivedFields.lastFirst) { + author.setDataValue('lastFirst', derivedFields.lastFirst) + author.changed('lastFirst', true) + changed = true + } + + if (author.searchName !== derivedFields.searchName) { + author.setDataValue('searchName', derivedFields.searchName) + author.changed('searchName', true) + changed = true + } + + return changed + } + /** * Check if author exists * @param {string} authorId