groceries: relevance-ranked search (plain/basic products first)
All checks were successful
Deploy production / deploy (push) Successful in 1m13s

- search.ts: score on the core name (brand/size stripped): exact or query+base suffix
  (kipdij+filet), head-final compounds, prefix, inside; penalties per extra word and per
  flavour/processing word (search-words.ts, editable); bonuses for store brands, the
  query's dominant category, store count, favourites; trigram tiebreak
- groceries.synonyms table (seeded: kipdij/kippendij, pasta → spaghetti, …) expands queries
- list rows show all pack sizes of a group; group picker uses the same ranking
- scripts/groceries-search-eval.ts prints top results + latency; preflight now 8 tables
This commit is contained in:
2026-10-25 20:20:34 +00:00
parent 5028524537
commit 60370d4572
14 changed files with 31221 additions and 16 deletions

View File

@@ -256,8 +256,30 @@ export const groceryImportRuns = groceriesSchema.table(
(t) => [index('import_runs_store_idx').on(t.storeId, t.startedAt)]
);
/**
* Search synonyms (search.ts): a query word `term` also finds products with `synonym`.
* One-directional (pasta → spaghetti); spelling variants are stored both ways. Folded,
* lower-case words; edits apply within 5 minutes (cached).
*/
export const grocerySynonyms = groceriesSchema.table(
'synonyms',
{
id: id(),
term: text('term').notNull(),
synonym: text('synonym').notNull(),
...timestamps
},
(t) => [
uniqueIndex('synonyms_term_synonym_key').on(t.term, t.synonym),
check('synonyms_term_check', sql`${t.term} ~ '^[a-z0-9][a-z0-9 -]{0,40}$'`),
check('synonyms_synonym_check', sql`${t.synonym} ~ '^[a-z0-9][a-z0-9 -]{0,40}$'`),
check('synonyms_not_self', sql`${t.term} <> ${t.synonym}`)
]
);
export type GroceryStore = typeof groceryStores.$inferSelect;
export type GroceryGroup = typeof groceryGroups.$inferSelect;
export type GroceryItem = typeof groceryItems.$inferSelect;
export type GroceryCategory = typeof groceryCategories.$inferSelect;
export type GroceryManualPrice = typeof groceryManualPrices.$inferSelect;
export type GrocerySynonym = typeof grocerySynonyms.$inferSelect;