This commit is contained in:
seb
2026-10-18 03:51:53 +02:00
parent 1b4b31b016
commit 33684fc860

View File

@@ -16,6 +16,9 @@ import articleIndexAutomat from './articleIndexAutomat';
import categoryAutomat from './categoryAutomat'; import categoryAutomat from './categoryAutomat';
import { parseFilter } from '../components/article/searchFormat'; import { parseFilter } from '../components/article/searchFormat';
// #region Types & Initial State
// MARK: typedefs, initial state, and FlexSearch instance variables
/** /**
* @typedef {Object} ArticleSearchResult * @typedef {Object} ArticleSearchResult
* @property {Record<string,object>} categoryFacets Facet counts per category id. * @property {Record<string,object>} categoryFacets Facet counts per category id.
@@ -67,7 +70,12 @@ const { Document, Charset } = FlexSearch;
let flexDocument = null; let flexDocument = null;
let lastIndexedArticles = null; let lastIndexedArticles = null;
let articleByInternalId = new Map(); 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. * Lazily constructs and maintains the FlexSearch Document index for the given articles array.
* *
@@ -120,7 +128,10 @@ function getOrCreateFlexIndex(articles) {
articleByInternalId = byId; articleByInternalId = byId;
return { doc, byId }; return { doc, byId };
} }
// #endregion
// #region queryFlexSearch
// MARK: queries FlexSearch document index
/** /**
* Searches the FlexSearch index with the query string. * Searches the FlexSearch index with the query string.
* *
@@ -158,7 +169,10 @@ function queryFlexSearch(query, articles) {
} }
return matchesList; 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. * Calculates a search relevance score for an article record against a query.
* *
@@ -200,7 +214,10 @@ export function getArticleRelevanceScore(article, query) {
return 10; 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. * 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'); 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. * 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; 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. * Determines whether a category ID or name represents the root category.
* *
@@ -326,7 +349,10 @@ function isRootCategory(id, rootCategoryId, metaName) {
} }
return false; return false;
} }
// #endregion
// #region getCategoryAndDescendantIds
// MARK: recursively collects category id and descendant category ids
/** /**
* Recursively collects categoryId and all its descendant category IDs. * Recursively collects categoryId and all its descendant category IDs.
* *
@@ -404,7 +430,10 @@ export function getCategoryAndDescendantIds(selectedCategories, categoriesMap, f
return allIds; 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. * 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()); const rowCats = String(row.categories).split(',').map((s) => s.trim());
return rowCats.some((catId) => expandedCategoryIds.has(String(catId))); 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. * Computes facet counts for each category represented in matched articles.
* *
@@ -463,8 +495,14 @@ function buildCategoryFacets(matchedArticles) {
return facets; return facets;
} }
// #endregion
// #endregion
// #region Actions
const actions = { const actions = {
// #region runSearch
// MARK: runs in-memory search and derivation synchronously
/** /**
* Executes in-memory search and derivation synchronously. * Executes in-memory search and derivation synchronously.
* *
@@ -537,7 +575,10 @@ const actions = {
return result; return result;
}, },
// #endregion
// #region search
// MARK: calls articleIndexAutomat.actions.ensureLoaded
/** /**
* Runs a client-side search over the article index with pagination. * Runs a client-side search over the article index with pagination.
* *
@@ -562,7 +603,10 @@ const actions = {
return actions.runSearch(params); 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. * Updates the page size and re-derives the result set.
* *
@@ -572,7 +616,10 @@ const actions = {
setPageSize(pageSize) { setPageSize(pageSize) {
return actions.runSearch({ 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. * 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); const offset = Math.max(0, (page - 1) * safeSize);
return actions.runSearch({ offset }); 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. * Sets 0-based offset and re-derives the result set.
* *
@@ -594,7 +644,10 @@ const actions = {
setOffset(offset) { setOffset(offset) {
return actions.runSearch({ 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 the search state and clear FlexSearch index. */
reset() { reset() {
flexDocument = null; flexDocument = null;
@@ -602,7 +655,12 @@ const actions = {
articleByInternalId = new Map(); articleByInternalId = new Map();
articleSearchAutomat.setState(initialState); 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. * Article search and pagination state for the `/artikel` overview page.
@@ -625,6 +683,10 @@ categoryAutomat.subscribe((catState) => {
actions.runSearch(); actions.runSearch();
} }
}); });
// #endregion
// #region Selectors & Exports
// MARK: selectSearchResult selector and default export
/** /**
* Selects the latest search result. * Selects the latest search result.
@@ -637,3 +699,4 @@ export function selectSearchResult(state) {
} }
export default articleSearchAutomat; export default articleSearchAutomat;
// #endregion