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

@@ -0,0 +1,127 @@
CREATE TABLE "groceries"."synonyms" (
"id" serial PRIMARY KEY,
"term" text NOT NULL,
"synonym" text NOT NULL,
"created_at" timestamp with time zone DEFAULT now() NOT NULL,
"updated_at" timestamp with time zone DEFAULT now() NOT NULL,
CONSTRAINT "synonyms_term_check" CHECK ("term" ~ '^[a-z0-9][a-z0-9 -]{0,40}$'),
CONSTRAINT "synonyms_synonym_check" CHECK ("synonym" ~ '^[a-z0-9][a-z0-9 -]{0,40}$'),
CONSTRAINT "synonyms_not_self" CHECK ("term" <> "synonym")
);
--> statement-breakpoint
CREATE UNIQUE INDEX "synonyms_term_synonym_key" ON "groceries"."synonyms" ("term","synonym");
--> statement-breakpoint
-- Starter synonyms for search ranking (search.ts). Edit freely; the app caches them for 5 minutes.
INSERT INTO "groceries"."synonyms" ("term", "synonym") VALUES
('afwasmiddel', 'vaatwasmiddel'),
('bacon', 'ontbijtspek'),
('bacon', 'spek'),
('champignon', 'champignons'),
('champignons', 'champignon'),
('chocoladepasta', 'chocopasta'),
('chocopasta', 'chocoladepasta'),
('dijfilet', 'kipdij'),
('dijfilet', 'kippedij'),
('dijfilet', 'kippendij'),
('drumstick', 'kipdrumstick'),
('drumstick', 'kippenpoot'),
('drumstick', 'kippenpoten'),
('eieren', 'eitjes'),
('eitjes', 'eieren'),
('friet', 'frites'),
('friet', 'patat'),
('frites', 'friet'),
('frites', 'patat'),
('kaas', 'belegen'),
('kaas', 'edammer'),
('kaas', 'goudse'),
('kipdij', 'dijfilet'),
('kipdij', 'kippedij'),
('kipdij', 'kippendij'),
('kipdrumstick', 'drumstick'),
('kipdrumstick', 'kippenpoot'),
('kipdrumstick', 'kippenpoten'),
('kipfilet', 'kippefilet'),
('kipfilet', 'kippenfilet'),
('kippedij', 'dijfilet'),
('kippedij', 'kipdij'),
('kippedij', 'kippendij'),
('kippefilet', 'kipfilet'),
('kippefilet', 'kippenfilet'),
('kippendij', 'dijfilet'),
('kippendij', 'kipdij'),
('kippendij', 'kippedij'),
('kippenfilet', 'kipfilet'),
('kippenfilet', 'kippefilet'),
('kippenpoot', 'drumstick'),
('kippenpoot', 'kipdrumstick'),
('kippenpoot', 'kippenpoten'),
('kippenpoten', 'drumstick'),
('kippenpoten', 'kipdrumstick'),
('kippenpoten', 'kippenpoot'),
('komkommer', 'komkommers'),
('komkommers', 'komkommer'),
('mayo', 'mayonaise'),
('mayonaise', 'mayo'),
('mie', 'noedels'),
('mie', 'noodles'),
('noedels', 'mie'),
('noedels', 'noodles'),
('noodles', 'mie'),
('noodles', 'noedels'),
('ontbijtspek', 'bacon'),
('ontbijtspek', 'spek'),
('paprika', 'paprikas'),
('paprikas', 'paprika'),
('pasta', 'farfalle'),
('pasta', 'fettuccine'),
('pasta', 'fusilli'),
('pasta', 'linguine'),
('pasta', 'macaroni'),
('pasta', 'penne'),
('pasta', 'rigatoni'),
('pasta', 'spaghetti'),
('pasta', 'tagliatelle'),
('patat', 'friet'),
('patat', 'frites'),
('pindakaas', 'pindapasta'),
('pindapasta', 'pindakaas'),
('rijst', 'basmati'),
('rijst', 'pandan'),
('rijst', 'zilvervlies'),
('rundergehakt', 'rundgehakt'),
('rundgehakt', 'rundergehakt'),
('sojasaus', 'soy'),
('sojasaus', 'soyasaus'),
('soy', 'sojasaus'),
('soy', 'soyasaus'),
('soyasaus', 'sojasaus'),
('soyasaus', 'soy'),
('spek', 'bacon'),
('spek', 'ontbijtspek'),
('toiletpapier', 'wc'),
('tomaat', 'tomaten'),
('tomaten', 'tomaat'),
('tortilla', 'tortillas'),
('tortilla', 'wraps'),
('tortillas', 'tortilla'),
('tortillas', 'wraps'),
('vaatwasmiddel', 'afwasmiddel'),
('wc', 'toiletpapier'),
('wraps', 'tortilla'),
('wraps', 'tortillas'),
('yoghourt', 'yoghurt'),
('yoghourt', 'yogurt'),
('yoghurt', 'yoghourt'),
('yoghurt', 'yogurt'),
('yogurt', 'yoghourt'),
('yogurt', 'yoghurt')
ON CONFLICT DO NOTHING;
--> statement-breakpoint
DO $$
BEGIN
IF current_user = 'personal' AND EXISTS (SELECT 1 FROM pg_roles WHERE rolname = 'personal_app') THEN
GRANT SELECT, INSERT, UPDATE, DELETE ON "groceries"."synonyms" TO personal_app;
GRANT USAGE, SELECT ON SEQUENCE "groceries"."synonyms_id_seq" TO personal_app;
END IF;
END $$;