Table of Contents

Namespace UsluzionicaServer.Infrastructure.Search

Classes

CategorySearchIndex

Foldovana imena kategorija, u memoriji.

Zašto ime kategorije NIJE denormalizovano u Listing.SearchTitle: kategorija je zajednička za mnogo oglasa, pa bi admin preimenovanjem jedne kategorije ustajao svaki oglas u njoj — a preimenovanje ne prolazi kroz oglase, pa bi indeks tiho ostao pogrešan. 188 kategorija stane u memoriju bez problema.

Poklapanje je PO TOKENU, ne unija. Za upit „frizer beograd" token „frizer" pogađa kategoriju, a „beograd" ne — pa oglas mora zadovoljiti oba uslova zasebno. Da se vraćala unija, upit bi vratio sve iz te kategorije bez obzira na grad.

Singleton sa TTL-om od 10 minuta. Podaci OSTAJU u memoriji svakog procesa namerno: pogađa se na svakom tokenu svake pretrage, pa mrežni poziv po tokenu ne dolazi u obzir.

Faza B je rešila deljenje: instanca koja izmeni kategorije objavi poruku preko CacheInvalidator, i SVE instance odmah očiste svoju kopiju. TTL ostaje kao rezerva kad Redis nije dostupan.

Fuzzy

Tolerancija na sitne greške u kucanju: frizerr, frizre, vodoinstalter.

Radi u dva koraka, jer nijedan sam nije upotrebljiv:

  1. SQL prefilter po pigeonhole principu — sužava kandidate, ne može promašiti tačan rezultat
  2. OSA rastojanje in-memory nad tim suženim skupom
SearchIndexBackfill

Popunjava Search* kolone za redove koji su indeksirani starom verzijom pravila (ili nikad).

Zašto uopšte postoji: SQL Server ne može da izvrši C# funkciju Fold(), pa se migracija ne može napisati kao SQL UPDATE. Alternativa bi bila lanac od ~40 ugnježdenih REPLACE-ova u T-SQL-u, koji bi bio collation-zavisan, ne bi umeo NFD, i logika bi živela na dva mesta koja se tiho razilaze.

Zašto SearchVersion: kad se pravila preklapanja promene (nova slova, druga mapa), dovoljno je podići SearchNormalizer.Version — sledeći start sam re-indeksira sve. Bez toga bi svaka promena tražila ručnu intervenciju nad produkcijskom bazom.

Idempotentno: ponovni poziv nad već indeksiranim redovima ne radi ništa.

SearchIndexer

Popunjava denormalizovane Search* kolone.

Poziva se iz AppDbContext.SaveChanges, a NE iz servisa. Razlog: da se održavanje indeksa radi u ListingService.CreateAsync/UpdateAsync, svaki budući put pisanja (admin izmena, seed, ručna popravka podataka) bio bi jedna zaboravljena linija od tihog raspada pretrage — oglas bi postojao u bazi ali se ne bi mogao naći.

Ovako je nemoguće zaobići: sve što prođe kroz change tracker dobija svež indeks.

SearchNormalizer

Svodi tekst na oblik pogodan za pretragu: mala slova, čist ASCII, bez dijakritike, ćirilica preslovljena u latinicu.

Cilj je da upit i sadržaj oglasa završe u ISTOM obliku, pa da: "sisanje" nađe "Šišanje" "фризер" nađe "Frizerski salon" "Đorđe" nađe "djordje"

Ova klasa je jedini izvor istine za preklapanje. Isti Fold() se koristi i pri upisu u bazu (denormalizovane Search* kolone) i pri obradi upita — da se dve strane ne mogu razići.

SearchQuery

Pripremljen korisnički upit: foldovan, razložen na tokene, sa varijantama i sa fragmentima za fuzzy prefilter.

Priprema se JEDNOM po zahtevu, pa se koristi u više slojeva pretrage — da se isti posao ne ponavlja za svaki sloj.

SearchQuery.Token

Jedan token upita sa svime što slojevi pretrage traže od njega.