cry-search 1.0.0 → 1.0.1

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
@@ -6,6 +6,17 @@ cry-search is a library for fast searching in large datasets.
6
6
  1. Bun bundler for development, testing, and bundling
7
7
  2. ESM jscript in dist for distribution
8
8
 
9
+ ## Documentation
10
+
11
+ Documentation is spread across multiple files that must be kept in sync:
12
+
13
+ 1. [CLAUDE.md](./CLAUDE.md) - Technical specification, types, architecture details
14
+ 2. [README.md](./README.md) - User-facing documentation, examples, quick start
15
+ 3. [UNIVERSE.md](./UNIVERSE.md) - SearchUniverse multi-collection manager documentation
16
+ 4. **JSDoc comments** - Inline documentation in source files (`src/**/*.ts`)
17
+
18
+ **Important:** When updating documentation (types, behavior, API changes), update ALL relevant files together to maintain consistency.
19
+
9
20
  ## Architecture
10
21
 
11
22
  The library has two implementations:
@@ -60,15 +71,20 @@ The numeric implementation uses a global token registry (`src/utils/tokenRegistr
60
71
  3. sanitation
61
72
  1. case-insensitive
62
73
  2. diacritics-insensitive (č,ć,c -> same c)
63
- 3. we only keep letters (a-z), digits (0-9), periods (.), minuses (-), spaces (" ") in search
74
+ tsc 3. we only keep letters (a-z), digits (0-9), periods (.), minuses (-), tildes (~), query prefixes (<>+*!=?), spaces (" ") in search
64
75
  4. other characters are replaced with space (" ")
65
76
  2. tokenization
66
77
  1. we split strings on spaces
67
78
  2. then we trim all substrings
68
79
  3. matching
69
80
  1. we match words in any order
70
- 2. we match numbers from start or from end
71
- 1. eg. 123 matches 12345 and 45123 but not 7812345
81
+ 2. number matching depends on TARGET token length (not query length):
82
+ 1. short numbers (≤6 digits in target): whole match only
83
+ - eg. query "123" vs target "123" → match
84
+ - eg. query "12" vs target "123" → no match (target is short, needs exact)
85
+ 2. long numbers (>6 digits in target): startEnd match
86
+ - eg. query "60731" vs target "3838989760731" → match (13-digit barcode)
87
+ - eg. query "989" vs target "3838989760731" → no match (not at start/end)
72
88
  4. nested data extraction
73
89
  1. nested objects and arrays are automatically extracted recursively
74
90
  2. Date objects are converted to ISO strings before tokenization
@@ -135,7 +151,16 @@ The numeric implementation uses a global token registry (`src/utils/tokenRegistr
135
151
  10. MatchMode: 'start' | 'end' | 'startEnd' | 'anywhere' | 'whole'
136
152
  1. Field token prefixes: '<' (start), '>' (end), '+' (startEnd), '*' (anywhere), '!' (whole)
137
153
  2. Numeric: encoded in high 3 bits of Uint32 token ID
138
- 11. SearchableLinkedObjectsSpec: primary SearchableObjectSpec, linked SearchableObjectSpec, foreignKeyGetter
154
+ 11. SearchOpts:
155
+ 1. matchWords?: MatchMode (default: 'start') - how to match words
156
+ 2. matchNumbers?: MatchMode (default: 'startEnd') - how to match long numbers (>threshold digits)
157
+ 3. matchNumbersShort?: MatchMode (default: 'whole') - how to match short numbers (≤threshold digits)
158
+ 4. matchNumbersThreshold?: number (default: 6) - digit count threshold for short vs long
159
+ 12. LinkedSearchResultWithMeta<TPrimary, TLinked>:
160
+ 1. queryTokens: string[] - tokenized query
161
+ 2. results: LinkedSearchResult[] - matching results
162
+ 3. hasNegation: boolean - whether query contains negated tokens
163
+ 13. SearchableLinkedObjectsSpec: primary SearchableObjectSpec, linked SearchableObjectSpec, foreignKeyGetter
139
164
 
140
165
  ## Search Query Prefixes
141
166
 
@@ -148,8 +173,11 @@ Override match behavior per token at search time. Priority: query prefix > field
148
173
  | `..word` | end | Match tokens ending with word | `..Pro` matches "MacBookPro" |
149
174
  | `=word` | whole | Exact match only | `=jan` matches "jan" not "jana" |
150
175
  | `?word` | anywhere | Match word anywhere in token | `?book` matches "MacBook" |
151
- | `--word` | negation | Exclude results containing word | `apple --iphone` excludes iPhones |
152
- | `~word` | negation | Same as `--word` | `apple ~iphone` |
176
+ | `-word` | negation | Exclude results containing word (letter after -) | `apple -iphone` excludes iPhones |
177
+ | `--word` | negation | Exclude (works for words and numbers) | `--123` excludes items with "123" |
178
+ | `~word` | negation | Exclude (works for words and numbers) | `~iphone` or `~123` |
179
+
180
+ **Note:** `-5` is NOT negation (it's a negative number). Use `--5` or `~5` to negate numbers.
153
181
 
154
182
  ### Programmatic prefixes
155
183
  | Prefix | Mode |
@@ -161,11 +189,14 @@ Override match behavior per token at search time. Priority: query prefix > field
161
189
  | `!word` | whole |
162
190
 
163
191
  ### Examples
164
- 1. `"jana useni"` → finds "jana usenik" (default startEnd matching)
192
+ 1. `"jana useni"` → finds "jana usenik" (default start matching for words)
165
193
  2. `"jana =useni"` → no match (whole match required, "useni" ≠ "usenik")
166
- 3. `"jana --macka"` → finds "jana" but excludes any result containing "macka"
167
- 4. `"APL.."` → finds all tokens starting with "APL" (APL-IP15P, APL-MBP16)
168
- 5. `"..nik"` → finds tokens ending with "nik" (usenik, krajnik)
194
+ 3. `"jana -macka"` → finds "jana" but excludes any result containing "macka"
195
+ 4. `"krajnik --cat"` → finds "krajnik" but excludes linked items with "cat" (mačke)
196
+ 5. `"APL.."` → finds all tokens starting with "APL" (APL-IP15P, APL-MBP16)
197
+ 6. `"..nik"` → finds tokens ending with "nik" (usenik, krajnik)
198
+ 7. `"60731"` → finds barcode "3838989760731" (13-digit target uses startEnd)
199
+ 8. `"=9001"` → finds exact "9001" but NOT "035585249001" or "9001917"
169
200
 
170
201
 
171
202
  ## Main Functions (Default = Numeric)
package/README.md CHANGED
@@ -8,7 +8,7 @@ A fast, memory-efficient search library for large datasets with support for toke
8
8
  - **Memory efficient** - Numeric token storage uses ~45% less memory than string-based approaches
9
9
  - **Updatable** - Add, update, or remove items without rebuilding metadata
10
10
  - **Flexible matching** - Per-field match modes and query prefixes for prefix, suffix, anywhere, exact, and negation
11
- - **Search prefixed** - use `--word` to exclude it, `...word` for suffix, and more
11
+ - **Search prefixes** - use `-word`, `--word`, or `~word` to exclude, `..word` for suffix, `=word` for exact match
12
12
  - **Automatic normalization** - Handles diacritics, case, dates, and number formatting
13
13
  - **[Universal search](./UNIVERSE.md)** - Search and update across linked collections (e.g., customers with their pets)
14
14
 
@@ -237,7 +237,13 @@ Override match behavior per token at search time using prefixes:
237
237
  | `..word` | end | Match tokens ending with "word" |
238
238
  | `=word` | whole | Exact match only |
239
239
  | `?word` | anywhere | Match "word" anywhere in token |
240
- | `--word` or `~word` | negation | Exclude results containing "word" |
240
+ | `-word` | negation | Exclude results (only when followed by letter) |
241
+ | `--word` | negation | Exclude results (works for words and numbers) |
242
+ | `~word` | negation | Exclude results (works for words and numbers) |
243
+
244
+ **Note:** `-5` is NOT negation (it's a negative number). Use `--5` or `~5` to negate numbers.
245
+
246
+ **Detecting negation:** Linked search results include a `hasNegation: boolean` flag indicating if the query contained negation tokens.
241
247
 
242
248
  Alternative prefixes (for programmatic use): `<word` (start), `>word` (end), `+word` (startEnd), `*word` (anywhere), `!word` (whole)
243
249
 
@@ -247,12 +253,28 @@ searchInDataReturnObjects('jana =usenik', metadata, data);
247
253
  // Matches "Jana Usenik" but not "Jana Useniker"
248
254
 
249
255
  // Find "apple" but exclude results containing "iphone"
250
- searchInDataReturnObjects('apple --iphone', metadata, data);
256
+ searchInDataReturnObjects('apple -iphone', metadata, data);
251
257
  // Matches MacBook Pro but not iPhone
252
258
 
253
259
  // Find products with SKU starting with "APL"
254
260
  searchInDataReturnObjects('APL..', metadata, data);
255
261
  // Matches APL-IP15P, APL-MBP16, etc.
262
+
263
+ // Find exact barcode code
264
+ searchInDataReturnObjects('=9001', metadata, data);
265
+ // Matches "9001" but NOT "035585249001" or "9001917"
266
+ ```
267
+
268
+ ### Number Matching
269
+
270
+ Match mode for numbers is determined by the **target token's length** (not the query):
271
+ - **Short numbers (≤6 digits)**: require exact (whole) match
272
+ - **Long numbers (>6 digits)**: allow start or end match (for barcodes)
273
+
274
+ ```typescript
275
+ // Query "60731" against barcode "3838989760731" (13 digits) → MATCH (ends with 60731)
276
+ // Query "123" against code "123" (3 digits) → MATCH (exact)
277
+ // Query "12" against code "123" (3 digits) → NO MATCH (short number needs exact)
256
278
  ```
257
279
 
258
280
  Priority: query prefix > field match mode > global SearchOpts default.
@@ -1 +1 @@
1
- {"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAEhB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAcvG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAiC;IAEpD;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM,GAClC,IAAI;IAmBP;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAsC1E;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,SAAS;IAKlF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,EAAE;IAKjE;;;;;OAKG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAK1D;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAK9E;;;;;;;OAOG;IACH,YAAY,CAAC,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE;IAQ/D;;;;;;;OAOG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EACvC,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,EAAE,GACZ,CAAC,EAAE;IAMN;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,GACN,YAAY;IAoFf;;;;;;OAMG;IACH,yBAAyB,CAAC,CAAC,SAAS,gBAAgB,EAClD,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EAAE,GACT,iBAAiB;IA0BpB;;;;;;OAMG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO;IAmCjE;;;;;;OAMG;IACH,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,MAAM;IAcxE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IA8FlJ;;;;;;;;OAQG;IACH,KAAK,IAAI,IAAI;CAUd"}
1
+ {"version":3,"file":"SearchUniverse.d.ts","sourceRoot":"","sources":["../../src/common/SearchUniverse.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAEhB,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,iBAAiB,EAClB,MAAM,UAAU,CAAC;AAWlB,YAAY,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAC;AAcvG;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAiC;IAEpD;;;;;OAKG;IACH,kBAAkB,CAAC,CAAC,SAAS,gBAAgB,EAAE,CAAC,GAAG,OAAO,EACxD,IAAI,EAAE,MAAM,EACZ,MAAM,GAAE,gBAAgB,CAAC,CAAC,EAAE,CAAC,CAAM,GAClC,IAAI;IAmBP;;;;;OAKG;IACH,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIpC;;;;OAIG;IACH,kBAAkB,IAAI,MAAM,EAAE;IAI9B;;;;;;OAMG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,EAAE,GAAG,IAAI;IAsC1E;;;;;;;OAOG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,GAAG,SAAS;IAKlF;;;;;;OAMG;IACH,QAAQ,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,CAAC,EAAE;IAKjE;;;;;OAKG;IACH,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM;IAKjD;;;;;OAKG;IACH,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,qBAAqB;IAK1D;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,SAAS,gBAAgB,EAAE,cAAc,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC;IAK9E;;;;;;;OAOG;IACH,YAAY,CAAC,oBAAoB,EAAE,MAAM,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,EAAE;IAQ/D;;;;;;;OAOG;IACH,cAAc,CAAC,CAAC,SAAS,gBAAgB,EACvC,oBAAoB,EAAE,MAAM,EAC5B,SAAS,EAAE,EAAE,GACZ,CAAC,EAAE;IAMN;;;;;;;;;;;;OAYG;IACH,oBAAoB,CAAC,CAAC,SAAS,gBAAgB,EAC7C,cAAc,EAAE,MAAM,EACtB,EAAE,EAAE,EAAE,EACN,IAAI,EAAE,CAAC,GACN,YAAY;IAoFf;;;;;;OAMG;IACH,yBAAyB,CAAC,CAAC,SAAS,gBAAgB,EAClD,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,CAAC,EAAE,GACT,iBAAiB;IA0BpB;;;;;;OAMG;IACH,wBAAwB,CAAC,cAAc,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,GAAG,OAAO;IAmCjE;;;;;;OAMG;IACH,6BAA6B,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,MAAM;IAcxE;;;;;;;OAOG;IACH,OAAO,CAAC,oBAAoB;IAQ5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,iBAAiB,CACf,KAAK,EAAE,MAAM,EACb,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,mBAAmB,GACzB,MAAM,CAAC,MAAM,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAAC;QAAE,MAAM,EAAE,gBAAgB,EAAE,CAAC;QAAC,OAAO,EAAE,gBAAgB,CAAC;QAAC,SAAS,EAAE,SAAS,GAAG,QAAQ,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;IA6FlJ;;;;;;;;OAQG;IACH,KAAK,IAAI,IAAI;CAUd"}
@@ -20,7 +20,7 @@ export type { LinkedSearchResult, LinkedSearchResultWithMeta } from '../types';
20
20
  * @param linked - Linked metadata from {@link createSearchLinkedMetadata}
21
21
  * @param limit - Maximum number of results to return (default: 10)
22
22
  * @param opts - Optional search options for controlling match behavior
23
- * @returns Search results with query tokens and matching items
23
+ * @returns Search results with query tokens, matching items, and hasNegation flag
24
24
  *
25
25
  * @example
26
26
  * ```typescript
@@ -1 +1 @@
1
- {"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../src/common/findInLinkedArrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,UAAU,CAAC;AAKlB,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAyF/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAEzC"}
1
+ {"version":3,"file":"findInLinkedArrays.d.ts","sourceRoot":"","sources":["../../src/common/findInLinkedArrays.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,0BAA0B,EAC1B,4BAA4B,EAC7B,MAAM,UAAU,CAAC;AAKlB,YAAY,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,UAAU,CAAC;AAE/E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,0BAA0B,CAAC,QAAQ,EAAE,OAAO,CAAC,CAgG/C;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,wBAAwB,CACtC,QAAQ,SAAS,gBAAgB,EACjC,OAAO,SAAS,gBAAgB,EAEhC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,4BAA4B,CAAC,QAAQ,EAAE,OAAO,CAAC,EACvD,KAAK,GAAE,MAAW,EAClB,IAAI,CAAC,EAAE,UAAU,GAChB,kBAAkB,CAAC,QAAQ,EAAE,OAAO,CAAC,EAAE,CAEzC"}
@@ -20,7 +20,7 @@ import type { NumericLinkedSearchMetadataWithData } from './createSearchLinkedMe
20
20
  * @param linked - Linked metadata from {@link createSearchLinkedMetadataNumeric}
21
21
  * @param limit - Maximum number of results to return (default: 10)
22
22
  * @param opts - Optional search options for controlling match behavior
23
- * @returns Search results with query tokens and matching items
23
+ * @returns Search results with query tokens, matching items, and hasNegation flag
24
24
  */
25
25
  export declare function findInLinkedArraysNumeric<TPrimary extends SearchableObject, TLinked extends SearchableObject>(query: string, linked: NumericLinkedSearchMetadataWithData<TPrimary, TLinked>, limit?: number, opts?: SearchOpts): LinkedSearchResultWithMeta<TPrimary, TLinked>;
26
26
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAIrB;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAoBN;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,SAAS,gBAAgB,EACzE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,IAAI,CAAC,EAAE,UAAU,GAChB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAYZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAsBN"}
1
+ {"version":3,"file":"searchInData.d.ts","sourceRoot":"","sources":["../../../src/common/numeric/searchInData.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,EAAE,EACF,qBAAqB,EACrB,cAAc,EACd,gBAAgB,EAChB,UAAU,EACX,MAAM,aAAa,CAAC;AAIrB;;;;;;;GAOG;AACH,wBAAgB,4BAA4B,CAC1C,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAEN;AAED;;;;;;;;GAQG;AACH,wBAAgB,gCAAgC,CAAC,CAAC,SAAS,gBAAgB,EACzE,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,IAAI,EAAE,cAAc,CAAC,CAAC,CAAC,EACvB,IAAI,CAAC,EAAE,UAAU,GAChB,GAAG,CAAC,EAAE,EAAE,CAAC,CAAC,CAYZ;AAED;;;;;;;;GAQG;AACH,wBAAgB,qCAAqC,CACnD,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,qBAAqB,EAC/B,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,UAAU,GAChB,EAAE,EAAE,CAsBN"}
package/dist/index.cjs CHANGED
@@ -105,7 +105,9 @@ module.exports = __toCommonJS(exports_src);
105
105
  // src/types/index.ts
106
106
  var DEFAULT_SEARCH_OPTS = {
107
107
  matchWords: "start",
108
- matchNumbers: "startEnd"
108
+ matchNumbers: "startEnd",
109
+ matchNumbersShort: "whole",
110
+ matchNumbersThreshold: 6
109
111
  };
110
112
  function arrayToSearchableData(items) {
111
113
  const map = new Map;
@@ -233,7 +235,7 @@ function preprocessString(input) {
233
235
 
234
236
  // src/utils/sanitiseString.ts
235
237
  var RE_DIACRITICS = /[\u0300-\u036f]/g;
236
- var RE_INVALID_CHARS = /[^a-z0-9.\- ]/g;
238
+ var RE_INVALID_CHARS = /[^a-z0-9.\-~<>+*!=? ]/g;
237
239
  function sanitiseString(input) {
238
240
  let result = input;
239
241
  result = result.toLowerCase();
@@ -277,11 +279,16 @@ function tokenize(input, config) {
277
279
  let tokens = input.split(" ").map((token) => token.trim()).filter((token) => token.length > 0);
278
280
  if (config?.replaceTokens) {
279
281
  tokens = tokens.map((token) => {
280
- if (token.startsWith("--") && token.length > 2) {
282
+ if (token.length > 2 && token.startsWith("--")) {
281
283
  const withoutPrefix = token.slice(2);
282
284
  const replaced = config.replaceTokens.get(withoutPrefix);
283
285
  return replaced ? "--" + replaced : token;
284
286
  }
287
+ if (token.length > 1 && token[0] === "-" && /^[a-z]/i.test(token[1])) {
288
+ const withoutPrefix = token.slice(1);
289
+ const replaced = config.replaceTokens.get(withoutPrefix);
290
+ return replaced ? "-" + replaced : token;
291
+ }
285
292
  return config.replaceTokens.get(token) ?? token;
286
293
  });
287
294
  }
@@ -750,14 +757,38 @@ function removeFromSearchMetadataBatchNumeric(metadata, objectIds) {
750
757
  }
751
758
  // src/utils/matchTokenNumeric.ts
752
759
  var RE_NUMERIC = /^[\d.\-]+$/;
760
+ function countDigits(str) {
761
+ let count = 0;
762
+ for (let i = 0;i < str.length; i++) {
763
+ const c = str[i];
764
+ if (c !== "." && c !== "-")
765
+ count++;
766
+ }
767
+ return count;
768
+ }
769
+ function getNumericMatchMode(str, opts) {
770
+ const threshold = opts?.matchNumbersThreshold ?? DEFAULT_SEARCH_OPTS.matchNumbersThreshold;
771
+ const digitCount = countDigits(str);
772
+ if (digitCount <= threshold) {
773
+ return opts?.matchNumbersShort ?? DEFAULT_SEARCH_OPTS.matchNumbersShort;
774
+ }
775
+ return opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers;
776
+ }
753
777
  function prepareQueryTokens(queryTokenStrs) {
754
- return queryTokenStrs.map((str) => {
778
+ return queryTokenStrs.filter((str) => {
779
+ if (str === "-" || str === "--" || str === "~")
780
+ return false;
781
+ return true;
782
+ }).map((str) => {
755
783
  let overrideMode;
756
784
  let negated = false;
757
785
  let cleanStr = str;
758
786
  if (str.length > 2 && str.startsWith("--")) {
759
787
  negated = true;
760
788
  cleanStr = str.slice(2);
789
+ } else if (str.length > 1 && str[0] === "-" && /^[a-z]/i.test(str[1])) {
790
+ negated = true;
791
+ cleanStr = str.slice(1);
761
792
  } else if (str.length > 1 && str.startsWith("~")) {
762
793
  negated = true;
763
794
  cleanStr = str.slice(1);
@@ -780,13 +811,16 @@ function prepareQueryTokens(queryTokenStrs) {
780
811
  cleanStr = str.slice(1);
781
812
  }
782
813
  }
783
- return {
814
+ const result = {
784
815
  str: cleanStr,
785
816
  id: findTokenId(cleanStr),
786
- isNumeric: RE_NUMERIC.test(cleanStr),
787
- overrideMode,
788
- negated: negated || undefined
817
+ isNumeric: RE_NUMERIC.test(cleanStr)
789
818
  };
819
+ if (overrideMode)
820
+ result.overrideMode = overrideMode;
821
+ if (negated)
822
+ result.negated = true;
823
+ return result;
790
824
  });
791
825
  }
792
826
  function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded, opts, overrideMode) {
@@ -801,8 +835,8 @@ function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded,
801
835
  } else if (hasExplicitMode) {
802
836
  mode = decodeMatchMode(targetTokenEncoded);
803
837
  } else {
804
- const isNumeric = RE_NUMERIC.test(searchTokenStr);
805
- mode = isNumeric ? opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
838
+ const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
839
+ mode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
806
840
  }
807
841
  switch (mode) {
808
842
  case "start":
@@ -837,9 +871,8 @@ function binarySearchUint32(arr, target) {
837
871
  return false;
838
872
  }
839
873
  function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
840
- const { str: searchTokenStr, id: searchTokenId, isNumeric, overrideMode } = preparedToken;
841
- const effectiveMode = overrideMode ?? (isNumeric ? opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords);
842
- if (effectiveMode === "whole" && searchTokenId !== -1) {
874
+ const { str: searchTokenStr, id: searchTokenId, overrideMode, negated } = preparedToken;
875
+ if (overrideMode === "whole" && searchTokenId !== -1) {
843
876
  let hasEncodedTokens = false;
844
877
  for (let i = 0;i < targetTokens.length; i++) {
845
878
  if (targetTokens[i] > TOKEN_ID_MASK) {
@@ -856,47 +889,36 @@ function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
856
889
  if (matchWithModeNumeric(searchTokenId, searchTokenStr, targetEncoded, opts, overrideMode)) {
857
890
  return true;
858
891
  }
892
+ if (negated) {
893
+ const targetTokenId = decodeTokenId(targetEncoded);
894
+ const targetTokenStr = tokens[targetTokenId];
895
+ if (targetTokenStr) {
896
+ const hasExplicitMode = targetEncoded !== targetTokenId;
897
+ let targetMode;
898
+ if (overrideMode) {
899
+ targetMode = overrideMode;
900
+ } else if (hasExplicitMode) {
901
+ targetMode = decodeMatchMode(targetEncoded);
902
+ } else {
903
+ const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
904
+ targetMode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
905
+ }
906
+ if ((targetMode === "start" || targetMode === "startEnd") && searchTokenStr.startsWith(targetTokenStr)) {
907
+ return true;
908
+ }
909
+ }
910
+ }
859
911
  }
860
912
  return false;
861
913
  }
862
914
  function matchTokenInSortedListNumeric(searchTokenStr, targetTokens, opts) {
863
- let overrideMode;
864
- let negated = false;
865
- let cleanStr = searchTokenStr;
866
- if (searchTokenStr.length > 2 && searchTokenStr.startsWith("--")) {
867
- negated = true;
868
- cleanStr = searchTokenStr.slice(2);
869
- } else if (searchTokenStr.length > 1 && searchTokenStr.startsWith("~")) {
870
- negated = true;
871
- cleanStr = searchTokenStr.slice(1);
872
- } else if (searchTokenStr.length > 3 && searchTokenStr.startsWith("...")) {
873
- overrideMode = "end";
874
- cleanStr = searchTokenStr.slice(3);
875
- } else if (searchTokenStr.length > 2 && searchTokenStr.startsWith("..")) {
876
- overrideMode = "end";
877
- cleanStr = searchTokenStr.slice(2);
878
- } else if (searchTokenStr.length > 3 && searchTokenStr.endsWith("...")) {
879
- overrideMode = "start";
880
- cleanStr = searchTokenStr.slice(0, -3);
881
- } else if (searchTokenStr.length > 2 && searchTokenStr.endsWith("..")) {
882
- overrideMode = "start";
883
- cleanStr = searchTokenStr.slice(0, -2);
884
- } else if (searchTokenStr.length > 1) {
885
- const firstChar = searchTokenStr[0];
886
- if (firstChar && PREFIX_TO_MATCH_MODE[firstChar]) {
887
- overrideMode = PREFIX_TO_MATCH_MODE[firstChar];
888
- cleanStr = searchTokenStr.slice(1);
889
- }
890
- }
891
- const preparedToken = {
892
- str: cleanStr,
893
- id: findTokenId(cleanStr),
894
- isNumeric: RE_NUMERIC.test(cleanStr),
895
- overrideMode,
896
- negated: negated || undefined
897
- };
915
+ const preparedTokens = prepareQueryTokens([searchTokenStr]);
916
+ if (preparedTokens.length === 0) {
917
+ return true;
918
+ }
919
+ const preparedToken = preparedTokens[0];
898
920
  const matches = matchPreparedTokenInSortedList(preparedToken, targetTokens, opts);
899
- return negated ? !matches : matches;
921
+ return preparedToken.negated ? !matches : matches;
900
922
  }
901
923
  function allPreparedTokensMatch(preparedTokens, targetTokens, opts) {
902
924
  for (const preparedToken of preparedTokens) {
@@ -918,18 +940,7 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
918
940
 
919
941
  // src/common/numeric/searchInData.ts
920
942
  function searchInDataReturnIdsNumeric(query, metadata, opts) {
921
- const queryTokenStrs = prepareStringForSearch(query);
922
- if (queryTokenStrs.length === 0) {
923
- return [];
924
- }
925
- const preparedTokens = prepareQueryTokens(queryTokenStrs);
926
- const results = [];
927
- for (const [id, targetTokens] of metadata) {
928
- if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
929
- results.push(id);
930
- }
931
- }
932
- return results;
943
+ return searchInDataReturnIdsWithLimitNumeric(query, metadata, Infinity, opts);
933
944
  }
934
945
  function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
935
946
  const ids = searchInDataReturnIdsNumeric(query, metadata, opts);
@@ -963,7 +974,7 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
963
974
  function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
964
975
  const queryTokenStrs = prepareStringForSearch(query);
965
976
  if (queryTokenStrs.length === 0) {
966
- return { queryTokens: queryTokenStrs, results: [] };
977
+ return { queryTokens: queryTokenStrs, results: [], hasNegation: false };
967
978
  }
968
979
  const preparedTokens = prepareQueryTokens(queryTokenStrs);
969
980
  const positiveTokens = preparedTokens.filter((t) => !t.negated);
@@ -1061,7 +1072,7 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
1061
1072
  });
1062
1073
  }
1063
1074
  }
1064
- return { queryTokens: queryTokenStrs, results };
1075
+ return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
1065
1076
  }
1066
1077
  function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
1067
1078
  return findInLinkedArraysNumeric(query, linked, limit, opts).results;
@@ -1253,8 +1264,9 @@ function findInArray(query, data, metadata, limit = 10, opts) {
1253
1264
  // src/common/findInLinkedArrays.ts
1254
1265
  function findInLinkedArrays(query, linked, limit = 10, opts) {
1255
1266
  const queryTokens = prepareStringForSearch(query);
1267
+ const hasNegation = queryTokens.some((t) => t.startsWith("--") || t.startsWith("~") || t.length > 1 && t[0] === "-" && /^[a-z]/i.test(t[1]));
1256
1268
  if (queryTokens.length === 0) {
1257
- return { queryTokens, results: [] };
1269
+ return { queryTokens, results: [], hasNegation: false };
1258
1270
  }
1259
1271
  const results = [];
1260
1272
  for (const [primaryId, primary] of linked.primaryData) {
@@ -1307,7 +1319,7 @@ function findInLinkedArrays(query, linked, limit = 10, opts) {
1307
1319
  });
1308
1320
  }
1309
1321
  }
1310
- return { queryTokens, results };
1322
+ return { queryTokens, results, hasNegation };
1311
1323
  }
1312
1324
  function findInLinkedArraysSimple(query, linked, limit = 10, opts) {
1313
1325
  return findInLinkedArrays(query, linked, limit, opts).results;
@@ -1779,7 +1791,7 @@ class SearchUniverse {
1779
1791
  const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, searchOpts);
1780
1792
  if (linkedSearchResults.results.length > 0) {
1781
1793
  const combinedResults = [];
1782
- const hasNegation = query.includes("--");
1794
+ const hasNegation = linkedSearchResults.hasNegation;
1783
1795
  for (const result of linkedSearchResults.results) {
1784
1796
  let linkedItems = result.linked;
1785
1797
  if (result.matchedIn === "primary" && !hasNegation) {
package/dist/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  // src/types/index.ts
2
2
  var DEFAULT_SEARCH_OPTS = {
3
3
  matchWords: "start",
4
- matchNumbers: "startEnd"
4
+ matchNumbers: "startEnd",
5
+ matchNumbersShort: "whole",
6
+ matchNumbersThreshold: 6
5
7
  };
6
8
  function arrayToSearchableData(items) {
7
9
  const map = new Map;
@@ -129,7 +131,7 @@ function preprocessString(input) {
129
131
 
130
132
  // src/utils/sanitiseString.ts
131
133
  var RE_DIACRITICS = /[\u0300-\u036f]/g;
132
- var RE_INVALID_CHARS = /[^a-z0-9.\- ]/g;
134
+ var RE_INVALID_CHARS = /[^a-z0-9.\-~<>+*!=? ]/g;
133
135
  function sanitiseString(input) {
134
136
  let result = input;
135
137
  result = result.toLowerCase();
@@ -173,11 +175,16 @@ function tokenize(input, config) {
173
175
  let tokens = input.split(" ").map((token) => token.trim()).filter((token) => token.length > 0);
174
176
  if (config?.replaceTokens) {
175
177
  tokens = tokens.map((token) => {
176
- if (token.startsWith("--") && token.length > 2) {
178
+ if (token.length > 2 && token.startsWith("--")) {
177
179
  const withoutPrefix = token.slice(2);
178
180
  const replaced = config.replaceTokens.get(withoutPrefix);
179
181
  return replaced ? "--" + replaced : token;
180
182
  }
183
+ if (token.length > 1 && token[0] === "-" && /^[a-z]/i.test(token[1])) {
184
+ const withoutPrefix = token.slice(1);
185
+ const replaced = config.replaceTokens.get(withoutPrefix);
186
+ return replaced ? "-" + replaced : token;
187
+ }
181
188
  return config.replaceTokens.get(token) ?? token;
182
189
  });
183
190
  }
@@ -646,14 +653,38 @@ function removeFromSearchMetadataBatchNumeric(metadata, objectIds) {
646
653
  }
647
654
  // src/utils/matchTokenNumeric.ts
648
655
  var RE_NUMERIC = /^[\d.\-]+$/;
656
+ function countDigits(str) {
657
+ let count = 0;
658
+ for (let i = 0;i < str.length; i++) {
659
+ const c = str[i];
660
+ if (c !== "." && c !== "-")
661
+ count++;
662
+ }
663
+ return count;
664
+ }
665
+ function getNumericMatchMode(str, opts) {
666
+ const threshold = opts?.matchNumbersThreshold ?? DEFAULT_SEARCH_OPTS.matchNumbersThreshold;
667
+ const digitCount = countDigits(str);
668
+ if (digitCount <= threshold) {
669
+ return opts?.matchNumbersShort ?? DEFAULT_SEARCH_OPTS.matchNumbersShort;
670
+ }
671
+ return opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers;
672
+ }
649
673
  function prepareQueryTokens(queryTokenStrs) {
650
- return queryTokenStrs.map((str) => {
674
+ return queryTokenStrs.filter((str) => {
675
+ if (str === "-" || str === "--" || str === "~")
676
+ return false;
677
+ return true;
678
+ }).map((str) => {
651
679
  let overrideMode;
652
680
  let negated = false;
653
681
  let cleanStr = str;
654
682
  if (str.length > 2 && str.startsWith("--")) {
655
683
  negated = true;
656
684
  cleanStr = str.slice(2);
685
+ } else if (str.length > 1 && str[0] === "-" && /^[a-z]/i.test(str[1])) {
686
+ negated = true;
687
+ cleanStr = str.slice(1);
657
688
  } else if (str.length > 1 && str.startsWith("~")) {
658
689
  negated = true;
659
690
  cleanStr = str.slice(1);
@@ -676,13 +707,16 @@ function prepareQueryTokens(queryTokenStrs) {
676
707
  cleanStr = str.slice(1);
677
708
  }
678
709
  }
679
- return {
710
+ const result = {
680
711
  str: cleanStr,
681
712
  id: findTokenId(cleanStr),
682
- isNumeric: RE_NUMERIC.test(cleanStr),
683
- overrideMode,
684
- negated: negated || undefined
713
+ isNumeric: RE_NUMERIC.test(cleanStr)
685
714
  };
715
+ if (overrideMode)
716
+ result.overrideMode = overrideMode;
717
+ if (negated)
718
+ result.negated = true;
719
+ return result;
686
720
  });
687
721
  }
688
722
  function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded, opts, overrideMode) {
@@ -697,8 +731,8 @@ function matchWithModeNumeric(searchTokenId, searchTokenStr, targetTokenEncoded,
697
731
  } else if (hasExplicitMode) {
698
732
  mode = decodeMatchMode(targetTokenEncoded);
699
733
  } else {
700
- const isNumeric = RE_NUMERIC.test(searchTokenStr);
701
- mode = isNumeric ? opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
734
+ const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
735
+ mode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
702
736
  }
703
737
  switch (mode) {
704
738
  case "start":
@@ -733,9 +767,8 @@ function binarySearchUint32(arr, target) {
733
767
  return false;
734
768
  }
735
769
  function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
736
- const { str: searchTokenStr, id: searchTokenId, isNumeric, overrideMode } = preparedToken;
737
- const effectiveMode = overrideMode ?? (isNumeric ? opts?.matchNumbers ?? DEFAULT_SEARCH_OPTS.matchNumbers : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords);
738
- if (effectiveMode === "whole" && searchTokenId !== -1) {
770
+ const { str: searchTokenStr, id: searchTokenId, overrideMode, negated } = preparedToken;
771
+ if (overrideMode === "whole" && searchTokenId !== -1) {
739
772
  let hasEncodedTokens = false;
740
773
  for (let i = 0;i < targetTokens.length; i++) {
741
774
  if (targetTokens[i] > TOKEN_ID_MASK) {
@@ -752,47 +785,36 @@ function matchPreparedTokenInSortedList(preparedToken, targetTokens, opts) {
752
785
  if (matchWithModeNumeric(searchTokenId, searchTokenStr, targetEncoded, opts, overrideMode)) {
753
786
  return true;
754
787
  }
788
+ if (negated) {
789
+ const targetTokenId = decodeTokenId(targetEncoded);
790
+ const targetTokenStr = tokens[targetTokenId];
791
+ if (targetTokenStr) {
792
+ const hasExplicitMode = targetEncoded !== targetTokenId;
793
+ let targetMode;
794
+ if (overrideMode) {
795
+ targetMode = overrideMode;
796
+ } else if (hasExplicitMode) {
797
+ targetMode = decodeMatchMode(targetEncoded);
798
+ } else {
799
+ const isTargetNumeric = RE_NUMERIC.test(targetTokenStr);
800
+ targetMode = isTargetNumeric ? getNumericMatchMode(targetTokenStr, opts) : opts?.matchWords ?? DEFAULT_SEARCH_OPTS.matchWords;
801
+ }
802
+ if ((targetMode === "start" || targetMode === "startEnd") && searchTokenStr.startsWith(targetTokenStr)) {
803
+ return true;
804
+ }
805
+ }
806
+ }
755
807
  }
756
808
  return false;
757
809
  }
758
810
  function matchTokenInSortedListNumeric(searchTokenStr, targetTokens, opts) {
759
- let overrideMode;
760
- let negated = false;
761
- let cleanStr = searchTokenStr;
762
- if (searchTokenStr.length > 2 && searchTokenStr.startsWith("--")) {
763
- negated = true;
764
- cleanStr = searchTokenStr.slice(2);
765
- } else if (searchTokenStr.length > 1 && searchTokenStr.startsWith("~")) {
766
- negated = true;
767
- cleanStr = searchTokenStr.slice(1);
768
- } else if (searchTokenStr.length > 3 && searchTokenStr.startsWith("...")) {
769
- overrideMode = "end";
770
- cleanStr = searchTokenStr.slice(3);
771
- } else if (searchTokenStr.length > 2 && searchTokenStr.startsWith("..")) {
772
- overrideMode = "end";
773
- cleanStr = searchTokenStr.slice(2);
774
- } else if (searchTokenStr.length > 3 && searchTokenStr.endsWith("...")) {
775
- overrideMode = "start";
776
- cleanStr = searchTokenStr.slice(0, -3);
777
- } else if (searchTokenStr.length > 2 && searchTokenStr.endsWith("..")) {
778
- overrideMode = "start";
779
- cleanStr = searchTokenStr.slice(0, -2);
780
- } else if (searchTokenStr.length > 1) {
781
- const firstChar = searchTokenStr[0];
782
- if (firstChar && PREFIX_TO_MATCH_MODE[firstChar]) {
783
- overrideMode = PREFIX_TO_MATCH_MODE[firstChar];
784
- cleanStr = searchTokenStr.slice(1);
785
- }
786
- }
787
- const preparedToken = {
788
- str: cleanStr,
789
- id: findTokenId(cleanStr),
790
- isNumeric: RE_NUMERIC.test(cleanStr),
791
- overrideMode,
792
- negated: negated || undefined
793
- };
811
+ const preparedTokens = prepareQueryTokens([searchTokenStr]);
812
+ if (preparedTokens.length === 0) {
813
+ return true;
814
+ }
815
+ const preparedToken = preparedTokens[0];
794
816
  const matches = matchPreparedTokenInSortedList(preparedToken, targetTokens, opts);
795
- return negated ? !matches : matches;
817
+ return preparedToken.negated ? !matches : matches;
796
818
  }
797
819
  function allPreparedTokensMatch(preparedTokens, targetTokens, opts) {
798
820
  for (const preparedToken of preparedTokens) {
@@ -814,18 +836,7 @@ function allTokensMatchNumeric(searchTokenStrs, targetTokens, opts) {
814
836
 
815
837
  // src/common/numeric/searchInData.ts
816
838
  function searchInDataReturnIdsNumeric(query, metadata, opts) {
817
- const queryTokenStrs = prepareStringForSearch(query);
818
- if (queryTokenStrs.length === 0) {
819
- return [];
820
- }
821
- const preparedTokens = prepareQueryTokens(queryTokenStrs);
822
- const results = [];
823
- for (const [id, targetTokens] of metadata) {
824
- if (allPreparedTokensMatch(preparedTokens, targetTokens, opts)) {
825
- results.push(id);
826
- }
827
- }
828
- return results;
839
+ return searchInDataReturnIdsWithLimitNumeric(query, metadata, Infinity, opts);
829
840
  }
830
841
  function searchInDataReturnObjectsNumeric(query, metadata, data, opts) {
831
842
  const ids = searchInDataReturnIdsNumeric(query, metadata, opts);
@@ -859,7 +870,7 @@ function searchInDataReturnIdsWithLimitNumeric(query, metadata, limit, opts) {
859
870
  function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
860
871
  const queryTokenStrs = prepareStringForSearch(query);
861
872
  if (queryTokenStrs.length === 0) {
862
- return { queryTokens: queryTokenStrs, results: [] };
873
+ return { queryTokens: queryTokenStrs, results: [], hasNegation: false };
863
874
  }
864
875
  const preparedTokens = prepareQueryTokens(queryTokenStrs);
865
876
  const positiveTokens = preparedTokens.filter((t) => !t.negated);
@@ -957,7 +968,7 @@ function findInLinkedArraysNumeric(query, linked, limit = 10, opts) {
957
968
  });
958
969
  }
959
970
  }
960
- return { queryTokens: queryTokenStrs, results };
971
+ return { queryTokens: queryTokenStrs, results, hasNegation: negatedTokens.length > 0 };
961
972
  }
962
973
  function findInLinkedArraysSimpleNumeric(query, linked, limit = 10, opts) {
963
974
  return findInLinkedArraysNumeric(query, linked, limit, opts).results;
@@ -1149,8 +1160,9 @@ function findInArray(query, data, metadata, limit = 10, opts) {
1149
1160
  // src/common/findInLinkedArrays.ts
1150
1161
  function findInLinkedArrays(query, linked, limit = 10, opts) {
1151
1162
  const queryTokens = prepareStringForSearch(query);
1163
+ const hasNegation = queryTokens.some((t) => t.startsWith("--") || t.startsWith("~") || t.length > 1 && t[0] === "-" && /^[a-z]/i.test(t[1]));
1152
1164
  if (queryTokens.length === 0) {
1153
- return { queryTokens, results: [] };
1165
+ return { queryTokens, results: [], hasNegation: false };
1154
1166
  }
1155
1167
  const results = [];
1156
1168
  for (const [primaryId, primary] of linked.primaryData) {
@@ -1203,7 +1215,7 @@ function findInLinkedArrays(query, linked, limit = 10, opts) {
1203
1215
  });
1204
1216
  }
1205
1217
  }
1206
- return { queryTokens, results };
1218
+ return { queryTokens, results, hasNegation };
1207
1219
  }
1208
1220
  function findInLinkedArraysSimple(query, linked, limit = 10, opts) {
1209
1221
  return findInLinkedArrays(query, linked, limit, opts).results;
@@ -1675,7 +1687,7 @@ class SearchUniverse {
1675
1687
  const linkedSearchResults = findInLinkedArraysNumeric(query, linkedMetadata, limit, searchOpts);
1676
1688
  if (linkedSearchResults.results.length > 0) {
1677
1689
  const combinedResults = [];
1678
- const hasNegation = query.includes("--");
1690
+ const hasNegation = linkedSearchResults.hasNegation;
1679
1691
  for (const result of linkedSearchResults.results) {
1680
1692
  let linkedItems = result.linked;
1681
1693
  if (result.matchedIn === "primary" && !hasNegation) {
@@ -54,7 +54,11 @@ export interface SearchableObjectSpec<T extends SearchableObject, C = unknown> {
54
54
  fieldMatchModes?: Partial<Record<keyof T, MatchMode>>;
55
55
  }
56
56
  /**
57
- * Options for controlling how search matching works
57
+ * Options for controlling how search matching works.
58
+ *
59
+ * **Important:** Match mode for numbers is determined by the TARGET token's length,
60
+ * not the search query's length. This allows short codes (e.g., "9001") to require
61
+ * exact match while long barcodes (e.g., "3838989760731") allow prefix/suffix matching.
58
62
  */
59
63
  export interface SearchOpts {
60
64
  /**
@@ -63,10 +67,21 @@ export interface SearchOpts {
63
67
  */
64
68
  matchWords?: MatchMode;
65
69
  /**
66
- * How to match numbers (numeric tokens)
70
+ * How to match long numbers (more digits than matchNumbersThreshold)
67
71
  * @default 'startEnd'
68
72
  */
69
73
  matchNumbers?: MatchMode;
74
+ /**
75
+ * How to match short numbers (digits <= matchNumbersThreshold)
76
+ * @default 'whole'
77
+ */
78
+ matchNumbersShort?: MatchMode;
79
+ /**
80
+ * Threshold for short vs long number matching (count of digits, excluding . and -)
81
+ * Numbers with <= this many digits use matchNumbersShort, otherwise matchNumbers
82
+ * @default 6
83
+ */
84
+ matchNumbersThreshold?: number;
70
85
  }
71
86
  /** Default search options */
72
87
  export declare const DEFAULT_SEARCH_OPTS: Required<SearchOpts>;
@@ -125,7 +140,8 @@ export interface LinkedSearchResult<TPrimary, TLinked> {
125
140
  /**
126
141
  * Result of a linked search with query metadata.
127
142
  *
128
- * Includes the tokenized query for debugging/highlighting purposes.
143
+ * Includes the tokenized query for debugging/highlighting purposes,
144
+ * and a flag indicating whether the query contained negation tokens.
129
145
  *
130
146
  * @typeParam TPrimary - Type of primary items (e.g., Stranka)
131
147
  * @typeParam TLinked - Type of linked items (e.g., Pacient)
@@ -135,6 +151,8 @@ export interface LinkedSearchResultWithMeta<TPrimary, TLinked> {
135
151
  queryTokens: SearchTokenSortedList;
136
152
  /** Array of matching results */
137
153
  results: LinkedSearchResult<TPrimary, TLinked>[];
154
+ /** Whether the query contains negated tokens (--word, -word, ~word) */
155
+ hasNegation: boolean;
138
156
  }
139
157
  /**
140
158
  * Result of a search operation with metadata included.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,14 @@ export interface PreparedQueryToken {
12
12
  /** If true, this token must NOT appear in the target (negation with -- or ~ prefix) */
13
13
  negated?: boolean;
14
14
  }
15
+ /**
16
+ * Checks if any prepared tokens have negation.
17
+ * Use this to determine if negation filtering should be applied.
18
+ *
19
+ * @param preparedTokens - Array of prepared tokens
20
+ * @returns True if any token is negated
21
+ */
22
+ export declare function hasNegatedTokens(preparedTokens: PreparedQueryToken[]): boolean;
15
23
  /**
16
24
  * Prepares query tokens by pre-computing their IDs.
17
25
  * Call this ONCE at the start of a search to avoid repeated findTokenId calls.
@@ -23,7 +31,9 @@ export interface PreparedQueryToken {
23
31
  * - `+word` = startEnd (prefix or suffix)
24
32
  * - `*word` or `?word` = anywhere (substring match)
25
33
  * - `word..` = start (prefix match, suffix notation)
26
- * - `--word` or `~word` = negation (token must NOT appear)
34
+ * - `-word` or `~word` = negation (token must NOT appear, only if followed by letter)
35
+ *
36
+ * Bare prefix tokens (e.g., just `-` or `~` without a word) are filtered out.
27
37
  *
28
38
  * @param queryTokenStrs - Array of query token strings
29
39
  * @returns Array of prepared tokens with pre-computed IDs
@@ -41,6 +51,11 @@ export declare function binarySearchUint32(arr: Uint32Array, target: number): bo
41
51
  * Checks if a prepared search token matches any token in a sorted numeric list.
42
52
  * Uses pre-computed token ID for efficiency.
43
53
  *
54
+ * For negated tokens with prefix modes (start/startEnd), also checks if the search
55
+ * token starts with the target token (reverse check). This handles cases where
56
+ * stored tokens are shortened versions (e.g., "izdelek" → "izd"), so searching
57
+ * "--izde" correctly matches and excludes items with "izd".
58
+ *
44
59
  * @param preparedToken - The prepared token with pre-computed ID
45
60
  * @param targetTokens - Sorted Uint32Array of target token IDs
46
61
  * @param opts - Optional search options for match mode
@@ -59,7 +74,7 @@ export declare function matchPreparedTokenInSortedList(preparedToken: PreparedQu
59
74
  * - `+word` = startEnd
60
75
  * - `*word` or `?word` = anywhere
61
76
  * - `word..` = start
62
- * - `--word` or `~word` = negation (returns true if NOT found)
77
+ * - `-word` or `~word` = negation (returns true if NOT found)
63
78
  *
64
79
  * @param searchTokenStr - The string token to search for (may include prefix/suffix)
65
80
  * @param targetTokens - Sorted Uint32Array of target token IDs
@@ -1 +1 @@
1
- {"version":3,"file":"matchTokenNumeric.d.ts","sourceRoot":"","sources":["../../src/utils/matchTokenNumeric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAS9E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,kBAAkB,EAAE,CAoDjF;AAmED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB5E;AAED;;;;;;;;GAQG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAmCT;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAsDT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,kBAAkB,EAAE,EACpC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAYT;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EAAE,EACzB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAGT"}
1
+ {"version":3,"file":"matchTokenNumeric.d.ts","sourceRoot":"","sources":["../../src/utils/matchTokenNumeric.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,UAAU,CAAC;AAiC9E;;;GAGG;AACH,MAAM,WAAW,kBAAkB;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,OAAO,CAAC;IACnB,6EAA6E;IAC7E,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,uFAAuF;IACvF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,cAAc,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAE9E;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,kBAAkB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,kBAAkB,EAAE,CA8DjF;AAoED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAiB5E;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,8BAA8B,CAC5C,aAAa,EAAE,kBAAkB,EACjC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CA4DT;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,6BAA6B,CAC3C,cAAc,EAAE,MAAM,EACtB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAaT;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,kBAAkB,EAAE,EACpC,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAYT;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,eAAe,EAAE,MAAM,EAAE,EACzB,YAAY,EAAE,sBAAsB,EACpC,IAAI,CAAC,EAAE,UAAU,GAChB,OAAO,CAGT"}
@@ -4,7 +4,8 @@
4
4
  * Processing steps:
5
5
  * 1. Converts to lowercase (case-insensitive search)
6
6
  * 2. Removes diacritics using Unicode normalization (č,ć → c, š → s, etc.)
7
- * 3. Keeps only letters (a-z), digits (0-9), periods (.), minuses (-), and spaces
7
+ * 3. Keeps only letters (a-z), digits (0-9), periods (.), minuses (-), tildes (~),
8
+ * query prefixes (<, >, +, *, !, =, ?), and spaces
8
9
  * 4. Replaces all other characters with space
9
10
  *
10
11
  * @param input - The preprocessed string to sanitise
@@ -12,14 +13,17 @@
12
13
  *
13
14
  * @example
14
15
  * ```typescript
15
- * sanitiseString('Čokolada Milka!')
16
- * // Returns: 'cokolada milka '
16
+ * sanitiseString('Čokolada Milka')
17
+ * // Returns: 'cokolada milka'
17
18
  *
18
19
  * sanitiseString('Žaba ČAČA')
19
20
  * // Returns: 'zaba caca'
20
21
  *
21
22
  * sanitiseString('price: $12.50')
22
23
  * // Returns: 'price 12.50'
24
+ *
25
+ * sanitiseString('=exact ~negate')
26
+ * // Returns: '=exact ~negate' (query prefixes preserved)
23
27
  * ```
24
28
  */
25
29
  export declare function sanitiseString(input: string): string;
@@ -1 +1 @@
1
- {"version":3,"file":"sanitiseString.d.ts","sourceRoot":"","sources":["../../src/utils/sanitiseString.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAepD"}
1
+ {"version":3,"file":"sanitiseString.d.ts","sourceRoot":"","sources":["../../src/utils/sanitiseString.ts"],"names":[],"mappings":"AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAepD"}
@@ -1 +1 @@
1
- {"version":3,"file":"tokenize.d.ts","sourceRoot":"","sources":["../../src/utils/tokenize.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,qBAcjC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,aAUhC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CAuBzE"}
1
+ {"version":3,"file":"tokenize.d.ts","sourceRoot":"","sources":["../../src/utils/tokenize.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACpC,8BAA8B;IAC9B,YAAY,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IAC3B;;;;OAIG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACjC;AAED,kDAAkD;AAClD,eAAO,MAAM,sBAAsB,qBAcjC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,qBAAqB,aAUhC,CAAC;AAEH,0EAA0E;AAC1E,eAAO,MAAM,uBAAuB,EAAE,cAGrC,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,cAAc,GAAG,MAAM,EAAE,CA6BzE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-search",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "A fast, memory-efficient search library for large datasets with support for tokenized matching, linked collections, and per-field match modes.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",