From 5378e64eab67e0b101321e3f3a745b0cdb7cd098 Mon Sep 17 00:00:00 2001 From: Nicholas Wallace Date: Sun, 31 May 2026 08:38:42 -0700 Subject: [PATCH] Move normalization functions into migration file for rollback --- .../v2.34.0-add-author-search-name.js | 39 ++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/server/migrations/v2.34.0-add-author-search-name.js b/server/migrations/v2.34.0-add-author-search-name.js index 1b691478a..7452d0554 100644 --- a/server/migrations/v2.34.0-add-author-search-name.js +++ b/server/migrations/v2.34.0-add-author-search-name.js @@ -21,6 +21,43 @@ const AUTHOR_SEARCH_INDEX = 'author_search_name' const AUTHOR_LAST_FIRST_INDEX = 'author_last_first' const UNIQUE_SEARCH_INDEX = 'unique_author_search_name_per_library' +/** + * Remove all punctionation, diacritics, and whitespace and convert to lowercase for searching and matching + * (copied from Author model to ensure consistent normalization between the migration and the model) + * @param {string} name + * @returns {string} + */ +async function normalizeSearchName(name) { + if (!name?.trim()) return null + return name + .normalize('NFKC') // Standardize compatibility characters + .normalize('NFD') // Split accents into combining marks + .toLocaleLowerCase('und') + .replace(/[\p{P}\p{Z}\p{M}\s]+/gu, '') // Remove punctuation, whitespace, and diacritics + .trim() +} + +/** + * Calculate derived fields. Returns null if name is empty after normalization + * (copied from Author model to ensure consistent normalization between the migration and the model) + * @param {string} name + * @returns { lastFirst: string?, searchName: string? } + */ +async function buildAuthorDerivedFields(name) { + const searchName = normalizeSearchName(name) + if (!searchName) { + return { + lastFirst: null, // populated after migration complete by server startup + searchName: null + } + } + + return { + lastFirst: null, // populated after migration complete by server startup + searchName + } +} + async function indexExists(queryInterface, tableName, indexName) { const indexes = await queryInterface.showIndex(tableName) return indexes.some((index) => index.name === indexName) @@ -62,7 +99,7 @@ async function backfillAuthorSearchName(queryInterface, logger, transaction, Aut logger.info(`${loggerPrefix} backfilling derived author fields for ${authors.length} authors`) for (const author of authors) { - const derivedFields = Author.buildAuthorDerivedFields(author.name) + const derivedFields = buildAuthorDerivedFields(author.name) await queryInterface.sequelize.query( `UPDATE ${AUTHORS_TABLE} SET lastFirst = :lastFirst,