cry-search 2.1.0 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -387,6 +387,7 @@ src/
387
387
  │ │ ├── searchAndRank.ts # Wrapper w/ exactKeysFn boost + fallback
388
388
  │ │ ├── scoring.ts # scoreOne, scoreOrderAndPresence, scoreBag
389
389
  │ │ ├── syncSearchArrayMetadata.ts
390
+ │ │ ├── matchMany.ts # Mass matching (matchMany / matchManySync)
390
391
  │ │ └── AugmentedMetadata.ts
391
392
  │ └── SearchUniverse.ts # Multi-collection manager
392
393
  ├── utils/
@@ -399,11 +400,29 @@ src/
399
400
  │ ├── sanitiseString.ts
400
401
  │ ├── tokenize.ts
401
402
  │ └── compareTokenized.ts # Jaccard token-set similarity (0..1)
403
+ ├── vet/ # Domain extension, published as `cry-search/vet`
404
+ │ ├── index.ts # Public API of the subpath
405
+ │ ├── spec.ts # vetArtikelSpec / vetQueryOpts (field normalizers)
406
+ │ ├── normalizatorji.ts # Units, decimals, brands, quantities, sizes, species
407
+ │ ├── ocena.ts # oceniPar: score + acceptance rule + reason
408
+ │ ├── uparjanje.ts # pripraviVetIndeks, upariArtikle/upariVse(Sync)
409
+ │ └── data/ # Generated tables (enote.ts, znamke.gen.ts)
402
410
  ├── types/
403
411
  │ └── index.ts
404
412
  └── index.ts # Public API
