diff --git a/src/automats/articleSearchAutomat.js b/src/automats/articleSearchAutomat.js index 7506fe7..f78ea25 100644 --- a/src/automats/articleSearchAutomat.js +++ b/src/automats/articleSearchAutomat.js @@ -16,6 +16,9 @@ import articleIndexAutomat from './articleIndexAutomat'; import categoryAutomat from './categoryAutomat'; import { parseFilter } from '../components/article/searchFormat'; +// #region Types & Initial State +// MARK: typedefs, initial state, and FlexSearch instance variables + /** * @typedef {Object} ArticleSearchResult * @property {Record} categoryFacets Facet counts per category id. @@ -67,7 +70,12 @@ const { Document, Charset } = FlexSearch; let flexDocument = null; let lastIndexedArticles = null; let articleByInternalId = new Map(); +// #endregion +// #region Helpers + +// #region getOrCreateFlexIndex +// MARK: constructs or retrieves FlexSearch document index /** * Lazily constructs and maintains the FlexSearch Document index for the given articles array. * @@ -120,7 +128,10 @@ function getOrCreateFlexIndex(articles) { articleByInternalId = byId; return { doc, byId }; } +// #endregion +// #region queryFlexSearch +// MARK: queries FlexSearch document index /** * Searches the FlexSearch index with the query string. * @@ -158,7 +169,10 @@ function queryFlexSearch(query, articles) { } return matchesList; } +// #endregion +// #region getArticleRelevanceScore +// MARK: calculates search relevance score for an article /** * Calculates a search relevance score for an article record against a query. * @@ -200,7 +214,10 @@ export function getArticleRelevanceScore(article, query) { return 10; } +// #endregion +// #region sortArticlesByRelevance +// MARK: sorts articles by relevance score, name, and sku /** * Sorts articles by relevance to the query, breaking ties alphabetically by name and SKU. * @@ -225,7 +242,10 @@ export function sortArticlesByRelevance(articles, query) { return String(a.sku ?? '').localeCompare(String(b.sku ?? ''), 'de'); }); } +// #endregion +// #region searchArticlesIndex +// MARK: calls articleIndexAutomat.actions.ensureLoaded /** * Synchronously searches the article index using the search automat token matching and relevance order. * @@ -308,7 +328,10 @@ export function searchArticlesIndex(query, options = {}) { return recordset; } +// #endregion +// #region isRootCategory +// MARK: checks if category id or name represents the root category /** * Determines whether a category ID or name represents the root category. * @@ -326,7 +349,10 @@ function isRootCategory(id, rootCategoryId, metaName) { } return false; } +// #endregion +// #region getCategoryAndDescendantIds +// MARK: recursively collects category id and descendant category ids /** * Recursively collects categoryId and all its descendant category IDs. * @@ -404,7 +430,10 @@ export function getCategoryAndDescendantIds(selectedCategories, categoriesMap, f return allIds; } +// #endregion +// #region matchesCategory +// MARK: checks whether article matches expanded category ids /** * Checks whether an article belongs to at least one of the target categories. * @@ -418,7 +447,10 @@ function matchesCategory(row, expandedCategoryIds) { const rowCats = String(row.categories).split(',').map((s) => s.trim()); return rowCats.some((catId) => expandedCategoryIds.has(String(catId))); } +// #endregion +// #region buildCategoryFacets +// MARK: computes category facet counts from matched articles /** * Computes facet counts for each category represented in matched articles. * @@ -463,8 +495,14 @@ function buildCategoryFacets(matchedArticles) { return facets; } +// #endregion +// #endregion + +// #region Actions const actions = { + // #region runSearch + // MARK: runs in-memory search and derivation synchronously /** * Executes in-memory search and derivation synchronously. * @@ -537,7 +575,10 @@ const actions = { return result; }, + // #endregion + // #region search + // MARK: calls articleIndexAutomat.actions.ensureLoaded /** * Runs a client-side search over the article index with pagination. * @@ -562,7 +603,10 @@ const actions = { return actions.runSearch(params); }, + // #endregion + // #region setPageSize + // MARK: updates page size and re-derives result set /** * Updates the page size and re-derives the result set. * @@ -572,7 +616,10 @@ const actions = { setPageSize(pageSize) { return actions.runSearch({ pageSize }); }, + // #endregion + // #region setPage + // MARK: sets 1-based page number and re-derives result set /** * Sets page number (1-based) and re-derives the result set. * @@ -584,7 +631,10 @@ const actions = { const offset = Math.max(0, (page - 1) * safeSize); return actions.runSearch({ offset }); }, + // #endregion + // #region setOffset + // MARK: sets 0-based offset and re-derives result set /** * Sets 0-based offset and re-derives the result set. * @@ -594,7 +644,10 @@ const actions = { setOffset(offset) { return actions.runSearch({ offset }); }, + // #endregion + // #region reset + // MARK: resets search state and clears FlexSearch index /** Reset the search state and clear FlexSearch index. */ reset() { flexDocument = null; @@ -602,7 +655,12 @@ const actions = { articleByInternalId = new Map(); articleSearchAutomat.setState(initialState); }, + // #endregion }; +// #endregion + +// #region Automat Instance & Subscriptions +// MARK: initializes Automat instance and subscribes to articleIndexAutomat & categoryAutomat /** * Article search and pagination state for the `/artikel` overview page. @@ -625,6 +683,10 @@ categoryAutomat.subscribe((catState) => { actions.runSearch(); } }); +// #endregion + +// #region Selectors & Exports +// MARK: selectSearchResult selector and default export /** * Selects the latest search result. @@ -637,3 +699,4 @@ export function selectSearchResult(state) { } export default articleSearchAutomat; +// #endregion