9197d93a92
- Nytt endpoint GET /v1/inventory/natural-search. - Robust matchning med unaccent + pg_trgm similarity mot display_name, brand och kanoniska ingrediensnamn (sv/en). - Svar byggs deterministiskt från faktisk data: lagringsplats + sublocation om registrerad, trust-state-hedge från befintlig trust-motor. - Server-sidan i18n-katalog för 12 språk. - Tester för svensk träff och noll-träff på flera språk. - Design-dokument docs/30-sökrobusthet-matvaror.md.
71 lines
2.9 KiB
Markdown
71 lines
2.9 KiB
Markdown
# Sökrobusthet för matvaror i hushållslagret
|
||
|
||
> Målbild: användaren kan ställa en fritextfråga om vad som finns hemma och få ett svar som känns mänskligt, visar exakt lagring (FoodTwin) och ärligt berättar hur säker appen är – utan att hitta på något.
|
||
|
||
## Befintliga motorer
|
||
|
||
### Sökmotor: `/v1/inventory`
|
||
- Enkel `ILIKE` mot `inventory_items.display_name` och `brand`.
|
||
- Inga synonymer, ingen fonetisk matchning, ingen felstavningstolerans.
|
||
- Filtrering på `storage_location_id`, `expiry_status`, paginering.
|
||
|
||
### Förbättrad sökmotor: `/v1/inventory/natural-search`
|
||
- Kombinerar `unaccent(...) ILIKE unaccent(...)` med PostgreSQL `pg_trgm`-similarity.
|
||
- Söker även mot kanoniska ingrediensnamn (`name_sv`, `name_en`).
|
||
- Sorterar på `GREATEST(similarity(...))`.
|
||
- Returnerar både strukturerade träffar och ett naturligt-språkligt svar.
|
||
|
||
### Trust-motor (`@app/inventory-engine`)
|
||
- `computeTrust(...)` ger `trustState` (`trusted`/`decaying`/`stale`/`unverified`) och `trustScore` 0–100.
|
||
- Baserat på `confidence`, `verifiedByUser`, ålder och förruttnelseprofil.
|
||
- Används redan i `/v1/inventory` och vid skanning/konsumtion.
|
||
|
||
### FoodTwin-data
|
||
- `storage_locations`: namn, typ, sublocations.
|
||
- `inventory_items`: `storage_location_id`, `sublocation`, kvantitet, enhet, `trust_state`.
|
||
- `canonical_ingredients`: kanoniskt namn, synonymer, hållbarhetsriktlinjer.
|
||
|
||
## Svarsformat
|
||
|
||
`GET /v1/inventory/natural-search?q=mjölk&languageTag=sv-SE`
|
||
|
||
```json
|
||
{
|
||
"query": "mjölk",
|
||
"response": "Jag hittade **Mjölk** i Kylen (mellanmålshyllan). Jag är ganska säker på att den finns kvar.",
|
||
"items": [
|
||
{
|
||
"id": "...",
|
||
"displayName": "Mjölk",
|
||
"quantity": 1,
|
||
"unit": "LITER",
|
||
"locationName": "Kylen",
|
||
"locationType": "fridge",
|
||
"sublocation": "mellanmålshyllan",
|
||
"trustState": "trusted",
|
||
"trustScore": 95
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
## i18n
|
||
|
||
Server-sidan använder en minimal katalog i `apps/api/src/lib/inventorySearchResponse.ts`.
|
||
Stödja språk (12): sv, en, da, de, es, fi, fr, it, nb, nl, pl, pt.
|
||
Vid okänt språk faller vi tillbaka till engelska och sedan svenska.
|
||
|
||
## Hårda regler
|
||
|
||
1. **Inga påhittade fakta.** Om sublocation saknas visas inte "på hylla X".
|
||
2. **Trust-hedge måste matcha `trustState`.** `stale` ger en osäker formulering; `trusted` ger en trygg.
|
||
3. **Noll träffar** ska erkänna det och erbjuda att lägga till.
|
||
4. **Flera träffar** ska lista dem med lagring och peka på listan för trust-score.
|
||
|
||
## Nästa steg / öppna frågor
|
||
|
||
- Ska vi också söka i `canonical_ingredients.aliases` (array)? Kräver en `unnest`/`CROSS JOIN LATERAL` eller en trigram-index på en materialiserad vy.
|
||
- Ska vi lägga till fonetisk matchning (`fuzzystrmatch`) för vanliga felstavningar?
|
||
- Ska svaret också inkludera hållbarhetsstatus för träffarna?
|
||
- Behövs en separat vector/embedding-sökning för semantiska matchningar (t.ex. "mjölkprodukter")?
|