405
413
  ```
406
414
 
415
+ The core stays domain-free. It only exposes narrow seams for domain modules:
416
+ `SearchableObjectSpec.valueNormalizers` (index side, may return `{ text, classes }`),
417
+ `SearchOpts.queryNormalizers` + `normalizeQueryWithOpts` (query side) and `matchMany` /
418
+ `matchManySync` (mass matching with optional domain `rank`/`accept`). Soft domain data lives
419
+ in `metadata.classes` (never in the token stream, so retrieval speed is unaffected) and is
420
+ read back with `itemClasses(metadata, id)`. Domain rules belong in `src/vet/` (see `VET.md`).
421
+
422
+ Build note: `cry-vetzdravila` is an optional peer dependency and is marked external in the
423
+ bundles (`bun build --external cry-vetzdravila`), so core consumers do not pull in its
424
+ `xlsx`/`pdfjs-dist` dependencies.
425
+
407
426
  ## Coding rules
408
427
 
409
428
  1. each function is in separate file
@@ -415,6 +434,13 @@ src/
415
434
 
416
435
  ## Testing
417
436
 
437
+ 0. Quality first: `bun test` — thresholds for `cry-search/vet` live in
438
+ `test/vet/pari.test.ts` (recall@1/3, precision@1 per group) and the golden output in
439
+ `test/vet/zlati-gw.json`.
440
+ 0b. Speed separately: `bun run bench` (not part of `bun test`, not in CI) measures index build
441
+ and mass-pairing throughput against the stored baseline `test/vet/bench-osnova.json` and
442
+ fails only when latency grows > 20 % or `uspeh@1` drops > 2 pp. Refresh the baseline
443
+ deliberately with `bun run bench --zapisi-osnovo`.
418
444
  1. Large real-life datasets are in ./test-data (arikli, stranke, pacienti).
419
445
  2. tests are in ./test
420
446
  3. tests per implementation are in ./test/(IMPLEMENTATION)
package/README.md CHANGED
@@ -532,6 +532,19 @@ See [CLAUDE.md](./CLAUDE.md) for the complete technical specification including:
532
532
 
533
533
  See [LICENSE.md](./LICENSE.md) for license terms.
534
534
 
535
+ ### Measuring performance
536
+
537
+ Quality thresholds live in the test suite; performance is measured separately (it depends on
538
+ machine load, so it must not fail the quality tests):
539
+
540
+ ```bash
541
+ bun run bench # measure and compare against test/vet/bench-osnova.json
542
+ bun run bench --zapisi-osnovo # deliberately refresh the baseline
543
+ ```
544
+
545
+ The bench fails only when latency grows by more than 20 % or `uspeh@1` drops by more than
546
+ 2 percentage points.
547
+
535
548
  ## Domain extensions and the `cry-search/vet` entry point
536
549
 
537
550
  The core exposes a narrow seam for domain logic (all optional, no behaviour change when unused):
@@ -545,6 +558,19 @@ The core exposes a narrow seam for domain logic (all optional, no behaviour chan
545
558
  collection as queries; asynchronous iterator (chunked), top-K candidates per item with scores,
546
559
  optional `samoEnolicno` (greedy 1:1), optional domain `rank`/`accept`, and
547
560
  `alternativnePoizvedbe` (retry with a narrowed query).
561
+ - `matchManySync(prepared, queries, opts)` — the same in one shot, for callers whose API is not
562
+ async (no I/O happen in the hot path, so the sync variant is equivalent).
563
+
564
+ The veterinary module also ships a **bilingual SI↔EN lexicon** (`data/prevodi.ts`, normalizer
565
+ `prevodiNormalizator`): English catalogue names (`Monge dog rabbit rice&potatoes 2,5kg`) pair with
566
+ Slovenian Pantheon names (`MONGE DOG ADULT zajec, riž, krompir 2,5kg`). The same table is applied
567
+ to index values and queries (symmetry is required — one-sided translation makes results worse),
568
+ brand names are never translated, and marketing/connector words (`care`, `for`, `with` …) are
569
+ dropped. `razdeljevalciNormalizator` splits `&`/`+`/`/` inside names, and life stages
570
+ (`puppy`/`kitten`/`adult`/`senior`) become the soft class `starost`.
571
+
572
+ With `vrniNesprejete: true` the domain module keeps candidates that fail the acceptance rule and
573
+ marks them (`sprejeto: false`) instead of dropping them — useful in UIs where the user decides.
548
574
 
549
575
  A ready-made veterinary implementation ships as a subpath (optional peer dependency on
550
576
  `cry-vetzdravila`):
@@ -89,23 +89,21 @@ export interface PreparedCollection<T extends SearchableObject> {
89
89
  data: SearchableData<T>;
90
90
  metadata: NumericSearchMetadata;
91
91
  }
92
+ /**
93
+ * Masovno uparjanje v enem kosu (sinhrono, brez I/O). Enaki rezultati kot
94
+ * `matchMany`; uporabno tam, kjer klicoči API ni async (npr. predpomnilnik
95
+ * gatewaya) ali iteracija po kosih ni potrebna.
96
+ */
97
+ export declare function matchManySync<T extends SearchableObject, TQuery>(prepared: PreparedCollection<T>, queries: readonly TQuery[], options: MatchManyOptions<T, TQuery>): MatchManyRezultat<T>[];
92
98
  /**
93
99
  * Masovno uparjanje: indeks ene strani, elementi druge strani kot poizvedbe.
94
100
  *
95
101
  * Vrne async iterator, ki teče po kosih (`kos`, privzeto 50), tako da rezultat
96
- * velikega uparjanja (npr. 40k × 25k) ne gre v celoti v pomnilnik. Za vsak
97
- * element vrne največ `limit` kandidatov nad `minScore`, urejenih padajoče.
102
+ * velikega uparjanja (npr. 40k × 25k) ne gre v celoti v pomnilnik. Za vsak element
103
+ * vrne največ `limit` kandidatov, urejenih padajoče po oceni.
98
104
  *
99
- * Uporablja isto iskalno logiko kot `searchAndRank` (retrieval po `metadata.sorted`
100
- * z AND-ujemanjem + ocena po `metadata.unsorted`), zato so rezultati skladni z
105
+ * Uporablja isto iskalno logiko kot `searchAndRank`, zato so rezultati skladni z
101
106
  * navadnim iskanjem. Domenski modul lahko oceno in sprejem zamenja z `rank`/`accept`.
102
- *
103
- * @example
104
- * ```typescript
105
- * for await (const kos of matchMany(prepared, levi, { queryOf: (a) => a.naziv })) {
106
- * console.log(kos.id, kos.kandidati[0]?.item.naziv);
107
- * }
108
- * ```
109
107
  */
110
108
  export declare function matchMany<T extends SearchableObject, TQuery>(prepared: PreparedCollection<T>, queries: readonly TQuery[], options: MatchManyOptions<T, TQuery>): AsyncGenerator<MatchManyRezultat<T>[], void, void>;
111
109
  //# sourceMappingURL=matchMany.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"matchMany.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/matchMany.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,EAAE,EACF,eAAe,EACf,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAIrB;;GAEG;AACH,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,gBAAgB;IAC3D,EAAE,EAAE,EAAE,CAAC;IACP,IAAI,EAAE,CAAC,CAAC;IACR,wEAAwE;IACxE,KAAK,EAAE,MAAM,CAAC;IACd,mEAAmE;IACnE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;CACpC;AAED,0CAA0C;AAC1C,MAAM,WAAW,iBAAiB,CAAC,CAAC,SAAS,gBAAgB;IAC3D,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IACX,oDAAoD;IACpD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACxC,6DAA6D;IAC7D,SAAS,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,CAAC;CACnC;AAED,sCAAsC;AACtC,MAAM,WAAW,gBAAgB,CAAC,CAAC,SAAS,gBAAgB,EAAE,MAAM;IAClE;;;OAGG;IACH,OAAO,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,eAAe,CAAC;IACrD,wFAAwF;IACxF,IAAI,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC;IACjD,iDAAiD;IACjD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,uCAAuC;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kEAAkE;IAClE,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,uEAAuE;IACvE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC;IAChD,sEAAsE;IACtE,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,KAAK,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACzD,uDAAuD;IACvD,IAAI,CAAC,EAAE,CACL,QAAQ,EAAE;QAAE,EAAE,EAAE,EAAE,CAAC;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;KAAE,EACjE,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;KAAE,KAC5D,MAAM,CAAC;IACZ,qEAAqE;IACrE,MAAM,CAAC,EAAE,CACP,QAAQ,EAAE;QAAE,EAAE,EAAE,EAAE,CAAC;QAAC,IAAI,EAAE,CAAC,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;KAAE,EACjE,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;KAAE,KAC5D,OAAO,CAAC;IACb,8EAA8E;IAC9E,IAAI,CAAC,EAAE,UAAU,CAAC;IAClB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,SAAS,CAAC,MAAM,GAAG,eAAe,CAAC,EAAE,CAAC;IACjF;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;CACtC;AAED,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB,CAAC,CAAC,SAAS,gBAAgB;IAC5D,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC;IACxB,QAAQ,EAAE,qBAAqB,CAAC;CACjC;AAeD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAuB,SAAS,CAAC,CAAC,SAAS,gBAAgB,EAAE,MAAM,EACjE,QAAQ,EAAE,kBAAkB,CAAC,CAAC,CAAC,EAC/B,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,OAAO,EAAE,gBAAgB,CAAC,CAAC,EAAE,MAAM,CAAC,GACnC,cAAc,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CA4FpD"}
1
+ {"version":3,"file":"matchMany.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/matchMany.ts"],"names":[],"mappings":"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"}
package/dist/index.cjs CHANGED
@@ -70,6 +70,7 @@ __export(exports_src, {
70
70
  isDeletedOrBlocked: () => isDeletedOrBlocked,
71
71
  itemClasses: () => itemClasses,
72
72
  matchMany: () => matchMany,
73
+ matchManySync: () => matchManySync,
73
74
  matchPreparedTokenInSortedList: () => matchPreparedTokenInSortedList,
74
75
  matchTokenInSortedListNumeric: () => matchTokenInSortedListNumeric,
75
76
  normalizeDates: () => normalizeDates,
@@ -1761,76 +1762,87 @@ function naSezname(classes) {
1761
1762
  }
1762
1763
  return Object.keys(out).length ? out : undefined;
1763
1764
  }
1764
- async function* matchMany(prepared, queries, options) {
1765
- const { queryOf, idOf, limit = 3, minScore = 0, kos = 50, samoEnolicno, rank, accept, opts } = options;
1765
+ function matchOne(prepared, query, indeks, options, porabljeni) {
1766
+ const { queryOf, idOf, limit = 3, minScore = 0, rank, accept, opts } = options;
1766
1767
  const retrievalLimit = options.retrievalLimit ?? Math.max(20, limit * 4);
1767
1768
  const primaryTextOf = options.primaryTextOf ?? ((item) => item["naziv"]);
1768
- const porabljeni = samoEnolicno ? new Set : undefined;
1769
- let kosZbirka = [];
1770
- for (let indeks = 0;indeks < queries.length; indeks++) {
1771
- const query = queries[indeks];
1772
- const vhod = queryOf(query);
1773
- const text = typeof vhod === "string" ? vhod : vhod.text;
1774
- const queryClasses = typeof vhod === "string" ? undefined : naSezname(vhod.classes);
1775
- const id = idOf ? idOf(query, indeks) : String(query["_id"] ?? query["id"] ?? indeks);
1776
- const poisciKandidate = (besedilo, razredi) => {
1777
- if (!besedilo.trim())
1778
- return { kandidati: [], zadetkov: 0 };
1779
- const zadetki = searchAndRankNumeric(besedilo, prepared.data, prepared.metadata, {
1780
- primaryTextFn: primaryTextOf,
1781
- ...options.exactKeysOf ? { exactKeysFn: options.exactKeysOf } : {},
1782
- ...opts ? { opts } : {},
1783
- limit: retrievalLimit
1784
- });
1785
- const poizvedbaZaDomeno = { text: besedilo, ...razredi ? { classes: razredi } : {} };
1786
- const najdeni = [];
1787
- for (const zadetek of zadetki) {
1788
- const classes = itemClasses(prepared.metadata, zadetek.id);
1789
- const kandidat = {
1790
- id: zadetek.id,
1791
- item: zadetek.item,
1792
- ...classes ? { classes } : {}
1793
- };
1794
- if (accept && !accept(kandidat, poizvedbaZaDomeno))
1795
- continue;
1796
- const ocena = rank ? rank(kandidat, poizvedbaZaDomeno) : zadetek.score;
1797
- if (ocena < minScore)
1798
- continue;
1799
- if (porabljeni?.has(zadetek.id))
1800
- continue;
1801
- najdeni.push({ id: zadetek.id, item: zadetek.item, score: ocena, ...classes ? { classes } : {} });
1802
- }
1803
- najdeni.sort((a, b) => {
1804
- if (b.score !== a.score)
1805
- return b.score - a.score;
1806
- return (primaryTextOf(a.item) ?? "").localeCompare(primaryTextOf(b.item) ?? "");
1807
- });
1808
- if (najdeni.length > limit)
1809
- najdeni.length = limit;
1810
- return { kandidati: najdeni, zadetkov: zadetki.length };
1811
- };
1812
- let { kandidati, zadetkov } = poisciKandidate(text, queryClasses);
1813
- const poskusiAlternative = kandidati.length === 0 && (options.alternativneLeCeNiZadetkov ? zadetkov === 0 : true);
1814
- if (poskusiAlternative && options.alternativnePoizvedbe) {
1815
- for (const alternativa of options.alternativnePoizvedbe(query)) {
1816
- const altBesedilo = typeof alternativa === "string" ? alternativa : alternativa.text;
1817
- if (!altBesedilo?.trim() || altBesedilo === text)
1818
- continue;
1819
- const altRazredi = typeof alternativa === "string" ? undefined : naSezname(alternativa.classes);
1820
- ({ kandidati } = poisciKandidate(altBesedilo, altRazredi));
1821
- if (kandidati.length)
1822
- break;
1823
- }
1824
- }
1825
- if (porabljeni) {
1826
- for (const kandidat of kandidati)
1827
- porabljeni.add(kandidat.id);
1769
+ const vhod = queryOf(query);
1770
+ const text = typeof vhod === "string" ? vhod : vhod.text;
1771
+ const queryClasses = typeof vhod === "string" ? undefined : naSezname(vhod.classes);
1772
+ const id = idOf ? idOf(query, indeks) : String(query["_id"] ?? query["id"] ?? indeks);
1773
+ const poisciKandidate = (besedilo, razredi) => {
1774
+ if (!besedilo.trim())
1775
+ return { kandidati: [], zadetkov: 0 };
1776
+ const zadetki = searchAndRankNumeric(besedilo, prepared.data, prepared.metadata, {
1777
+ primaryTextFn: primaryTextOf,
1778
+ ...options.exactKeysOf ? { exactKeysFn: options.exactKeysOf } : {},
1779
+ ...opts ? { opts } : {},
1780
+ limit: retrievalLimit
1781
+ });
1782
+ const poizvedbaZaDomeno = { text: besedilo, ...razredi ? { classes: razredi } : {} };
1783
+ const najdeni = [];
1784
+ for (const zadetek of zadetki) {
1785
+ const classes = itemClasses(prepared.metadata, zadetek.id);
1786
+ const kandidat = {
1787
+ id: zadetek.id,
1788
+ item: zadetek.item,
1789
+ ...classes ? { classes } : {}
1790
+ };
1791
+ if (accept && !accept(kandidat, poizvedbaZaDomeno))
1792
+ continue;
1793
+ const ocena = rank ? rank(kandidat, poizvedbaZaDomeno) : zadetek.score;
1794
+ if (ocena < minScore)
1795
+ continue;
1796
+ if (porabljeni?.has(zadetek.id))
1797
+ continue;
1798
+ najdeni.push({ id: zadetek.id, item: zadetek.item, score: ocena, ...classes ? { classes } : {} });
1828
1799
  }
1829
- kosZbirka.push({
1830
- id,
1831
- ...queryClasses ? { queryClasses } : {},
1832
- kandidati
1800
+ najdeni.sort((a, b) => {
1801
+ if (b.score !== a.score)
1802
+ return b.score - a.score;
1803
+ return (primaryTextOf(a.item) ?? "").localeCompare(primaryTextOf(b.item) ?? "");
1833
1804
  });
1805
+ if (najdeni.length > limit)
1806
+ najdeni.length = limit;
1807
+ return { kandidati: najdeni, zadetkov: zadetki.length };
1808
+ };
1809
+ let { kandidati, zadetkov } = poisciKandidate(text, queryClasses);
1810
+ const poskusiAlternative = kandidati.length === 0 && (options.alternativneLeCeNiZadetkov ? zadetkov === 0 : true);
1811
+ if (poskusiAlternative && options.alternativnePoizvedbe) {
1812
+ for (const alternativa of options.alternativnePoizvedbe(query)) {
1813
+ const altBesedilo = typeof alternativa === "string" ? alternativa : alternativa.text;
1814
+ if (!altBesedilo?.trim() || altBesedilo === text)
1815
+ continue;
1816
+ const altRazredi = typeof alternativa === "string" ? undefined : naSezname(alternativa.classes);
1817
+ ({ kandidati } = poisciKandidate(altBesedilo, altRazredi));
1818
+ if (kandidati.length)
1819
+ break;
1820
+ }
1821
+ }
1822
+ if (porabljeni) {
1823
+ for (const kandidat of kandidati)
1824
+ porabljeni.add(kandidat.id);
1825
+ }
1826
+ return {
1827
+ id,
1828
+ ...queryClasses ? { queryClasses } : {},
1829
+ kandidati
1830
+ };
1831
+ }
1832
+ function matchManySync(prepared, queries, options) {
1833
+ const porabljeni = options.samoEnolicno ? new Set : undefined;
1834
+ const izhod = new Array(queries.length);
1835
+ for (let indeks = 0;indeks < queries.length; indeks++) {
1836
+ izhod[indeks] = matchOne(prepared, queries[indeks], indeks, options, porabljeni);
1837
+ }
1838
+ return izhod;
1839
+ }
1840
+ async function* matchMany(prepared, queries, options) {
1841
+ const kos = options.kos ?? 50;
1842
+ const porabljeni = options.samoEnolicno ? new Set : undefined;
1843
+ let kosZbirka = [];
1844
+ for (let indeks = 0;indeks < queries.length; indeks++) {
1845
+ kosZbirka.push(matchOne(prepared, queries[indeks], indeks, options, porabljeni));
1834
1846
  if (kosZbirka.length >= kos) {
1835
1847
  yield kosZbirka;
1836
1848
  kosZbirka = [];
package/dist/index.d.cts CHANGED
@@ -24,7 +24,7 @@ export { findInLinkedArraysNumeric, findInLinkedArraysSimpleNumeric, } from './c
24
24
  export { AugmentedMetadata } from './common/numeric/AugmentedMetadata';
25
25
  export { syncSearchArrayMetadataNumeric as syncSearchArrayMetadata, syncSearchArrayMetadataNumericWithPrevious as syncSearchArrayMetadataWithPrevious, } from './common/numeric/syncSearchArrayMetadata';
26
26
  export { normalizeQueryWithOpts } from './common/numeric/prepareQueryVariants';
27
- export { matchMany, type MatchManyKandidat, type MatchManyOptions, type MatchManyRezultat, type PreparedCollection, } from './common/numeric/matchMany';
27
+ export { matchMany, matchManySync, type MatchManyKandidat, type MatchManyOptions, type MatchManyRezultat, type PreparedCollection, } from './common/numeric/matchMany';
28
28
  export { upsertPrimaryItemNumeric as upsertPrimaryItem, removePrimaryItemNumeric as removePrimaryItem, upsertLinkedItemNumeric as upsertLinkedItem, removeLinkedItemNumeric as removeLinkedItem, syncLinkedItemsForPrimaryNumeric as syncLinkedItemsForPrimary, } from './common/numeric/updateSearchLinkedMetadata';
29
29
  export { matchTokenInSortedListNumeric, allTokensMatchNumeric, binarySearchUint32, prepareQueryTokens, matchPreparedTokenInSortedList, allPreparedTokensMatch, type PreparedQueryToken, } from './utils/matchTokenNumeric';
30
30
  export { searchAndRankNumeric as searchAndRank, searchAndRankNumeric, scoreOne, scoreOrderAndPresence, scoreBag, orderedTokenize, type ScoredResult, type SearchAndRankOptions, type OrderAndPresenceWeights, } from './common/numeric/searchAndRank';
package/dist/index.d.ts CHANGED
@@ -24,7 +24,7 @@ export { findInLinkedArraysNumeric, findInLinkedArraysSimpleNumeric, } from './c
24
24
  export { AugmentedMetadata } from './common/numeric/AugmentedMetadata';
25
25
  export { syncSearchArrayMetadataNumeric as syncSearchArrayMetadata, syncSearchArrayMetadataNumericWithPrevious as syncSearchArrayMetadataWithPrevious, } from './common/numeric/syncSearchArrayMetadata';
26
26
  export { normalizeQueryWithOpts } from './common/numeric/prepareQueryVariants';
27
- export { matchMany, type MatchManyKandidat, type MatchManyOptions, type MatchManyRezultat, type PreparedCollection, } from './common/numeric/matchMany';
27
+ export { matchMany, matchManySync, type MatchManyKandidat, type MatchManyOptions, type MatchManyRezultat, type PreparedCollection, } from './common/numeric/matchMany';
28
28
  export { upsertPrimaryItemNumeric as upsertPrimaryItem, removePrimaryItemNumeric as removePrimaryItem, upsertLinkedItemNumeric as upsertLinkedItem, removeLinkedItemNumeric as removeLinkedItem, syncLinkedItemsForPrimaryNumeric as syncLinkedItemsForPrimary, } from './common/numeric/updateSearchLinkedMetadata';
29
29
  export { matchTokenInSortedListNumeric, allTokensMatchNumeric, binarySearchUint32, prepareQueryTokens, matchPreparedTokenInSortedList, allPreparedTokensMatch, type PreparedQueryToken, } from './utils/matchTokenNumeric';
30
30
  export { searchAndRankNumeric as searchAndRank, searchAndRankNumeric, scoreOne, scoreOrderAndPresence, scoreBag, orderedTokenize, type ScoredResult, type SearchAndRankOptions, type OrderAndPresenceWeights, } from './common/numeric/searchAndRank';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -1650,76 +1650,87 @@ function naSezname(classes) {
1650
1650
  }
1651
1651
  return Object.keys(out).length ? out : undefined;
1652
1652
  }
1653
- async function* matchMany(prepared, queries, options) {
1654
- const { queryOf, idOf, limit = 3, minScore = 0, kos = 50, samoEnolicno, rank, accept, opts } = options;
1653
+ function matchOne(prepared, query, indeks, options, porabljeni) {
1654
+ const { queryOf, idOf, limit = 3, minScore = 0, rank, accept, opts } = options;
1655
1655
  const retrievalLimit = options.retrievalLimit ?? Math.max(20, limit * 4);
1656
1656
  const primaryTextOf = options.primaryTextOf ?? ((item) => item["naziv"]);
1657
- const porabljeni = samoEnolicno ? new Set : undefined;
1658
- let kosZbirka = [];
1659
- for (let indeks = 0;indeks < queries.length; indeks++) {
1660
- const query = queries[indeks];
1661
- const vhod = queryOf(query);
1662
- const text = typeof vhod === "string" ? vhod : vhod.text;
1663
- const queryClasses = typeof vhod === "string" ? undefined : naSezname(vhod.classes);
1664
- const id = idOf ? idOf(query, indeks) : String(query["_id"] ?? query["id"] ?? indeks);
1665
- const poisciKandidate = (besedilo, razredi) => {
1666
- if (!besedilo.trim())
1667
- return { kandidati: [], zadetkov: 0 };
1668
- const zadetki = searchAndRankNumeric(besedilo, prepared.data, prepared.metadata, {
1669
- primaryTextFn: primaryTextOf,
1670
- ...options.exactKeysOf ? { exactKeysFn: options.exactKeysOf } : {},
1671
- ...opts ? { opts } : {},
1672
- limit: retrievalLimit
1673
- });
1674
- const poizvedbaZaDomeno = { text: besedilo, ...razredi ? { classes: razredi } : {} };
1675
- const najdeni = [];
1676
- for (const zadetek of zadetki) {
1677
- const classes = itemClasses(prepared.metadata, zadetek.id);
1678
- const kandidat = {
1679
- id: zadetek.id,
1680
- item: zadetek.item,
1681
- ...classes ? { classes } : {}
1682
- };
1683
- if (accept && !accept(kandidat, poizvedbaZaDomeno))
1684
- continue;
1685
- const ocena = rank ? rank(kandidat, poizvedbaZaDomeno) : zadetek.score;
1686
- if (ocena < minScore)
1687
- continue;
1688
- if (porabljeni?.has(zadetek.id))
1689
- continue;
1690
- najdeni.push({ id: zadetek.id, item: zadetek.item, score: ocena, ...classes ? { classes } : {} });
1691
- }
1692
- najdeni.sort((a, b) => {
1693
- if (b.score !== a.score)
1694
- return b.score - a.score;
1695
- return (primaryTextOf(a.item) ?? "").localeCompare(primaryTextOf(b.item) ?? "");
1696
- });
1697
- if (najdeni.length > limit)
1698
- najdeni.length = limit;
1699
- return { kandidati: najdeni, zadetkov: zadetki.length };
1700
- };
1701
- let { kandidati, zadetkov } = poisciKandidate(text, queryClasses);
1702
- const poskusiAlternative = kandidati.length === 0 && (options.alternativneLeCeNiZadetkov ? zadetkov === 0 : true);
1703
- if (poskusiAlternative && options.alternativnePoizvedbe) {
1704
- for (const alternativa of options.alternativnePoizvedbe(query)) {
1705
- const altBesedilo = typeof alternativa === "string" ? alternativa : alternativa.text;
1706
- if (!altBesedilo?.trim() || altBesedilo === text)
1707
- continue;
1708
- const altRazredi = typeof alternativa === "string" ? undefined : naSezname(alternativa.classes);
1709
- ({ kandidati } = poisciKandidate(altBesedilo, altRazredi));
1710
- if (kandidati.length)
1711
- break;
1712
- }
1713
- }
1714
- if (porabljeni) {
1715
- for (const kandidat of kandidati)
1716
- porabljeni.add(kandidat.id);
1657
+ const vhod = queryOf(query);
1658
+ const text = typeof vhod === "string" ? vhod : vhod.text;
1659
+ const queryClasses = typeof vhod === "string" ? undefined : naSezname(vhod.classes);
1660
+ const id = idOf ? idOf(query, indeks) : String(query["_id"] ?? query["id"] ?? indeks);
1661
+ const poisciKandidate = (besedilo, razredi) => {
1662
+ if (!besedilo.trim())
1663
+ return { kandidati: [], zadetkov: 0 };
1664
+ const zadetki = searchAndRankNumeric(besedilo, prepared.data, prepared.metadata, {
1665
+ primaryTextFn: primaryTextOf,
1666
+ ...options.exactKeysOf ? { exactKeysFn: options.exactKeysOf } : {},
1667
+ ...opts ? { opts } : {},
1668
+ limit: retrievalLimit
1669
+ });
1670
+ const poizvedbaZaDomeno = { text: besedilo, ...razredi ? { classes: razredi } : {} };
1671
+ const najdeni = [];
1672
+ for (const zadetek of zadetki) {
1673
+ const classes = itemClasses(prepared.metadata, zadetek.id);
1674
+ const kandidat = {
1675
+ id: zadetek.id,
1676
+ item: zadetek.item,
1677
+ ...classes ? { classes } : {}
1678
+ };
1679
+ if (accept && !accept(kandidat, poizvedbaZaDomeno))
1680
+ continue;
1681
+ const ocena = rank ? rank(kandidat, poizvedbaZaDomeno) : zadetek.score;
1682
+ if (ocena < minScore)
1683
+ continue;
1684
+ if (porabljeni?.has(zadetek.id))
1685
+ continue;
1686
+ najdeni.push({ id: zadetek.id, item: zadetek.item, score: ocena, ...classes ? { classes } : {} });
1717
1687
  }
1718
- kosZbirka.push({
1719
- id,
1720
- ...queryClasses ? { queryClasses } : {},
1721
- kandidati
1688
+ najdeni.sort((a, b) => {
1689
+ if (b.score !== a.score)
1690
+ return b.score - a.score;
1691
+ return (primaryTextOf(a.item) ?? "").localeCompare(primaryTextOf(b.item) ?? "");
1722
1692
  });
1693
+ if (najdeni.length > limit)
1694
+ najdeni.length = limit;
1695
+ return { kandidati: najdeni, zadetkov: zadetki.length };
1696
+ };
1697
+ let { kandidati, zadetkov } = poisciKandidate(text, queryClasses);
1698
+ const poskusiAlternative = kandidati.length === 0 && (options.alternativneLeCeNiZadetkov ? zadetkov === 0 : true);
1699
+ if (poskusiAlternative && options.alternativnePoizvedbe) {
1700
+ for (const alternativa of options.alternativnePoizvedbe(query)) {
1701
+ const altBesedilo = typeof alternativa === "string" ? alternativa : alternativa.text;
1702
+ if (!altBesedilo?.trim() || altBesedilo === text)
1703
+ continue;
1704
+ const altRazredi = typeof alternativa === "string" ? undefined : naSezname(alternativa.classes);
1705
+ ({ kandidati } = poisciKandidate(altBesedilo, altRazredi));
1706
+ if (kandidati.length)
1707
+ break;
1708
+ }
1709
+ }
1710
+ if (porabljeni) {
1711
+ for (const kandidat of kandidati)
1712
+ porabljeni.add(kandidat.id);
1713
+ }
1714
+ return {
1715
+ id,
1716
+ ...queryClasses ? { queryClasses } : {},
1717
+ kandidati
1718
+ };
1719
+ }
1720
+ function matchManySync(prepared, queries, options) {
1721
+ const porabljeni = options.samoEnolicno ? new Set : undefined;
1722
+ const izhod = new Array(queries.length);
1723
+ for (let indeks = 0;indeks < queries.length; indeks++) {
1724
+ izhod[indeks] = matchOne(prepared, queries[indeks], indeks, options, porabljeni);
1725
+ }
1726
+ return izhod;
1727
+ }
1728
+ async function* matchMany(prepared, queries, options) {
1729
+ const kos = options.kos ?? 50;
1730
+ const porabljeni = options.samoEnolicno ? new Set : undefined;
1731
+ let kosZbirka = [];
1732
+ for (let indeks = 0;indeks < queries.length; indeks++) {
1733
+ kosZbirka.push(matchOne(prepared, queries[indeks], indeks, options, porabljeni));
1723
1734
  if (kosZbirka.length >= kos) {
1724
1735
  yield kosZbirka;
1725
1736
  kosZbirka = [];
@@ -2352,6 +2363,7 @@ export {
2352
2363
  isDeletedOrBlocked,
2353
2364
  itemClasses,
2354
2365
  matchMany,
2366
+ matchManySync,
2355
2367
  matchPreparedTokenInSortedList,
2356
2368
  matchTokenInSortedListNumeric,
2357
2369
  normalizeDates,
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Dvojezični leksikon SI↔EN za veterinarske nazive artikelov (živila + zdravila).
3
+ *
4
+ * Zakaj: isti artikel ima v klik.vetu pogosto **angleški** naziv (vnesen po
5
+ * dobaviteljskem katalogu), v Pantheonu pa **slovenskega** (ali obratno — pri
6
+ * znamkah N&D/Hills je pantheonov naziv pogosto v angleščini, klik.vetov pa
7
+ * slovenski). Brez preslikave iskalnik takih parov NE NAJDE, ker se ključne besede
8
+ * (`rabbit`/`zajec`, `rice`/`riž`, `codfish`/`polenovka`) ne ujemajo:
9
+ *
10
+ * „Monge dog rabbit rice&potatoes 2,5kg“ ↔ „MONGE DOG ADULT zajec, riž, krompir 2,5kg“
11
+ * → brez leksikona 0 kandidatov, z leksikonom pravi par prvi (`sprejeto: true`).
12
+ *
13
+ * Pravila (izmerjeno na podatkih tackakp + SI_TACKA, 2026-10-04):
14
+ * - preslikava je **simetrična**: ista tabela se uporabi na INDEKSU in na
15
+ * POIZVEDBI (normalizator + ocenjevalec), zato sta obe strani vedno v kanonični
16
+ * obliki — tudi kadar sta obe angleški ali obe slovenski;
17
+ * - ključi so normalizirani (male črke, brez šumnikov, brez ločil), kanonične
18
+ * vrednosti so prav tako brez šumnikov (jedro jih itak normalizira);
19
+ * - **znamk in učinkovin ne prevajamo** (Monge, Royal Canin, N&D, Hills, Brit,
20
+ * Flamingo, Trixie, Kong, Kerbl, Rogz, Ferplast, Flexi, JBL, Rinti, Dubex, Sera,
21
+ * Oasy, Vetlife, Dingo, Matis, Besta, Ferribella, MEDROL …) — zanje skrbi
22
+ * `znamkaNormalizator`; v tem seznamu jih namenoma NI;
23
+ * - besed, ki so v obeh jezikih enake (`light`, `mini`, `maxi`, `gel`, `spot`,
24
+ * `tuna`, `premium`, `hairball` …), NI na seznamu — preslikava bi bila odveč;
25
+ * - okrajšava `MED` (= „medium“) je namenoma izven te tabele: `med` je v slovenščini
26
+ * tudi „honey“ („z medom“ je v tackakp 497 artikelov), zato jo rešuje ciljno
27
+ * pravilo v `razdeljevalciNormalizator` (`MED&MAXI` → `MEDIUM MAXI`).
28
+ *
29
+ * Vzdrževanje: kandidate za dodajanje izlušči diagnostični skript
30
+ * `panthenImport/src/debug/besedisceZaLeksikon.ts` (`--kandidati`).
31
+ */
32
+ /**
33
+ * Angleške (in okrajšane) besede → kanonična slovenska oblika (brez šumnikov).
34
+ *
35
+ * Vrednost je lahko tudi večbesedna (`granatno jabolko`), tako se „POMEG“ in
36
+ * „granatno jabolko“ ujameta kot ista dva žetona.
37
+ */
38
+ export declare const PREVODI: Record<string, string>;
39
+ /**
40
+ * Vezniki in marketinške besede, ki ne nosijo pomena izdelka — iz besedila se
41
+ * ODSTRANIJO (brez razreda). Merjeno: par „urinary care“ ↔ „urinarne“ je imel
42
+ * zaradi `care` pokritost kandidata 50 % namesto 100 %.
43
+ */
44
+ export declare const STOP_BESEDE: string[];
45
+ /**
46
+ * Življenjska stopnja → kanonična oblika, ki postane RAZRED `starost`.
47
+ *
48
+ * Stopnja je močan razločevalnik (mladič proti odraslemu je drug izdelek), a jo
49
+ * nazivi navajajo nedosledno, zato razred (kazen ob nasprotju), ne trdi žeton.
50
+ * Zajema tudi angleške zapise in okrajšave, ker so v podatkih pogosti.
51
+ */
52
+ export declare const STAROSTI: Record<string, string>;
53
+ /**
54
+ * Angleške velikosti → kanonična velikost (isti ključi kot `VELIKOSTI_SINONIMI`).
55
+ *
56
+ * `mini` in `maxi` namenoma OSTAJAJO besede: uporablja jih obe strani enako, v
57
+ * linijah RC/N&D pa pomenita razred velikosti. V razred gredo le nedvoumne.
58
+ */
59
+ export declare const VELIKOSTI_EN: Record<string, string>;
60
+ /** Okrajšave velikosti, ki se v Pantheonu pojavljajo s `&` (`MED&MAXI`) — ciljno pravilo. */
61
+ export declare const VELIKOSTI_OKRAJSAVE: Record<string, string>;
62
+ /** Izvoz za teste in diagnostične skripte. */
63
+ export declare const PREVODI_VIR = "ro\u010Dno kurirano 2026-10-04 (besedi\u0161\u010De izmerjeno v tackakp + SI_TACKA)";
64
+ //# sourceMappingURL=prevodi.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prevodi.d.ts","sourceRoot":"","sources":["../../../src/vet/data/prevodi.ts"],"names":[],"mappings":"AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CA0T1C,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,WAAW,EAAE,MAAM,EAiB/B,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAqB3C,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAO/C,CAAC;AAEF,6FAA6F;AAC7F,eAAO,MAAM,mBAAmB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAItD,CAAC;AAEF,8CAA8C;AAC9C,eAAO,MAAM,WAAW,wFAAyE,CAAC"}