@devisfuture/mega-collection 1.3.1 → 2.0.3

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/README.md CHANGED
@@ -6,53 +6,54 @@
6
6
 
7
7
  If this package saved you some time, a ⭐ on GitHub would be much appreciated.
8
8
 
9
- # @devisfuture/mega-collection
10
-
11
- > Search, filter & sort engine for **100K+** item collections in JavaScript / TypeScript.
12
-
13
- ## Navigation
14
-
15
- - [What does this package solve](#what-does-this-package-solve) – explains the core problem addressed by the library
16
- - [Features](#features) – overview of package capabilities
17
- - [React demo](#react-demo) – link to example app and live demo
18
- - [Install](#install) – npm installation instructions
19
- - [Quick Start](#quick-start) – examples for using the engines
20
- - [All-in-one: `MergeEngines`](#all-in-one-mergeengines) – combine search, filter, sort
21
- - [Search only](#search-only) – text search engine usage
22
- - [Filter only](#filter-only) – multi-criteria filter engine
23
- - [Sort only](#sort-only) – sort engine examples
24
- - [API Reference](#api-reference) – detailed methods and options
25
- - [`MergeEngines<T>`](#mergeenginest-root-module) – unified facade around dataset
26
- - [`TextSearchEngine<T>`](#textsearchenginet-search-module) – trigram search engine
27
- - [`FilterEngine<T>`](#filterenginet-filter-module) – indexed filter engine
28
- - [`SortEngine<T>`](#sortenginet-sort-module) – pre-sorted comparators
29
- - [Types](#types) – exported TypeScript types
30
- - [Architecture](#architecture) – project structure overview
31
- - [Build](#build) – build and development commands
32
- - [Contributing](#contributing) – guidelines for contributors
33
- - [Security](#security) – policy information
34
- - [License](#license) – MIT license terms
9
+ ## Table of Contents
10
+
11
+ - [What does this package solve](#what-does-this-package-solve) what problem this package helps with
12
+ - [Features](#features) – what the package can do
13
+ - [React demo](#react-demo) – example project and live demo
14
+ - [Install](#install) – how to install the package
15
+ - [Quick Start](#quick-start) – basic usage examples
16
+ - [All-in-one: `MergeEngines`](#all-in-one-mergeengines) – use search, filter, and sort from one engine
17
+ - [Search only](#search-only) – use only text search
18
+ - [Flat collections search](#flat-collections-search) – search simple fields like `name` or `city`
19
+ - [Nested collections search](#nested-collections-search) – search inside nested arrays like `orders.status`
20
+ - [Filter only](#filter-only) – use only filtering
21
+ - [Flat collections filter](#flat-collections-filter) – filter by simple top-level fields
22
+ - [Nested collections filter](#nested-collections-filter) – filter by nested array fields
23
+ - [Sort only](#sort-only) – use only sorting
24
+ - [API Reference](#api-reference) – list of options and methods
25
+ - [`MergeEngines<T>`](#mergeenginest-root-module) – one engine that combines everything
26
+ - [`TextSearchEngine<T>`](#textsearchenginet-search-module) – engine for text search
27
+ - [`FilterEngine<T>`](#filterenginet-filter-module) – engine for filtering
28
+ - [`SortEngine<T>`](#sortenginet-sort-module) – engine for sorting
29
+ - [Types](#types) – TypeScript types you can import
30
+ - [Build](#build) – commands for build and development
31
+ - [Contributing](#contributing) – contribution rules
32
+ - [Security](#security) – security policy
33
+ - [License](#license) – license information
35
34
 
36
35
  ## What does this package solve
37
36
 
38
37
  Sometimes in projects, you need to iterate through huge collections (100K+ elements in an array) that have come from the server. Usually, the most common features are searching, filtering, and sorting.
39
- So, this package helps to perform searching, filtering, and sorting of large collections faster than standard JavaScript methods. This operation is performed before rendering the UI content.
38
+ This package helps you search, filter, and sort large collections faster than plain JavaScript methods. Usually you do this before showing data in the UI.
40
39
 
41
40
  Zero dependencies. Tree-shakeable. Import only what you need.
42
41
 
43
42
  Each engine lives in its own entry point (`/search`, `/filter`, `/sort`).
44
43
  Importing just `@devisfuture/mega-collection/search` or the other sub-modules means
45
- only that code ends up in your bundle unused engines stay out. For example, if
46
- you only pull in `TextSearchEngine` the filter and sort logic won’t be included.
44
+ only that code goes into your bundle. Unused engines stay out. For example, if
45
+ you only import `TextSearchEngine`, the filter and sort code will not be included.
47
46
 
48
47
  ## Features
49
48
 
50
- | Capability | Strategy | Complexity |
51
- | -------------------------- | -------------------------------------- | ---------------------------------- |
52
- | **Indexed filter** | Hash-Map index (`Map<value, T[]>`) | **O(1)** |
53
- | **Multi-value filter** | Index intersection + `Set` membership | **O(k)** indexed / **O(n)** linear |
54
- | **Text search** (contains) | Trigram inverted index + verify | **O(candidates)** |
55
- | **Sorting** | Pre-sorted index (cached) / V8 TimSort | **O(n)** cached / **O(n log n)** |
49
+ | Capability | Strategy | Complexity |
50
+ | ---------------------------- | -------------------------------------- | ---------------------------------- |
51
+ | **Indexed filter** | Hash-Map index (`Map<value, T[]>`) | **O(1)** |
52
+ | **Multi-value filter** | Index intersection + `Set` membership | **O(k)** indexed / **O(n)** linear |
53
+ | **Nested collection filter** | Pre-built nested index + `Set` lookup | **O(k)** indexed / **O(n)** linear |
54
+ | **Text search** (contains) | Trigram inverted index + verify | **O(candidates)** |
55
+ | **Nested collection search** | Nested trigram index + verify | **O(candidates)** |
56
+ | **Sorting** | Pre-sorted index (cached) / V8 TimSort | **O(n)** cached / **O(n log n)** |
56
57
 
57
58
  ## React demo
58
59
 
@@ -78,18 +79,31 @@ interface User {
78
79
  city: string;
79
80
  age: number;
80
81
  }
82
+
83
+ interface Order {
84
+ id: string;
85
+ status: string;
86
+ }
87
+
88
+ interface UserWithOrders extends User {
89
+ orders: Order[];
90
+ }
81
91
  ```
82
92
 
83
93
  ### All-in-one: `MergeEngines`
84
94
 
85
- Use `MergeEngines` to combine search, filter and sort around a single shared dataset.
86
- Declare which engines you need in `imports` only those are initialised.
95
+ Use `MergeEngines` when you want one engine that works with the same dataset.
96
+ Add the engines you need in `imports`. Only those engines will be created.
97
+
98
+ You can create many engine instances in one project for different collections.
99
+ Each instance keeps its own data and its own indexes, so they work separately
100
+ and do not overwrite each other.
87
101
 
88
102
  Each engine accepts an optional `fields` array (set via the `search`,
89
103
  `filter` or `sort` option) which tells it which properties should be indexed up
90
- front. Indexes power the fast paths used throughout the library; you can leave
91
- these options out and everything still works, but the code will fall back to
92
- simple linear scans.
104
+ front. This means the engine prepares those fields once so later calls can be
105
+ faster. If you skip `fields`, everything still works, but the engine may need
106
+ to scan the full array.
93
107
 
94
108
  ```ts
95
109
  import { MergeEngines } from "@devisfuture/mega-collection";
@@ -111,6 +125,26 @@ engine
111
125
  .sort([{ field: "age", direction: "asc" }])
112
126
  .filter([{ field: "city", values: ["Miami", "New York"] }]);
113
127
 
128
+ // with nested collection fields (e.g. orders inside each user)
129
+ const nestedEngine = new MergeEngines<UserWithOrders>({
130
+ imports: [TextSearchEngine, SortEngine, FilterEngine],
131
+ data: usersWithOrders,
132
+ search: {
133
+ fields: ["name", "city"],
134
+ nestedFields: ["orders.status"],
135
+ minQueryLength: 2,
136
+ },
137
+ filter: {
138
+ fields: ["city", "age"],
139
+ nestedFields: ["orders.status"],
140
+ filterByPreviousResult: true,
141
+ },
142
+ sort: { fields: ["age", "name", "city"] },
143
+ });
144
+
145
+ nestedEngine.search("pending"); // finds users whose orders contain "pending"
146
+ nestedEngine.filter([{ field: "orders.status", values: ["delivered"] }]);
147
+
114
148
  // update dataset later without creating a new instance
115
149
  engine.data([
116
150
  {
@@ -133,24 +167,24 @@ engine.getOriginData();
133
167
 
134
168
  ### Search only
135
169
 
170
+ Use `TextSearchEngine` when you only need text search.
171
+ The examples below show the difference between simple fields and nested fields.
172
+
173
+ #### Flat collections search
174
+
136
175
  ```ts
137
176
  import { TextSearchEngine } from "@devisfuture/mega-collection/search";
138
177
 
139
- // `fields` tells the engine which properties to index for fast lookups. The
140
- // index is built once during construction; if you omit `fields` the engine
141
- // still works but every search will scan the entire dataset.
178
+ // `fields` tells the engine which fields it should prepare for faster search.
179
+ // If you skip `fields`, search still works, but it will scan the full dataset.
142
180
  const engine = new TextSearchEngine<User>({
143
181
  data: users,
144
182
  fields: ["name", "city"],
145
183
  minQueryLength: 2, // begins searching when query length >= 2
146
184
  });
147
185
 
148
- // note: inputs shorter than `minQueryLength` bypass the indexed search and
149
- // simply return the original dataset (rather than clearing the result).
150
- // A one‑character search usually matches most of the dataset, so avoiding
151
- // extra work makes typing feel snappier. Once the query reaches the
152
- // threshold the indexed search kicks in and performance improves
153
- // dramatically. Empty/blank queries return the original dataset.
186
+ // If the query is shorter than `minQueryLength`, the engine returns the
187
+ // original dataset. Empty or blank queries also return the original dataset.
154
188
 
155
189
  engine.search("john"); // searches all indexed fields, deduplicated
156
190
  engine.search("name", "john"); // searches a specific field
@@ -161,19 +195,40 @@ engine.data(users);
161
195
  // access original dataset stored in the engine
162
196
  engine.getOriginData();
163
197
 
164
- // chain support
165
- engine.search("john").clearIndexes().clearData();
198
+ // service methods stay on the engine instance
199
+ engine.clearIndexes();
200
+ engine.clearData();
201
+ ```
202
+
203
+ #### Nested collections search
204
+
205
+ ```ts
206
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
207
+
208
+ // Search inside nested arrays. `nestedFields` uses dot notation.
209
+ const nestedSearch = new TextSearchEngine<UserWithOrders>({
210
+ data: usersWithOrders,
211
+ fields: ["name", "city"],
212
+ nestedFields: ["orders.status"],
213
+ minQueryLength: 2,
214
+ });
215
+
216
+ nestedSearch.search("pending"); // finds users whose orders match
217
+ nestedSearch.search("orders.status", "delivered"); // search a specific nested field
166
218
  ```
167
219
 
168
220
  ### Filter only
169
221
 
222
+ Use `FilterEngine` when you only need filtering.
223
+ The examples below show the difference between simple fields and nested fields.
224
+
225
+ #### Flat collections filter
226
+
170
227
  ```ts
171
228
  import { FilterEngine } from "@devisfuture/mega-collection/filter";
172
229
 
173
- // `fields` config tells the filter engine which properties should have an
174
- // index built. Indexed lookups are O(1) per value, so multi-criteria queries
175
- // can be orders of magnitude faster. Without `fields` the engine still filters
176
- // correctly but always does a linear scan.
230
+ // `fields` tells the engine which fields should be prepared for faster filter
231
+ // lookups. Without `fields`, filtering still works, but it will scan the data.
177
232
  const engine = new FilterEngine<User>({
178
233
  data: users,
179
234
  fields: ["city", "age"],
@@ -201,26 +256,45 @@ engine
201
256
  // Sequential mode example:
202
257
  // 1) First call filters by city
203
258
  const byCity = engine.filter([{ field: "city", values: ["Miami"] }]);
204
- // 2) Second call filters only inside previous result
259
+ // 2) Second call filters only inside the previous result
205
260
  const byCityAndAge = engine.filter([{ field: "age", values: [22] }]);
206
261
  ```
207
262
 
263
+ #### Nested collections filter
264
+
265
+ ```ts
266
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
267
+
268
+ // Filter inside nested arrays. `nestedFields` uses dot notation.
269
+ const nestedFilter = new FilterEngine<UserWithOrders>({
270
+ data: usersWithOrders,
271
+ fields: ["city", "age"],
272
+ nestedFields: ["orders.status"],
273
+ filterByPreviousResult: true,
274
+ });
275
+
276
+ nestedFilter.filter([{ field: "orders.status", values: ["pending"] }]);
277
+ nestedFilter.filter([
278
+ { field: "orders.status", values: ["pending"] },
279
+ { field: "city", values: ["New-York"] },
280
+ ]);
281
+ ```
282
+
208
283
  ### Sort only
209
284
 
285
+ Use `SortEngine` when you only need sorting.
286
+
210
287
  ```ts
211
288
  import { SortEngine } from "@devisfuture/mega-collection/sort";
212
289
 
213
- // `fields` instructs the engine to pre-build a sorted index for each property.
214
- // When you later run a single-field sort the result can be pulled directly
215
- // from that index in linear time. If you leave out `fields` the engine still
216
- // sorts correctly, it merely falls back to standard `Array.prototype.sort`
217
- // (O(n log n)).
290
+ // `fields` tells the engine which fields should be prepared for faster
291
+ // single-field sorting. If you skip `fields`, sorting still works.
218
292
  const engine = new SortEngine<User>({
219
293
  data: users,
220
294
  fields: ["age", "name", "city"],
221
295
  });
222
296
 
223
- // Single-field sort — O(n) via cached index
297
+ // Single-field sort
224
298
  engine.sort([{ field: "age", direction: "asc" }]);
225
299
 
226
300
  // replace dataset without re-initializing
@@ -229,14 +303,11 @@ engine.data(users);
229
303
  // access original dataset stored in the engine
230
304
  engine.getOriginData();
231
305
 
232
- // chain support
233
- engine
234
- .sort([{ field: "age", direction: "asc" }])
235
- .sort([{ field: "name", direction: "desc" }])
236
- .clearIndexes()
237
- .clearData();
306
+ // service methods stay on the engine instance
307
+ engine.clearIndexes();
308
+ engine.clearData();
238
309
 
239
- // Multi-field sort — O(n log n)
310
+ // Multi-field sort
240
311
  engine.sort([
241
312
  { field: "age", direction: "asc" },
242
313
  { field: "name", direction: "desc" },
@@ -249,7 +320,7 @@ engine.sort([
249
320
 
250
321
  ### `MergeEngines<T>` (root module)
251
322
 
252
- Unified facade that composes all three engines around a shared dataset.
323
+ One engine that brings search, filter, and sort together on the same dataset.
253
324
 
254
325
  **Constructor options:**
255
326
 
@@ -257,8 +328,8 @@ Unified facade that composes all three engines around a shared dataset.
257
328
  | --------- | ----------------------------------------------------------- | -------------------------------------------- |
258
329
  | `imports` | `(typeof TextSearchEngine \| SortEngine \| FilterEngine)[]` | Engine classes to activate |
259
330
  | `data` | `T[]` | Shared dataset — passed once at construction |
260
- | `search` | `{ fields, minQueryLength? }` | Config for TextSearchEngine |
261
- | `filter` | `{ fields, filterByPreviousResult? }` | Config for FilterEngine |
331
+ | `search` | `{ fields, nestedFields?, minQueryLength? }` | Config for TextSearchEngine |
332
+ | `filter` | `{ fields, nestedFields?, filterByPreviousResult? }` | Config for FilterEngine |
262
333
  | `sort` | `{ fields }` | Config for SortEngine |
263
334
 
264
335
  **Methods:**
@@ -280,40 +351,45 @@ Unified facade that composes all three engines around a shared dataset.
280
351
 
281
352
  ### `TextSearchEngine<T>` (search module)
282
353
 
283
- Trigram-based text search engine.
354
+ Text search engine. It supports `nestedFields` if you need to search inside
355
+ nested collections such as `["orders.status"]`. Search methods return plain
356
+ arrays.
284
357
 
285
- | Method | Description |
286
- | ---------------------- | ----------------------------------------------------- |
287
- | `search(query)` | Search all indexed fields, deduplicated |
288
- | `search(field, query)` | Search a specific indexed field |
289
- | `getOriginData()` | Get the original stored dataset |
290
- | `data(data)` | Replace stored dataset and rebuild configured indexes |
291
- | `clearIndexes()` | Clear n-gram indexes |
292
- | `clearData()` | Clear stored data |
358
+ | Method | Description |
359
+ | ---------------------- | ---------------------------------------------------------- |
360
+ | `search(query)` | Search all indexed fields (including nested), deduplicated |
361
+ | `search(field, query)` | Search a specific indexed field or nested field path |
362
+ | `getOriginData()` | Get the original stored dataset |
363
+ | `data(data)` | Replace stored dataset and rebuild configured indexes |
364
+ | `clearIndexes()` | Clear n-gram indexes (including nested) |
365
+ | `clearData()` | Clear stored data |
293
366
 
294
367
  ### `FilterEngine<T>` (filter module)
295
368
 
296
- Multi-criteria AND filter with index-accelerated fast path.
369
+ Filter engine for one or more rules. It supports `nestedFields` if you need to
370
+ filter by values inside nested collections such as `["orders.status"]`.
297
371
 
298
372
  Constructor option highlights:
299
373
 
300
- | Option | Type | Description |
301
- | ------------------------ | --------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
302
- | `filterByPreviousResult` | `boolean` | When `true`, each `filter(criteria)` call filters from previous result. Defaults to `false` (each call starts from the original dataset). |
374
+ | Option | Type | Description |
375
+ | ------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
376
+ | `filterByPreviousResult` | `boolean` | When `true`, the next `filter(criteria)` call works on the previous result. By default each call starts from the original dataset. |
377
+ | `nestedFields` | `string[]` | Nested field paths in dot notation, for example `["orders.status"]`. |
303
378
 
304
379
  | Method | Description |
305
380
  | ------------------------ | -------------------------------------------------------------------------- |
306
- | `filter(criteria)` | Filter using stored dataset |
381
+ | `filter(criteria)` | Filter using stored dataset (supports nested field criteria) |
307
382
  | `filter(data, criteria)` | Filter with an explicit dataset |
308
383
  | `getOriginData()` | Get the original stored dataset |
309
384
  | `data(data)` | Replace stored dataset, rebuild configured indexes, and reset filter state |
310
385
  | `resetFilterState()` | Reset previous-result state for sequential filtering |
311
- | `clearIndexes()` | Free all index memory |
386
+ | `clearIndexes()` | Free all index memory (including nested indexes) |
312
387
  | `clearData()` | Clear stored data |
313
388
 
314
389
  ### `SortEngine<T>` (sort module)
315
390
 
316
- Sorting with pre-compiled comparators and cached sort indexes.
391
+ Sort engine with prepared indexes for faster sorting in common cases. Sort
392
+ methods return plain arrays.
317
393
 
318
394
  | Method | Description |
319
395
  | ----------------------------------- | ----------------------------------------------------- |
@@ -326,7 +402,7 @@ Sorting with pre-compiled comparators and cached sort indexes.
326
402
 
327
403
  ---
328
404
 
329
- **Note on `data` method:** Calling the `data` method automatically rebuilds configured indexes and resets any internal state (such as filter state in FilterEngine), so it is sufficient on its own without needing to call `clearIndexes` separately.
405
+ **Note on `data` method:** Calling `data` updates the stored dataset. It also rebuilds configured indexes and resets internal state when needed, so you usually do not need to call `clearIndexes` before it.
330
406
 
331
407
  ## Types
332
408
 
@@ -359,27 +435,6 @@ import type {
359
435
 
360
436
  ---
361
437
 
362
- ## Architecture
363
-
364
- ```
365
- src/
366
- types.ts — Shared type definitions
367
- indexer.ts — Hash-Map index engine (internal, O(1) lookups)
368
- search/
369
- text-search.ts — Trigram inverted index engine
370
- index.ts — Search module entry point
371
- filter/
372
- filter.ts — Multi-criteria filter engine (owns Indexer internally)
373
- index.ts — Filter module entry point
374
- sort/
375
- sorter.ts — Sort engine (TimSort + index-sort)
376
- index.ts — Sort module entry point
377
- merge/
378
- merge-engines.ts — MergeEngines unified facade
379
- index.ts — Merge module entry point
380
- index.ts — Root barrel export
381
- ```
382
-
383
438
  ## Build
384
439
 
385
440
  ```bash
@@ -0,0 +1,2 @@
1
+ var p=class u extends Error{constructor(e){super(e),this.name="TextSearchEngineError";}static missingDatasetForBuildIndex(){return new u("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}};function T(u){let e=u.toLowerCase(),t=Math.min(3,e.length),s=e.length-t+1,n=new Array(s);for(let i=0;i<s;i++)n[i]=e.substring(i,i+t);return n}function L(u,e){let t=u.get(e);if(t)return t;let s=new Set;return u.set(e,s),s}function I(u){let e=T(u);if(e.length<=12)return new Set(e);let t=new Set,s=e.length-1,n=11;for(let i=0;i<=n;i++){let o=Math.round(i*s/n);t.add(e[o]);}return t}function x(u,e,t){for(let s=0,n=e.length;s<n;s++){let i=n-s,o=Math.min(3,i);for(let r=1;r<=o;r++){let a=e.substring(s,s+r);L(u,a).add(t);}}}var y=class{constructor(){this.registeredFields=new Set;this.fieldDescriptors=new Map;this.ngramIndexes=new Map;this.normalizedFieldValues=new Map;}registerFields(e){if(e?.length)for(let t=0;t<e.length;t++)this.registerField(e[t]);}hasRegisteredFields(){return this.registeredFields.size>0}hasField(e){return this.registeredFields.has(e)}hasIndexes(){return this.ngramIndexes.size>0}clearIndexes(){this.ngramIndexes.clear(),this.normalizedFieldValues.clear();}buildIndexes(e){this.clearIndexes();for(let t of this.registeredFields)this.buildIndex(e,t);}searchAllIndexedFields(e,t,s){let n=new Set,i=[];for(let o of this.registeredFields)for(let r of this.searchIndexedField(e,o,t,s))n.has(r)||(n.add(r),i.push(r));return i}searchIndexedField(e,t,s,n){let i=this.ngramIndexes.get(t);if(!i)return [];let o=[];for(let d of n){let l=i.get(d);if(!l)return [];o.push(l);}o.sort((d,l)=>d.size-l.size);let r=o[0],a=o.length,h=[],c=this.normalizedFieldValues.get(t);for(let d of r){let l=true;for(let m=1;m<a;m++)if(!o[m].has(d)){l=false;break}if(!l)continue;let g=e[d];!g||!c?.[d]?.includes(s)||h.push(g);}return h}searchFieldLinear(e,t,s){let n=this.fieldDescriptors.get(t);if(!n)return [];let{collectionKey:i,nestedKey:o}=n,r=[];for(let a=0;a<e.length;a++){let h=e[a],c=h[i];if(!Array.isArray(c))continue;let d=false;for(let l=0;l<c.length;l++){let g=c[l][o];if(typeof g=="string"&&g.toLowerCase().includes(s)){d=true;break}}d&&r.push(h);}return r}matchesAnyField(e,t){for(let s of this.registeredFields){let n=this.fieldDescriptors.get(s);if(!n)continue;let{collectionKey:i,nestedKey:o}=n,r=e[i];if(Array.isArray(r))for(let a=0;a<r.length;a++){let h=r[a][o];if(typeof h=="string"&&h.toLowerCase().includes(t))return true}}return false}registerField(e){let t=this.createDescriptor(e);t&&(this.registeredFields.add(e),this.fieldDescriptors.set(e,t));}createDescriptor(e){let t=e.indexOf(".");return t===-1?null:{collectionKey:e.substring(0,t),nestedKey:e.substring(t+1)}}buildIndex(e,t){let s=this.fieldDescriptors.get(t);if(!s)return;let{collectionKey:n,nestedKey:i}=s,o=new Map,r=new Array(e.length);for(let a=0,h=e.length;a<h;a++){let d=e[a][n];if(!Array.isArray(d))continue;let l=[];for(let g=0;g<d.length;g++){let f=d[g][i];if(typeof f!="string")continue;let m=f.toLowerCase();l.push(m),x(o,m,a);}l.length!==0&&(r[a]=l.join(`
2
+ `));}this.ngramIndexes.set(t,o),this.normalizedFieldValues.set(t,r);}};var F=class{constructor(e={}){this.ngramIndexes=new Map;this.normalizedFieldValues=new Map;this.dataset=[];this.indexedFields=new Set;this.nestedCollection=new y;if(this.minQueryLength=e.minQueryLength??1,this.nestedCollection.registerFields(e.nestedFields),!e.data)return;this.dataset=e.data;let t=e.fields?.length,s=this.nestedCollection.hasRegisteredFields();if(t)for(let n of e.fields)this.indexedFields.add(n);(t||s)&&this.rebuildConfiguredIndexes();}rebuildConfiguredIndexes(){this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);this.nestedCollection.hasRegisteredFields()&&this.nestedCollection.buildIndexes(this.dataset);}buildIndex(e,t){let s,n;if(Array.isArray(e))s=e,n=t;else {if(!this.dataset.length)throw p.missingDatasetForBuildIndex();s=this.dataset,n=e;}this.dataset=s;let i=new Map,o=new Array(s.length);for(let r=0,a=s.length;r<a;r++){let h=s[r][n];if(typeof h!="string")continue;let c=h.toLowerCase();o[r]=c,x(i,c,r);}return this.ngramIndexes.set(n,i),this.normalizedFieldValues.set(n,o),this}search(e,t){return t===void 0?this.searchAllFields(e):this.searchField(e,t)}normalizeQuery(e){return e.trim().toLowerCase()}searchAllFields(e){let t=this.normalizeQuery(e);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!this.ngramIndexes.size&&!this.nestedCollection.hasIndexes())return this.searchAllFieldsLinear(t);let s=I(t);if(!s.size)return [];let n=new Set,i=[];for(let o of this.ngramIndexes.keys())for(let r of this.searchFieldWithPreparedQuery(o,t,s))n.has(r)||(n.add(r),i.push(r));for(let o of this.nestedCollection.searchAllIndexedFields(this.dataset,t,s))n.has(o)||(n.add(o),i.push(o));return i}searchField(e,t){let s=this.normalizeQuery(t);if(!s)return this.dataset;if(s.length<this.minQueryLength)return this.dataset;if(this.nestedCollection.hasField(e)){let i=I(s);return i.size?this.nestedCollection.hasIndexes()?this.nestedCollection.searchIndexedField(this.dataset,e,s,i):this.nestedCollection.searchFieldLinear(this.dataset,e,s):[]}if(!this.ngramIndexes.size)return this.searchFieldLinear(e,s);let n=I(s);return n.size?this.searchFieldWithPreparedQuery(e,s,n):[]}searchFieldWithPreparedQuery(e,t,s){let n=this.ngramIndexes.get(e);if(!n)return [];let i=[];for(let c of s){let d=n.get(c);if(!d)return [];i.push(d);}i.sort((c,d)=>c.size-d.size);let o=i[0],r=i.length,a=[],h=this.normalizedFieldValues.get(e);for(let c of o){let d=true;for(let f=1;f<r;f++)if(!i[f].has(c)){d=false;break}if(!d)continue;let l=this.dataset[c];if(!l)continue;h?.[c]?.includes(t)&&a.push(l);}return a}searchAllFieldsLinear(e){if(!this.dataset.length)return [];let t=[];for(let s=0;s<this.dataset.length;s++){let n=this.dataset[s],i=false;for(let o in n){let r=n[o];if(typeof r=="string"&&r.toLowerCase().includes(e)){i=true;break}}i||(i=this.nestedCollection.matchesAnyField(n,e)),i&&t.push(n);}return t}searchFieldLinear(e,t){if(!this.dataset.length)return [];if(this.nestedCollection.hasField(e))return this.nestedCollection.searchFieldLinear(this.dataset,e,t);let s=[];for(let n=0;n<this.dataset.length;n++){let i=this.dataset[n][e];typeof i=="string"&&i.toLowerCase().includes(t)&&s.push(this.dataset[n]);}return s}clearIndexes(){return this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes(),this}getOriginData(){return this.dataset}data(e){return this.dataset=e,this.rebuildConfiguredIndexes(),this}clearData(){return this.dataset=[],this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes(),this}};export{F as a};
@@ -0,0 +1 @@
1
+ var C=class{constructor(){this.indexes=new Map;}buildIndex(e,t){let n=new Map;for(let s=0,r=e.length;s<r;s++){let i=e[s],l=i[t];if(l==null)continue;let a=n.get(l);a?a.push(i):n.set(l,[i]);}this.indexes.set(t,n);}getByValue(e,t){let n=this.indexes.get(e);return n?n.get(t)??[]:[]}getByValues(e,t){let n=this.indexes.get(e);if(!n)return [];if(t.length===1)return n.get(t[0])??[];let s=new Set,r=[];for(let i=0;i<t.length;i++){let l=n.get(t[i]);if(l!==void 0)for(let a=0;a<l.length;a++){let u=l[a];s.has(u)||(s.add(u),r.push(u));}}return r}hasIndex(e){return this.indexes.has(e)}clear(){this.indexes.clear();}getIndexMap(e){return this.indexes.get(e)}};var v=class{constructor(e){this.callbacks=e;}create(e){let t=e;return Object.defineProperty(t,"filter",{value:(n,s)=>s===void 0?this.callbacks.filter(e,n):this.callbacks.filter(n,s),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearIndexes",{value:()=>this.callbacks.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"data",{value:n=>this.callbacks.data(n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"getOriginData",{value:()=>this.callbacks.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearData",{value:()=>this.callbacks.clearData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"resetFilterState",{value:()=>this.callbacks.resetFilterState(),enumerable:false,configurable:true,writable:true}),t}};var T=class p extends Error{constructor(e){super(e),this.name="FilterEngineError";}static missingDatasetForBuildIndex(){return new p("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}static missingDatasetForFilter(){return new p("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call filter(data, criteria).")}};var x=Symbol("FilterEngine.execute");var F=class{constructor(){this.registeredFields=new Set;this.fieldDescriptors=new Map;this.indexes=new Map;}registerFields(e){if(e?.length)for(let t=0;t<e.length;t++)this.registerField(e[t]);}hasRegisteredFields(){return this.registeredFields.size>0}hasField(e){return this.registeredFields.has(e)}clearIndexes(){this.indexes.clear();}buildIndexes(e){this.indexes.clear();for(let t of this.registeredFields)this.buildIndex(e,t);}filter(e,t,n){if(e.length===0||t.length===0)return e;let s=[],r=[];for(let a=0;a<t.length;a++){let u=t[a];if(this.indexes.has(u.field)){s.push(u);continue}r.push(u);}let i=e,l=e===n?null:new Set(e);if(s.length>0&&(i=this.filterByIndexes(s,l),i.length===0))return i;for(let a=0;a<r.length;a++)if(i=this.filterLinearly(i,r[a]),i.length===0)return i;return i}registerField(e){let t=this.createDescriptor(e);t&&(this.registeredFields.add(e),this.fieldDescriptors.set(e,t));}createDescriptor(e){let t=e.indexOf(".");return t===-1?null:{collectionKey:e.substring(0,t),nestedKey:e.substring(t+1)}}buildIndex(e,t){let n=this.fieldDescriptors.get(t);if(!n)return;let{collectionKey:s,nestedKey:r}=n,i=new Map;for(let l=0,a=e.length;l<a;l++){let u=e[l],h=u[s];if(Array.isArray(h))for(let c=0;c<h.length;c++){let o=h[c][r];if(o==null)continue;let d=i.get(o);if(d){d[d.length-1]!==u&&d.push(u);continue}i.set(o,[u]);}}this.indexes.set(t,i);}filterByIndexes(e,t){if(e.length===1){let i=this.indexes.get(e[0].field);if(!i)return [];let l=this.getItemsByValues(i,e[0].values);if(l.length===0)return [];if(t===null)return l;let a=[];for(let u=0;u<l.length;u++){let h=l[u];t.has(h)&&a.push(h);}return a}let n=e.map(i=>({criterion:i,size:this.estimateIndexSize(i)})).sort((i,l)=>i.size-l.size),s=t,r=[];for(let i=0;i<n.length;i++){let{criterion:l}=n[i],a=this.indexes.get(l.field);if(!a)return [];let u=this.getItemsByValues(a,l.values);if(u.length===0)return [];if(s===null)r=u;else {r=[];for(let h=0;h<u.length;h++){let c=u[h];s.has(c)&&r.push(c);}}if(r.length===0)return [];s=new Set(r);}return r}getItemsByValues(e,t){if(t.length===1)return e.get(t[0])??[];let n=new Set,s=[];for(let r=0;r<t.length;r++){let i=e.get(t[r]);if(i)for(let l=0;l<i.length;l++){let a=i[l];n.has(a)||(n.add(a),s.push(a));}}return s}estimateIndexSize(e){let t=this.indexes.get(e.field);return t?e.values.reduce((n,s)=>{let r=t.get(s);return r?n+r.length:n},0):1/0}filterLinearly(e,t){let n=this.fieldDescriptors.get(t.field);if(!n)return e;let{collectionKey:s,nestedKey:r}=n,i=new Set(t.values),l=[];for(let a=0;a<e.length;a++){let u=e[a],h=u[s];if(!Array.isArray(h))continue;let c=false;for(let o=0;o<h.length;o++)if(i.has(h[o][r])){c=true;break}c&&l.push(u);}return l}};var y=class{constructor(e={}){this.dataset=[];this.indexedFields=new Set;this.nestedCollection=new F;this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;this.chainBuilder=new v({filter:(e,t)=>t===void 0?this.filter(e):this.filter(e,t),getOriginData:()=>this.getOriginData(),data:e=>this.data(e),clearIndexes:()=>this.clearIndexes(),clearData:()=>this.clearData(),resetFilterState:()=>this.resetFilterState()});if(this.indexer=new C,this.filterByPreviousResult=e.filterByPreviousResult??false,this.nestedCollection.registerFields(e.nestedFields),!e.data)return;this.dataset=e.data;let t=e.fields?.length,n=this.nestedCollection.hasRegisteredFields();if(t)for(let s of e.fields)this.indexedFields.add(s);(t||n)&&this.rebuildConfiguredIndexes();}rebuildConfiguredIndexes(){this.indexer.clear(),this.nestedCollection.clearIndexes();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);this.nestedCollection.hasRegisteredFields()&&this.nestedCollection.buildIndexes(this.dataset);}buildIndex(e,t){if(!Array.isArray(e)){if(!this.dataset.length)throw T.missingDatasetForBuildIndex();return this.indexer.buildIndex(this.dataset,e),this}return this.dataset=e,this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this.indexer.buildIndex(e,t),this}clearIndexes(){return this.indexer.clear(),this.nestedCollection.clearIndexes(),this}resetFilterState(){return this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this}clearData(){return this.dataset=[],this.indexer.clear(),this.nestedCollection.clearIndexes(),this.resetFilterState(),this}data(e){return this.dataset=e,this.resetFilterState(),this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}filter(e,t){return t===void 0?this.withChain(this[x](e)):this.withChain(this[x](e,t))}[x](e,t){let n=t===void 0,s,r,i;if(n){if(!this.dataset.length)throw T.missingDatasetForFilter();if(r=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.dataset){let o=this.hasCriteriaAdditions(this.previousCriteria,r),d=this.hasCriteriaRemovals(this.previousCriteria,r);if(!o&&!d)return this.previousResult;o&&!d?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,r)):(s=this.dataset,i=r);}else s=this.dataset,i=r;}else if(r=t,s=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===s){let o=this.hasCriteriaAdditions(this.previousCriteria,r),d=this.hasCriteriaRemovals(this.previousCriteria,r);if(!o&&!d)return this.previousResult;o&&!d?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,r)):i=r;}else i=r;if(r.length===0)return this.filterByPreviousResult&&(this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null),n?this.dataset:s;n&&!i&&(i=r);let l=[],a=[];for(let o=0;o<i.length;o++){let d=i[o];this.nestedCollection.hasField(d.field)?l.push(d):a.push(d);}if(l.length>0&&(s=this.nestedCollection.filter(s,l,this.dataset),s.length===0)){let o=[];return this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),o}if(a.length===0)return this.filterByPreviousResult&&(this.previousResult=s,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),s;let{indexedCriteria:u,linearCriteria:h}=a.reduce((o,d)=>(this.indexer.hasIndex(d.field)?o.indexedCriteria.push(d):o.linearCriteria.push(d),o),{indexedCriteria:[],linearCriteria:[]}),c;if(u.length>0&&h.length===0)return c=this.filterViaIndex(u,s),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c;if(u.length>0&&h.length>0){let o=this.filterViaIndex(u,s);return c=this.linearFilter(o,h),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c}return c=this.linearFilter(s,a),this.filterByPreviousResult&&(this.previousResult=c,this.previousCriteria=this.cloneCriteria(r),this.previousBaseData=n?this.dataset:e),c}withChain(e){return this.chainBuilder.create(e)}cloneCriteria(e){return e.map(({field:t,values:n})=>({field:t,values:[...n]}))}hasCriteriaAdditions(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=new Map(t.map(({field:r,values:i})=>[r,new Set(i)]));for(let[r,i]of s){let l=n.get(r);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}hasCriteriaRemovals(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=new Map(t.map(({field:r,values:i})=>[r,new Set(i)]));for(let[r,i]of n){let l=s.get(r);if(!l)return true;for(let a of i)if(!l.has(a))return true}return false}getAddedCriteria(e,t){let n=new Map(e.map(({field:r,values:i})=>[r,new Set(i)])),s=[];for(let{field:r,values:i}of t){let l=n.get(r);if(!l){s.push({field:r,values:[...i]});continue}let a=i.filter(u=>!l.has(u));a.length>0&&s.push({field:r,values:a});}return s}linearFilter(e,t){let n=new Map(t.map(({field:i,values:l})=>[i,new Set(l)])),s=t.map(({field:i})=>i),r=[];for(let i=0;i<e.length;i++){let l=e[i],a=true;for(let u=0;u<s.length;u++){let h=s[u];if(!n.get(h).has(l[h])){a=false;break}}a&&r.push(l);}return r}filterViaIndex(e,t){let s=t!==this.dataset?new Set(t):null;if(e.length===1){let o=this.indexer.getByValues(e[0].field,e[0].values);if(!s)return o.slice();let d=[];for(let f=0;f<o.length;f++){let g=o[f];s.has(g)&&d.push(g);}return d}let r=e.map(o=>({criterion:o,size:this.estimateIndexSize(o)})).sort((o,d)=>o.size-d.size),{field:i,values:l}=r[0].criterion,a=this.indexer.getByValues(i,l);if(a.length===0)return [];let u=new Map(r.slice(1).map(({criterion:{field:o,values:d}})=>[o,new Set(d)])),h=Array.from(u.keys()),c=[];for(let o=0;o<a.length;o++){let d=a[o],f=true;for(let g=0;g<h.length;g++){let m=h[g];if(!u.get(m).has(d[m])){f=false;break}}f&&s&&!s.has(d)&&(f=false),f&&c.push(d);}return c}estimateIndexSize(e){let t=this.indexer.getIndexMap(e.field);return t?e.values.reduce((n,s)=>{let r=t.get(s);return r?n+r.length:n},0):1/0}};export{x as a,y as b};
@@ -0,0 +1 @@
1
+ var u=class f extends Error{constructor(r){super(r),this.name="SortEngineError";}static missingDatasetForBuildIndex(){return new f("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).")}static missingDatasetForSort(){return new f("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call sort(data, descriptors).")}};var g=class{constructor(r={}){this.cache=new Map;this.dataset=[];this.indexedFields=new Set;if(r.data&&(this.dataset=r.data,r.fields?.length)){for(let a of r.fields)this.indexedFields.add(a);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.cache.clear();for(let r of this.indexedFields)this.buildIndex(this.dataset,r);}buildIndex(r,a){let d,t;if(Array.isArray(r))d=r,t=a;else {if(!this.dataset.length)throw u.missingDatasetForBuildIndex();d=this.dataset,t=r;}this.dataset=d;let e=d.length,s=new Uint32Array(e),n=new Array(e);for(let o=0;o<e;o++)s[o]=o,n[o]=d[o][t];if(typeof n[0]=="number"){let o=new Float64Array(e);for(let l=0;l<e;l++)o[l]=n[l];s.sort((l,c)=>o[l]-o[c]);}else s.sort((o,l)=>{let c=n[o],h=n[l];return c<h?-1:c>h?1:0});return this.cache.set(t,{indexes:s,dataRef:d,itemCount:e}),this}clearIndexes(){return this.cache.clear(),this}clearData(){return this.dataset=[],this.cache.clear(),this}data(r){return this.dataset=r,this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}sort(r,a,d=false){let t,e,s=a===void 0;if(a===void 0){if(!this.dataset.length)throw u.missingDatasetForSort();t=this.dataset,e=r;}else t=r,e=a;if(e.length===0||t.length===0)return t;if(e.length===1){let{field:o,direction:l}=e[0],c=this.cache.get(o);if(s&&c?.dataRef===t&&c.itemCount===t.length)return this.reconstructFromIndex(t,c.indexes,l)}let n=d?t:t.slice();if(e.length===1&&t.length>0&&typeof t[0][e[0].field]=="number")return this.radixSortNumeric(n,e[0].field,e[0].direction);let i=this.buildComparator(e);return n.sort(i),n}reconstructFromIndex(r,a,d){let t=r.length,e=new Array(t);if(d==="asc")for(let s=0;s<t;s++)e[s]=r[a[s]];else for(let s=0;s<t;s++)e[s]=r[a[t-1-s]];return e}buildComparator(r){let a=r.map(({field:e})=>e),d=r.map(({direction:e})=>e==="asc"?1:-1),t=a.length;return (e,s)=>{for(let n=0;n<t;n++){let i=e[a[n]],o=s[a[n]];if(i<o)return -d[n];if(i>o)return d[n]}return 0}}radixSortNumeric(r,a,d){let t=r.length,e=new Float64Array(t);for(let i=0;i<t;i++)e[i]=r[i][a];let s=new Uint32Array(t);for(let i=0;i<t;i++)s[i]=i;s.sort((i,o)=>e[i]-e[o]);let n=new Array(t);if(d==="asc")for(let i=0;i<t;i++)n[i]=r[s[i]];else for(let i=0;i<t;i++)n[t-1-i]=r[s[i]];return n}};export{g as a};
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-4PTQMQ73.mjs';import {b,a as a$2}from'./chunk-5B7Y4C22.mjs';import {a as a$1}from'./chunk-IYELLKOW.mjs';var g=class{constructor(e){this.callbacks=e;}create(e){let t=e;return Object.defineProperty(t,"search",{value:(r,n)=>n===void 0?this.callbacks.search(r):this.callbacks.search(r,n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"sort",{value:(r,n,i)=>n===void 0?this.callbacks.sort(e,r,i):this.callbacks.sort(r,n,i),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"filter",{value:(r,n)=>n===void 0?this.callbacks.filter(e,r):this.callbacks.filter(r,n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearIndexes",{value:r=>(this.callbacks.clearIndexes(r),this.create(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearData",{value:r=>(this.callbacks.clearData(r),this.create(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"data",{value:r=>this.callbacks.data(r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"getOriginData",{value:()=>this.callbacks.getOriginData(),enumerable:false,configurable:true,writable:true}),t}};var x=o=>o===a?"search":o===a$1?"sort":o===b?"filter":null,y=(o,e,t)=>{if(o===a){let r=new a({data:e,...t});return {moduleName:"search",executeSearch:(n,i)=>i===void 0?r.search(n):r.search(n,i),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}if(o===a$1){let r=new a$1({data:e,...t});return {moduleName:"sort",executeSort:(n,i,s)=>i===void 0?r.sort(n):r.sort(n,i,s),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}if(o===b){let r=new b({data:e,...t});return {moduleName:"filter",executeFilter:(n,i)=>i===void 0?r[a$2](n):r[a$2](n,i),clearIndexes:()=>r.clearIndexes(),clearData:()=>r.clearData(),data:n=>r.data(n),getOriginData:()=>r.getOriginData()}}return null};var N={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"},l=class o extends Error{constructor(e){super(e),this.name="MergeEnginesError";}static unavailableMethod(e){return new o(`MergeEngines: Method "${e}" is not available. Add module with method "${e}" to the \`imports\` array.`)}static unavailableEngine(e){let t=N[e];return new o(`MergeEngines: ${t} is not available. Add ${t} to the \`imports\` array.`)}static unavailableGetOriginData(){return new o("MergeEngines: getOriginData is not available. Add TextSearchEngine, SortEngine, or FilterEngine to the `imports` array.")}};var m=class{constructor(e){this.chainBuilder=new g({search:(e,t)=>t===void 0?this.search(e):this.search(e,t),sort:(e,t,r)=>t===void 0?this.sort(e):this.sort(e,t,r),filter:(e,t)=>t===void 0?this.filter(e):this.filter(e,t),getOriginData:()=>this.getOriginData(),data:e=>this.data(e),clearIndexes:e=>this.clearIndexes(e),clearData:e=>this.clearData(e)});let{imports:t,data:r,...n}=e,i=new Set(t),s=null,h=null,M=null,f={},E={},C={},b=null;for(let p of i){let D=x(p);if(!D)continue;let S=this.getModuleInitOptions(D,p.name,n),a=y(p,r,S);a&&(a.moduleName==="search"&&!s&&(s=a),a.moduleName==="sort"&&!h&&(h=a),a.moduleName==="filter"&&!M&&(M=a),f[a.moduleName]=()=>a.clearIndexes(),E[a.moduleName]=()=>a.clearData(),C[a.moduleName]=w=>a.data(w),b??(b=()=>a.getOriginData()));}this.searchModule=s,this.sortModule=h,this.filterModule=M,this.clearIndexMethods=f,this.clearDataMethods=E,this.setDataMethods=C,this.getOriginDataMethod=b;}getModuleInitOptions(e,t,r){let n={};for(let i of [e,t]){let s=r[i];this.isRecord(s)&&Object.assign(n,s);}return n}isRecord(e){return typeof e=="object"&&e!==null}search(e,t){if(!this.searchModule)throw l.unavailableEngine("search");return t===void 0?this.withChain(this.searchModule.executeSearch(e)):this.withChain(this.searchModule.executeSearch(e,t))}sort(e,t,r){if(!this.sortModule)throw l.unavailableEngine("sort");return t===void 0?this.withChain(this.sortModule.executeSort(e)):this.withChain(this.sortModule.executeSort(e,t,r))}filter(e,t){if(!this.filterModule)throw l.unavailableEngine("filter");return t===void 0?this.withChain(this.filterModule.executeFilter(e)):this.withChain(this.filterModule.executeFilter(e,t))}withChain(e){return this.chainBuilder.create(e)}getOriginData(){if(this.getOriginDataMethod)return this.getOriginDataMethod();throw l.unavailableGetOriginData()}clearIndexes(e){let t=this.clearIndexMethods[e];if(t)return t(),this;throw l.unavailableEngine(e)}data(e){let t=["search","sort","filter"];for(let r of t){let n=this.setDataMethods[r];n&&n(e);}return this}clearData(e){let t=this.clearDataMethods[e];if(t)return t(),this;throw l.unavailableEngine(e)}};export{m as a};
@@ -1,16 +1,6 @@
1
- import { C as CollectionItem, F as FilterCriterion } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey } from '../types-D24zQWME.mjs';
1
+ import { C as CollectionItem, F as FilterCriterion } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
3
3
 
4
- /**
5
- * FilterEngine class for filtering collections by multiple criteria,
6
- * supporting indexed lookups and linear scans for large datasets.
7
- */
8
-
9
- interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
10
- data?: T[];
11
- fields?: (keyof T & string)[];
12
- filterByPreviousResult?: boolean;
13
- }
14
4
  interface FilterEngineChain<T extends CollectionItem> {
15
5
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
16
6
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
@@ -20,14 +10,30 @@ interface FilterEngineChain<T extends CollectionItem> {
20
10
  clearData(): FilterEngine<T>;
21
11
  resetFilterState(): FilterEngine<T>;
22
12
  }
13
+ interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
14
+ data?: T[];
15
+ fields?: (keyof T & string)[];
16
+ nestedFields?: string[];
17
+ filterByPreviousResult?: boolean;
18
+ }
19
+
20
+ declare const FILTER_ENGINE_EXECUTE: unique symbol;
21
+
22
+ /**
23
+ * FilterEngine class for filtering collections by multiple criteria,
24
+ * supporting indexed lookups and linear scans for large datasets.
25
+ */
26
+
23
27
  declare class FilterEngine<T extends CollectionItem> {
24
28
  private indexer;
25
29
  private readonly filterByPreviousResult;
26
30
  private dataset;
27
31
  private readonly indexedFields;
32
+ private readonly nestedCollection;
28
33
  private previousResult;
29
34
  private previousCriteria;
30
35
  private previousBaseData;
36
+ private readonly chainBuilder;
31
37
  /**
32
38
  * Creates a new FilterEngine with optional data and fields to index.
33
39
  */
@@ -47,6 +53,8 @@ declare class FilterEngine<T extends CollectionItem> {
47
53
  */
48
54
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
49
55
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
56
+ [FILTER_ENGINE_EXECUTE](criteria: FilterCriterion<T>[]): T[];
57
+ [FILTER_ENGINE_EXECUTE](data: T[], criteria: FilterCriterion<T>[]): T[];
50
58
  private withChain;
51
59
  private cloneCriteria;
52
60
  /**
@@ -75,4 +83,4 @@ declare class FilterEngine<T extends CollectionItem> {
75
83
  private estimateIndexSize;
76
84
  }
77
85
 
78
- export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineOptions };
86
+ export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineChain, type FilterEngineOptions };
@@ -1 +1 @@
1
- export{a as FilterEngine}from'../chunk-VJ4JZMHD.mjs';
1
+ export{b as FilterEngine}from'../chunk-5B7Y4C22.mjs';
package/dist/index.d.mts CHANGED
@@ -1,5 +1,5 @@
1
1
  export { TextSearchEngine } from './search/index.mjs';
2
2
  export { FilterEngine } from './filter/index.mjs';
3
3
  export { SortEngine } from './sort/index.mjs';
4
- export { EngineApi, EngineConstructor, MergeEngines, MergeEnginesOptions } from './merge/index.mjs';
5
- export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-D24zQWME.mjs';
4
+ export { EngineApi, EngineConstructor, MergeEngines, MergeEnginesChain, MergeEnginesOptions, MergeModuleName } from './merge/index.mjs';
5
+ export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-DuQWbqOC.mjs';
package/dist/index.mjs CHANGED
@@ -1 +1 @@
1
- export{a as TextSearchEngine}from'./chunk-574GKMWM.mjs';export{a as FilterEngine}from'./chunk-VJ4JZMHD.mjs';export{a as SortEngine}from'./chunk-FZVBLKAD.mjs';export{a as MergeEngines}from'./chunk-B5H435OG.mjs';
1
+ export{a as MergeEngines}from'./chunk-LKNVBEFD.mjs';export{a as TextSearchEngine}from'./chunk-4PTQMQ73.mjs';export{b as FilterEngine}from'./chunk-5B7Y4C22.mjs';export{a as SortEngine}from'./chunk-IYELLKOW.mjs';
@@ -1,12 +1,19 @@
1
- import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey, a as SortDirection } from '../types-D24zQWME.mjs';
3
-
4
- /**
5
- * MergeEngines class that provides a unified interface for text search,
6
- * sorting, and filtering operations on collections.
7
- */
1
+ import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey, a as SortDirection } from '../types-DuQWbqOC.mjs';
8
3
 
9
4
  type MergeModuleName = "search" | "sort" | "filter";
5
+ interface MergeEnginesChain<T extends CollectionItem> {
6
+ search(query: string): T[] & MergeEnginesChain<T>;
7
+ search(field: (keyof T & string) | (string & {}), query: string): T[] & MergeEnginesChain<T>;
8
+ sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
9
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
10
+ filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
11
+ filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
12
+ getOriginData(): T[];
13
+ data(data: T[]): MergeEngines<T>;
14
+ clearIndexes(module: MergeModuleName): T[] & MergeEnginesChain<T>;
15
+ clearData(module: MergeModuleName): T[] & MergeEnginesChain<T>;
16
+ }
10
17
  interface EngineConstructor {
11
18
  new (options: Record<string, unknown>): object;
12
19
  prototype: object;
@@ -20,49 +27,33 @@ interface MergeEnginesOptions<T extends CollectionItem> {
20
27
  data: T[];
21
28
  [key: string]: unknown;
22
29
  }
23
- interface MergeEnginesChain<T extends CollectionItem> {
24
- search(query: string): T[] & MergeEnginesChain<T>;
25
- search(field: keyof T & string, query: string): T[] & MergeEnginesChain<T>;
26
- sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
27
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
28
- filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
29
- filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
30
- getOriginData(): T[];
31
- data(data: T[]): MergeEngines<T>;
32
- clearIndexes(module: MergeModuleName): T[] & MergeEnginesChain<T>;
33
- clearData(module: MergeModuleName): T[] & MergeEnginesChain<T>;
34
- }
30
+
31
+ /**
32
+ * MergeEngines class that provides a unified interface for text search,
33
+ * sorting, and filtering operations on collections.
34
+ */
35
+
35
36
  declare class MergeEngines<T extends CollectionItem> {
36
- private readonly engine;
37
+ private readonly searchModule;
38
+ private readonly sortModule;
39
+ private readonly filterModule;
37
40
  private readonly clearIndexMethods;
38
41
  private readonly clearDataMethods;
39
42
  private readonly setDataMethods;
40
43
  private readonly getOriginDataMethod;
44
+ private readonly chainBuilder;
41
45
  /**
42
46
  * Creates a new MergeEngines instance with the given options.
43
47
  * Collects all modules from imports.
44
48
  */
45
49
  constructor(options: MergeEnginesOptions<T>);
46
- private getModuleName;
47
50
  /**
48
51
  * Gets the initialization options for a module.
49
52
  */
50
53
  private getModuleInitOptions;
51
- /**
52
- * Gets the method names from a prototype.
53
- */
54
- private getMethodNames;
55
- /**
56
- * Checks if an object has a specific method.
57
- */
58
- private hasMethod;
59
54
  private isRecord;
60
- /**
61
- * Calls a method on the engine.
62
- */
63
- private callEngineMethod;
64
55
  search(query: string): T[] & MergeEnginesChain<T>;
65
- search(field: keyof T & string, query: string): T[] & MergeEnginesChain<T>;
56
+ search(field: (keyof T & string) | (string & {}), query: string): T[] & MergeEnginesChain<T>;
66
57
  sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
67
58
  sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & MergeEnginesChain<T>;
68
59
  filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
@@ -74,4 +65,4 @@ declare class MergeEngines<T extends CollectionItem> {
74
65
  clearData(module: MergeModuleName): this;
75
66
  }
76
67
 
77
- export { CollectionItem, type EngineApi, type EngineConstructor, FilterCriterion, MergeEngines, type MergeEnginesOptions, SortDescriptor };
68
+ export { CollectionItem, type EngineApi, type EngineConstructor, FilterCriterion, MergeEngines, type MergeEnginesChain, type MergeEnginesOptions, type MergeModuleName, SortDescriptor };
@@ -1 +1 @@
1
- export{a as MergeEngines}from'../chunk-B5H435OG.mjs';
1
+ export{a as MergeEngines}from'../chunk-LKNVBEFD.mjs';import'../chunk-4PTQMQ73.mjs';import'../chunk-5B7Y4C22.mjs';import'../chunk-IYELLKOW.mjs';
@@ -1,28 +1,24 @@
1
- import { C as CollectionItem } from '../types-D24zQWME.mjs';
2
- export { I as IndexableKey } from '../types-D24zQWME.mjs';
3
-
4
- /**
5
- * TextSearchEngine class for performing fast substring search on string fields
6
- * using n-gram indexing and intersection.
7
- */
1
+ import { C as CollectionItem } from '../types-DuQWbqOC.mjs';
2
+ export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
8
3
 
9
4
  interface TextSearchEngineOptions<T extends CollectionItem = CollectionItem> {
10
5
  data?: T[];
11
6
  fields?: (keyof T & string)[];
7
+ nestedFields?: string[];
12
8
  minQueryLength?: number;
13
9
  }
14
- interface TextSearchEngineChain<T extends CollectionItem> {
15
- search(query: string): T[] & TextSearchEngineChain<T>;
16
- search(field: keyof T & string, query: string): T[] & TextSearchEngineChain<T>;
17
- getOriginData(): T[];
18
- data(data: T[]): TextSearchEngine<T>;
19
- clearIndexes(): TextSearchEngine<T>;
20
- clearData(): TextSearchEngine<T>;
21
- }
10
+
11
+ /**
12
+ * TextSearchEngine class for performing fast substring search on string fields
13
+ * using n-gram indexing and intersection.
14
+ */
15
+
22
16
  declare class TextSearchEngine<T extends CollectionItem> {
23
17
  private ngramIndexes;
18
+ private normalizedFieldValues;
24
19
  private dataset;
25
20
  private readonly indexedFields;
21
+ private readonly nestedCollection;
26
22
  private readonly minQueryLength;
27
23
  /**
28
24
  * Creates a new TextSearchEngine with optional data and fields to index.
@@ -33,8 +29,8 @@ declare class TextSearchEngine<T extends CollectionItem> {
33
29
  * Builds an n-gram index for the given field.
34
30
  */
35
31
  private buildIndex;
36
- search(query: string): T[] & TextSearchEngineChain<T>;
37
- search(field: keyof T & string, query: string): T[] & TextSearchEngineChain<T>;
32
+ search(query: string): T[];
33
+ search(field: (keyof T & string) | (string & {}), query: string): T[];
38
34
  private normalizeQuery;
39
35
  /**
40
36
  * Searches all indexed fields.
@@ -56,7 +52,6 @@ declare class TextSearchEngine<T extends CollectionItem> {
56
52
  * Searches a specific field linearly without index.
57
53
  */
58
54
  private searchFieldLinear;
59
- private withChain;
60
55
  clearIndexes(): this;
61
56
  getOriginData(): T[];
62
57
  data(data: T[]): this;
@@ -1 +1 @@
1
- export{a as TextSearchEngine}from'../chunk-574GKMWM.mjs';
1
+ export{a as TextSearchEngine}from'../chunk-4PTQMQ73.mjs';
@@ -1,23 +1,16 @@
1
- import { C as CollectionItem, S as SortDescriptor } from '../types-D24zQWME.mjs';
2
- export { a as SortDirection } from '../types-D24zQWME.mjs';
1
+ import { C as CollectionItem, S as SortDescriptor } from '../types-DuQWbqOC.mjs';
2
+ export { a as SortDirection } from '../types-DuQWbqOC.mjs';
3
+
4
+ interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
5
+ data?: T[];
6
+ fields?: (keyof T & string)[];
7
+ }
3
8
 
4
9
  /**
5
10
  * SortEngine class for sorting large datasets using pre-built indexes
6
11
  * for O(n) performance or fallback to in-place sorting.
7
12
  */
8
13
 
9
- interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
10
- data?: T[];
11
- fields?: (keyof T & string)[];
12
- }
13
- interface SortEngineChain<T extends CollectionItem> {
14
- sort(descriptors: SortDescriptor<T>[]): T[] & SortEngineChain<T>;
15
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & SortEngineChain<T>;
16
- getOriginData(): T[];
17
- data(data: T[]): SortEngine<T>;
18
- clearIndexes(): SortEngine<T>;
19
- clearData(): SortEngine<T>;
20
- }
21
14
  declare class SortEngine<T extends CollectionItem> {
22
15
  private cache;
23
16
  private dataset;
@@ -41,17 +34,12 @@ declare class SortEngine<T extends CollectionItem> {
41
34
  /**
42
35
  * Sorts the data based on the given descriptors.
43
36
  */
44
- sort(descriptors: SortDescriptor<T>[]): T[] & SortEngineChain<T>;
45
- sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[] & SortEngineChain<T>;
46
- private withChain;
37
+ sort(descriptors: SortDescriptor<T>[]): T[];
38
+ sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
47
39
  /**
48
40
  * Reconstructs the sorted array from the cached index.
49
41
  */
50
42
  private reconstructFromIndex;
51
- /**
52
- * Checks if the field snapshot is still valid.
53
- */
54
- private isFieldSnapshotValid;
55
43
  /**
56
44
  * Builds a comparator function for sorting.
57
45
  */
@@ -1 +1 @@
1
- export{a as SortEngine}from'../chunk-FZVBLKAD.mjs';
1
+ export{a as SortEngine}from'../chunk-IYELLKOW.mjs';
@@ -3,7 +3,7 @@ type IndexableKey<T> = {
3
3
  [K in keyof T]: T[K] extends string | number ? K : never;
4
4
  }[keyof T];
5
5
  interface FilterCriterion<T extends CollectionItem> {
6
- field: keyof T & string;
6
+ field: (keyof T & string) | (string & {});
7
7
  values: any[];
8
8
  }
9
9
  type SortDirection = "asc" | "desc";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devisfuture/mega-collection",
3
- "version": "1.3.1",
3
+ "version": "2.0.3",
4
4
  "description": "High-performance search, filter & sort engine for 100K+ item collections in JavaScript/TypeScript",
5
5
  "exports": {
6
6
  ".": {
@@ -1 +0,0 @@
1
- var y=3,T=12;function I(l){let e=l.toLowerCase(),n=Math.min(y,e.length),t=e.length-n+1,r=new Array(t);for(let i=0;i<t;i++)r[i]=e.substring(i,i+n);return r}function m(l){let e=I(l);if(e.length<=T)return new Set(e);let n=new Set,t=e.length-1,r=T-1;for(let i=0;i<=r;i++){let s=Math.round(i*t/r);n.add(e[s]);}return n}function b(l,e){let n=l.get(e);if(n)return n;let t=new Set;return l.set(e,t),t}var x=class{constructor(e={}){this.ngramIndexes=new Map;this.dataset=[];this.indexedFields=new Set;if(this.minQueryLength=e.minQueryLength??1,!!e.data&&(this.dataset=e.data,e.fields?.length)){for(let n of e.fields)this.indexedFields.add(n);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.ngramIndexes.clear();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);}buildIndex(e,n){let t,r;if(Array.isArray(e))t=e,r=n;else {if(!this.dataset.length)throw new Error("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");t=this.dataset,r=e;}this.dataset=t;let i=new Map;for(let s=0,c=t.length;s<c;s++){let o=t[s][r];if(typeof o!="string")continue;let h=o.toLowerCase();for(let a=0,d=h.length;a<d;a++){let g=d-a,u=Math.min(y,g);for(let f=1;f<=u;f++){let p=h.substring(a,a+f);b(i,p).add(s);}}}return this.ngramIndexes.set(r,i),this}search(e,n){return n===void 0?this.withChain(this.searchAllFields(e)):this.withChain(this.searchField(e,n))}normalizeQuery(e){return e.trim().toLowerCase()}searchAllFields(e){let n=[...this.ngramIndexes.keys()],t=this.normalizeQuery(e);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!n.length)return this.searchAllFieldsLinear(t);let r=m(t);if(!r.size)return [];let i=new Set,s=[];for(let c of n)for(let o of this.searchFieldWithPreparedQuery(c,t,r))i.has(o)||(i.add(o),s.push(o));return s}searchField(e,n){let t=this.normalizeQuery(n);if(!t)return this.dataset;if(t.length<this.minQueryLength)return this.dataset;if(!this.ngramIndexes.size)return this.searchFieldLinear(e,t);let r=m(t);return r.size?this.searchFieldWithPreparedQuery(e,t,r):[]}searchFieldWithPreparedQuery(e,n,t){let r=this.ngramIndexes.get(e);if(!r)return [];let i=[];for(let h of t){let a=r.get(h);if(!a)return [];i.push(a);}i.sort((h,a)=>h.size-a.size);let s=i[0],c=i.length,o=[];for(let h of s){let a=true;for(let u=1;u<c;u++)if(!i[u].has(h)){a=false;break}if(!a)continue;let d=this.dataset[h];if(!d)continue;let g=d[e];typeof g=="string"&&g.toLowerCase().includes(n)&&o.push(d);}return o}searchAllFieldsLinear(e){if(!this.dataset.length)return [];let n=[];for(let t=0;t<this.dataset.length;t++){let r=this.dataset[t],i=false;for(let s of Object.values(r))if(typeof s=="string"&&s.toLowerCase().includes(e)){i=true;break}i&&n.push(r);}return n}searchFieldLinear(e,n){if(!this.dataset.length)return [];let t=[];for(let r=0;r<this.dataset.length;r++){let i=this.dataset[r][e];typeof i=="string"&&i.toLowerCase().includes(n)&&t.push(this.dataset[r]);}return t}withChain(e){let n=e;return Object.defineProperty(n,"search",{value:(t,r)=>r===void 0?this.search(t):this.search(t,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"data",{value:t=>this.data(t),enumerable:false,configurable:true,writable:true}),Object.defineProperty(n,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),n}clearIndexes(){return this.ngramIndexes.clear(),this}getOriginData(){return this.dataset}data(e){return this.dataset=e,this.rebuildConfiguredIndexes(),this}clearData(){return this.dataset=[],this.ngramIndexes.clear(),this}};export{x as a};
@@ -1 +0,0 @@
1
- var M=class{constructor(e){let{imports:t,data:n,...r}=e,a=new Set(t),s={},l={},c={},d={},u=null;for(let T of a){let f=T.prototype,g=this.getMethodNames(f);if(g.length===0)continue;let p=this.getModuleInitOptions(T.name,g,r),i=new T({data:n,...p}),o=this.getModuleName(g);o&&this.hasMethod(i,"clearIndexes")&&!l[o]&&(l[o]=i.clearIndexes.bind(i)),o&&this.hasMethod(i,"clearData")&&!c[o]&&(c[o]=i.clearData.bind(i)),o&&this.hasMethod(i,"data")&&!d[o]&&(d[o]=i.data.bind(i)),!u&&this.hasMethod(i,"getOriginData")&&(u=i.getOriginData.bind(i));for(let h of g)s[h]||this.hasMethod(i,h)&&(s[h]=i[h].bind(i));}this.engine=Object.keys(s).length>0?s:null,this.clearIndexMethods=l,this.clearDataMethods=c,this.setDataMethods=d,this.getOriginDataMethod=u;}getModuleName(e){return e.includes("search")?"search":e.includes("sort")?"sort":e.includes("filter")?"filter":null}getModuleInitOptions(e,t,n){let r={},a=n[e];this.isRecord(a)&&Object.assign(r,a);for(let s of t){let l=n[s];this.isRecord(l)&&Object.assign(r,l);}return r}getMethodNames(e){let t=e;return Object.getOwnPropertyNames(t).filter(n=>n==="constructor"?false:typeof t[n]=="function")}hasMethod(e,t){return typeof e=="object"&&e!==null&&typeof e[t]=="function"}isRecord(e){return typeof e=="object"&&e!==null}callEngineMethod(e,t){let n=this.engine?.[e];if(!n)throw new Error(`MergeEngines: Method "${e}" is not available. Add module with method "${e}" to the \`imports\` array.`);return n(...t)}search(e,t){if(!this.engine?.search)throw new Error("MergeEngines: TextSearchEngine is not available. Add TextSearchEngine to the `imports` array.");return t===void 0?this.withChain(this.callEngineMethod("search",[e])):this.withChain(this.callEngineMethod("search",[e,t]))}sort(e,t,n){if(!this.engine?.sort)throw new Error("MergeEngines: SortEngine is not available. Add SortEngine to the `imports` array.");return t===void 0?this.withChain(this.callEngineMethod("sort",[e])):this.withChain(this.callEngineMethod("sort",[e,t,n]))}filter(e,t){if(!this.engine?.filter)throw new Error("MergeEngines: FilterEngine is not available. Add FilterEngine to the `imports` array.");return t===void 0?this.withChain(this.callEngineMethod("filter",[e])):this.withChain(this.callEngineMethod("filter",[e,t]))}withChain(e){let t=e;return Object.defineProperty(t,"search",{value:(n,r)=>r===void 0?this.search(n):this.search(n,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"sort",{value:(n,r,a)=>r===void 0?this.sort(e,n,a):this.sort(n,r,a),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"filter",{value:(n,r)=>r===void 0?this.filter(e,n):this.filter(n,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearIndexes",{value:n=>(this.clearIndexes(n),this.withChain(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"clearData",{value:n=>(this.clearData(n),this.withChain(e)),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"data",{value:n=>this.data(n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(t,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),t}getOriginData(){if(this.getOriginDataMethod)return this.getOriginDataMethod();throw new Error("MergeEngines: getOriginData is not available. Add TextSearchEngine, SortEngine, or FilterEngine to the `imports` array.")}clearIndexes(e){let t=this.clearIndexMethods[e];if(t)return t(),this;let n={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"};throw new Error(`MergeEngines: ${n[e]} is not available. Add ${n[e]} to the \`imports\` array.`)}data(e){let t=["search","sort","filter"];for(let n of t){let r=this.setDataMethods[n];r&&r(e);}return this}clearData(e){let t=this.clearDataMethods[e];if(t)return t(),this;let n={search:"TextSearchEngine",sort:"SortEngine",filter:"FilterEngine"};throw new Error(`MergeEngines: ${n[e]} is not available. Add ${n[e]} to the \`imports\` array.`)}};export{M as a};
@@ -1 +0,0 @@
1
- var u=class{constructor(t={}){this.cache=new Map;this.dataset=[];this.indexedFields=new Set;if(t.data&&(this.dataset=t.data,t.fields?.length)){for(let s of t.fields)this.indexedFields.add(s);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.cache.clear();for(let t of this.indexedFields)this.buildIndex(this.dataset,t);}buildIndex(t,s){let a,e;if(Array.isArray(t))a=t,e=s;else {if(!this.dataset.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");a=this.dataset,e=t;}this.dataset=a;let r=a.length,n=new Uint32Array(r);for(let o=0;o<r;o++)n[o]=o;let l=a.map(o=>o[e]);if(typeof l[0]=="number"){let o=new Float64Array(r);for(let d=0;d<r;d++)o[d]=l[d];n.sort((d,c)=>o[d]-o[c]);}else n.sort((o,d)=>{let c=l[o],h=l[d];return c<h?-1:c>h?1:0});return this.cache.set(e,{indexes:n,dataRef:a,itemCount:r,fieldSnapshot:l}),this}clearIndexes(){return this.cache.clear(),this}clearData(){return this.dataset=[],this.cache.clear(),this}data(t){return this.dataset=t,this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}sort(t,s,a=false){let e,r;if(s===void 0){if(!this.dataset.length)throw new Error("SortEngine: no dataset in memory. Either pass `data` in the constructor options, or call sort(data, descriptors).");e=this.dataset,r=t;}else e=t,r=s;if(r.length===0||e.length===0)return this.withChain(e);if(r.length===1){let{field:i,direction:o}=r[0],d=this.cache.get(i);if(d&&d.dataRef===e&&d.itemCount===e.length&&this.isFieldSnapshotValid(e,i,d.fieldSnapshot))return this.withChain(this.reconstructFromIndex(e,d.indexes,o))}let n=a?e:e.slice();if(r.length===1&&e.length>0&&typeof e[0][r[0].field]=="number")return this.withChain(this.radixSortNumeric(n,r[0].field,r[0].direction));let l=this.buildComparator(r);return n.sort(l),this.withChain(n)}withChain(t){let s=t;return Object.defineProperty(s,"sort",{value:(a,e,r=false)=>e===void 0?this.sort(t,a,r):this.sort(a,e,r),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"data",{value:a=>this.data(a),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(s,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),s}reconstructFromIndex(t,s,a){let e=t.length,r=new Array(e);if(a==="asc")for(let n=0;n<e;n++)r[n]=t[s[n]];else for(let n=0;n<e;n++)r[n]=t[s[e-1-n]];return r}isFieldSnapshotValid(t,s,a){for(let e=0;e<t.length;e++)if(t[e][s]!==a[e])return false;return true}buildComparator(t){let s=t.map(({field:r})=>r),a=t.map(({direction:r})=>r==="asc"?1:-1),e=s.length;return (r,n)=>{for(let l=0;l<e;l++){let i=r[s[l]],o=n[s[l]];if(i<o)return -a[l];if(i>o)return a[l]}return 0}}radixSortNumeric(t,s,a){let e=t.length,r=new Float64Array(e);for(let i=0;i<e;i++)r[i]=t[i][s];let n=new Uint32Array(e);for(let i=0;i<e;i++)n[i]=i;n.sort((i,o)=>r[i]-r[o]);let l=new Array(e);if(a==="asc")for(let i=0;i<e;i++)l[i]=t[n[i]];else for(let i=0;i<e;i++)l[e-1-i]=t[n[i]];return l}};export{u as a};
@@ -1 +0,0 @@
1
- var p=class{constructor(){this.indexes=new Map;}buildIndex(e,r){let n=new Map;for(let s=0,t=e.length;s<t;s++){let i=e[s],a=i[r];if(a==null)continue;let l=n.get(a);l?l.push(i):n.set(a,[i]);}this.indexes.set(r,n);}getByValue(e,r){let n=this.indexes.get(e);return n?n.get(r)??[]:[]}getByValues(e,r){let n=this.indexes.get(e);if(!n)return [];if(r.length===1)return n.get(r[0])??[];let s=new Set,t=[];for(let i=0;i<r.length;i++){let a=n.get(r[i]);if(a!==void 0)for(let l=0;l<a.length;l++){let o=a[l];s.has(o)||(s.add(o),t.push(o));}}return t}hasIndex(e){return this.indexes.has(e)}clear(){this.indexes.clear();}getIndexMap(e){return this.indexes.get(e)}};var v=class{constructor(e={}){this.dataset=[];this.indexedFields=new Set;this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;if(this.indexer=new p,this.filterByPreviousResult=e.filterByPreviousResult??false,!!e.data&&(this.dataset=e.data,e.fields?.length)){for(let r of e.fields)this.indexedFields.add(r);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.indexer.clear();for(let e of this.indexedFields)this.buildIndex(this.dataset,e);}buildIndex(e,r){if(!Array.isArray(e)){if(!this.dataset.length)throw new Error("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");return this.indexer.buildIndex(this.dataset,e),this}return this.dataset=e,this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this.indexer.buildIndex(e,r),this}clearIndexes(){return this.indexer.clear(),this}resetFilterState(){return this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null,this}clearData(){return this.dataset=[],this.indexer.clear(),this.resetFilterState(),this}data(e){return this.dataset=e,this.resetFilterState(),this.rebuildConfiguredIndexes(),this}getOriginData(){return this.dataset}filter(e,r){let n=r===void 0,s,t,i;if(n){if(!this.dataset.length)throw new Error("FilterEngine: no dataset in memory. Either pass `data` in the constructor options, or call filter(data, criteria).");if(t=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.dataset){let u=this.hasCriteriaAdditions(this.previousCriteria,t),h=this.hasCriteriaRemovals(this.previousCriteria,t);if(!u&&!h)return this.withChain(this.previousResult);u&&!h?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,t)):(s=this.dataset,i=t);}else s=this.dataset,i=t;}else if(t=r,s=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===s){let u=this.hasCriteriaAdditions(this.previousCriteria,t),h=this.hasCriteriaRemovals(this.previousCriteria,t);if(!u&&!h)return this.withChain(this.previousResult);u&&!h?(s=this.previousResult,i=this.getAddedCriteria(this.previousCriteria,t)):i=t;}else i=t;if(t.length===0)return this.filterByPreviousResult&&(this.previousResult=null,this.previousCriteria=null,this.previousBaseData=null),this.withChain(n?this.dataset:s);n&&!i&&(i=t);let{indexedCriteria:a,linearCriteria:l}=i.reduce((u,h)=>(this.indexer.hasIndex(h.field)?u.indexedCriteria.push(h):u.linearCriteria.push(h),u),{indexedCriteria:[],linearCriteria:[]}),o;if(a.length>0&&l.length===0)return o=this.filterViaIndex(a,s),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o);if(a.length>0&&l.length>0){let u=this.filterViaIndex(a,s);return o=this.linearFilter(u,l),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o)}return o=this.linearFilter(s,i),this.filterByPreviousResult&&(this.previousResult=o,this.previousCriteria=this.cloneCriteria(t),this.previousBaseData=n?this.dataset:e),this.withChain(o)}withChain(e){let r=e;return Object.defineProperty(r,"filter",{value:(n,s)=>s===void 0?this.filter(e,n):this.filter(n,s),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"clearIndexes",{value:()=>this.clearIndexes(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"data",{value:n=>this.data(n),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"getOriginData",{value:()=>this.getOriginData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"clearData",{value:()=>this.clearData(),enumerable:false,configurable:true,writable:true}),Object.defineProperty(r,"resetFilterState",{value:()=>this.resetFilterState(),enumerable:false,configurable:true,writable:true}),r}cloneCriteria(e){return e.map(({field:r,values:n})=>({field:r,values:[...n]}))}hasCriteriaAdditions(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=new Map(r.map(({field:t,values:i})=>[t,new Set(i)]));for(let[t,i]of s){let a=n.get(t);if(!a)return true;for(let l of i)if(!a.has(l))return true}return false}hasCriteriaRemovals(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=new Map(r.map(({field:t,values:i})=>[t,new Set(i)]));for(let[t,i]of n){let a=s.get(t);if(!a)return true;for(let l of i)if(!a.has(l))return true}return false}getAddedCriteria(e,r){let n=new Map(e.map(({field:t,values:i})=>[t,new Set(i)])),s=[];for(let{field:t,values:i}of r){let a=n.get(t);if(!a){s.push({field:t,values:[...i]});continue}let l=i.filter(o=>!a.has(o));l.length>0&&s.push({field:t,values:l});}return s}linearFilter(e,r){let n=new Map(r.map(({field:i,values:a})=>[i,new Set(a)])),s=r.map(({field:i})=>i),t=[];for(let i=0;i<e.length;i++){let a=e[i],l=true;for(let o=0;o<s.length;o++){let u=s[o];if(!n.get(u).has(a[u])){l=false;break}}l&&t.push(a);}return t}filterViaIndex(e,r){let s=r!==this.dataset?new Set(r):null;if(e.length===1){let d=this.indexer.getByValues(e[0].field,e[0].values);return s?d.filter(f=>s.has(f)):d}let t=e.map(d=>({criterion:d,size:this.estimateIndexSize(d)})).sort((d,f)=>d.size-f.size),{field:i,values:a}=t[0].criterion,l=this.indexer.getByValues(i,a);if(l.length===0)return [];let o=new Map(t.slice(1).map(({criterion:{field:d,values:f}})=>[d,new Set(f)])),u=Array.from(o.keys()),h=[];for(let d=0;d<l.length;d++){let f=l[d],c=true;for(let g=0;g<u.length;g++){let T=u[g];if(!o.get(T).has(f[T])){c=false;break}}c&&s&&!s.has(f)&&(c=false),c&&h.push(f);}return h}estimateIndexSize(e){let r=this.indexer.getIndexMap(e.field);return r?e.values.reduce((n,s)=>{let t=r.get(s);return t?n+t.length:n},0):1/0}};export{v as a};