@devisfuture/mega-collection 2.1.4 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,11 +14,15 @@ If this package saved you some time, a ⭐ on GitHub would be much appreciated.
14
14
  - [Install](#install) – how to install the package
15
15
  - [Quick Start](#quick-start) – basic usage examples
16
16
  - [All-in-one: `MergeEngines`](#all-in-one-mergeengines) – use search, filter, and sort from one engine
17
+ - [Add items with `add([])`](#add-items-with-add) – append multiple items to stored data
17
18
  - [Search only](#search-only) – use only text search
18
19
  - [Flat collections search](#flat-collections-search) – search simple fields like `name` or `city`
19
20
  - [Nested collections search](#nested-collections-search) – search inside nested arrays like `orders.status`
20
21
  - [Filter only](#filter-only) – use only filtering
21
22
  - [Flat collections filter](#flat-collections-filter) – filter by simple top-level fields
23
+ - [Exclude items with `exclude`](#exclude-items-with-exclude) – remove matching items from the result
24
+ - [Result-only exclude](#result-only-exclude) – return a filtered result without mutating stored data
25
+ - [Mutable exclude with `mutableExcludeField`](#mutable-exclude-with-mutableexcludefield) – fast delete-like removal via swap-pop
22
26
  - [Nested collections filter](#nested-collections-filter) – filter by nested array fields
23
27
  - [Sort only](#sort-only) – use only sorting
24
28
  - [API Reference](#api-reference) – list of options and methods
@@ -34,15 +38,18 @@ If this package saved you some time, a ⭐ on GitHub would be much appreciated.
34
38
 
35
39
  ## What does this package solve
36
40
 
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.
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.
41
+ Sometimes the server returns a very large array, for example 100K+ items.
42
+ Most often you then need to search, filter, or sort this data.
39
43
 
40
- Zero dependencies. Tree-shakeable. Import only what you need.
44
+ This package helps do these operations faster than plain JavaScript methods.
45
+ Usually this happens before data is shown in the UI.
41
46
 
42
- Each engine lives in its own entry point (`/search`, `/filter`, `/sort`).
43
- Importing just `@devisfuture/mega-collection/search` or the other sub-modules means
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
+ The package has no dependencies.
48
+ You can import only the parts you need.
49
+
50
+ Each engine has its own entry point: `/search`, `/filter`, `/sort`.
51
+ If you import only `@devisfuture/mega-collection/search`, only search code goes into the bundle.
52
+ Unused modules are not included.
46
53
 
47
54
  ## Features
48
55
 
@@ -57,10 +64,10 @@ you only import `TextSearchEngine`, the filter and sort code will not be include
57
64
 
58
65
  ## React demo
59
66
 
60
- A small [repository](https://github.com/trae-op/quick-start_react_mega-collection) demonstrates using `@devisfuture/mega-collection` in a React project.
61
- The example shows search, filter, sort and merge all modules setups with a minimal UI.
67
+ A small [repository](https://github.com/trae-op/quick-start_react_mega-collection) shows how to use `@devisfuture/mega-collection` in React.
68
+ It has examples for search, filter, sort, and `MergeEngines` with a simple UI.
62
69
 
63
- A live build of the React app is available at [demo](https://trae-op.github.io/quick-start_react_mega-collection/), showcasing how the package works in a real application.
70
+ There is also a live [demo](https://trae-op.github.io/quick-start_react_mega-collection/).
64
71
 
65
72
  ## Install
66
73
 
@@ -92,19 +99,17 @@ interface UserWithOrders extends User {
92
99
 
93
100
  ### All-in-one: `MergeEngines`
94
101
 
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.
102
+ Use `MergeEngines` when you want one class that works with one dataset.
103
+ Add needed engines to `imports`. Only those engines will be created.
97
104
 
98
105
  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.
106
+ Each instance stores its own data and indexes, so they do not affect each other.
107
+
108
+ Each engine can receive an optional `fields` array through `search`, `filter`, or `sort` options.
109
+ These fields are used for indexes.
101
110
 
102
- Each engine accepts an optional `fields` array (set via the `search`,
103
- `filter` or `sort` option) which tells it which properties should use indexed
104
- execution. Those indexes are built lazily on the first matching operation, so
105
- initial engine creation stays fast even for very large collections. If you skip
106
- `fields`, everything still works, but the engine may need to scan the full
107
- array.
111
+ Indexes are built lazily on first use, so engine creation stays fast.
112
+ If you skip `fields`, everything still works, but the engine may scan the full array.
108
113
 
109
114
  ```ts
110
115
  import { MergeEngines } from "@devisfuture/mega-collection";
@@ -120,13 +125,19 @@ const engine = new MergeEngines<User>({
120
125
  sort: { fields: ["age", "name", "city"] },
121
126
  });
122
127
 
123
- // dataset is passed once at init — no need to repeat it in every call
128
+ const mutableMerge = new MergeEngines<User>({
129
+ imports: [FilterEngine],
130
+ data: users,
131
+ filter: { fields: ["id", "city"], mutableExcludeField: "id" },
132
+ });
133
+
134
+ // Dataset is passed once in the constructor.
124
135
  engine
125
136
  .search("john")
126
137
  .sort([{ field: "age", direction: "asc" }])
127
138
  .filter([{ field: "city", values: ["Miami", "New York"] }]);
128
139
 
129
- // with nested collection fields (e.g. orders inside each user)
140
+ // Example with nested fields, for example `orders` inside each user.
130
141
  const nestedEngine = new MergeEngines<UserWithOrders>({
131
142
  imports: [TextSearchEngine, SortEngine, FilterEngine],
132
143
  data: usersWithOrders,
@@ -146,7 +157,7 @@ const nestedEngine = new MergeEngines<UserWithOrders>({
146
157
  nestedEngine.search("pending"); // finds users whose orders contain "pending"
147
158
  nestedEngine.filter([{ field: "orders.status", values: ["delivered"] }]);
148
159
 
149
- // update dataset later without creating a new instance
160
+ // Replace dataset later without creating a new instance.
150
161
  engine.data([
151
162
  {
152
163
  id: 1,
@@ -156,12 +167,90 @@ engine.data([
156
167
  },
157
168
  ]);
158
169
 
159
- // clear indexes/data for one module
170
+ // Clear indexes or data for one module.
160
171
  engine.clearIndexes("search").clearIndexes("sort").clearIndexes("filter");
161
172
  engine.clearData("search").clearData("sort").clearData("filter");
162
173
 
163
- // get shared original dataset
174
+ // Get shared original dataset.
164
175
  engine.getOriginData();
176
+
177
+ // Remove items through the root facade.
178
+ mutableMerge.filter([{ field: "id", exclude: [1, 4] }]);
179
+ ```
180
+
181
+ ---
182
+
183
+ ### Add items with `add([])`
184
+
185
+ Use `add([])` when you need to append several new items to the stored dataset.
186
+ This is different from `data(...)`:
187
+
188
+ - `data(...)` replaces the whole stored dataset.
189
+ - `add([])` appends new items to the existing stored dataset.
190
+
191
+ If indexes are already built, the engine updates only the new items instead of rebuilding the whole dataset.
192
+ If you cleared indexes with `clearIndexes()`, `add([])` does not rebuild them automatically.
193
+
194
+ ```ts
195
+ import { MergeEngines } from "@devisfuture/mega-collection";
196
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
197
+ import { SortEngine } from "@devisfuture/mega-collection/sort";
198
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
199
+
200
+ const merge = new MergeEngines<User>({
201
+ imports: [TextSearchEngine, SortEngine, FilterEngine],
202
+ data: users,
203
+ search: { fields: ["name", "city"], minQueryLength: 2 },
204
+ filter: { fields: ["city", "age"] },
205
+ sort: { fields: ["age", "name"] },
206
+ });
207
+
208
+ merge.add([
209
+ { id: 6, name: "Lia", city: "Berlin", age: 28 },
210
+ { id: 7, name: "Omar", city: "Kyiv", age: 31 },
211
+ ]);
212
+
213
+ merge.search("Berlin");
214
+ merge.filter([{ field: "city", values: ["Kyiv"] }]);
215
+ merge.sort([{ field: "age", direction: "asc" }]);
216
+ ```
217
+
218
+ The same method works in each engine:
219
+
220
+ ```ts
221
+ import { TextSearchEngine } from "@devisfuture/mega-collection/search";
222
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
223
+ import { SortEngine } from "@devisfuture/mega-collection/sort";
224
+
225
+ const searchEngine = new TextSearchEngine<User>({
226
+ data: users,
227
+ fields: ["name", "city"],
228
+ });
229
+
230
+ searchEngine.add([
231
+ { id: 6, name: "Lia", city: "Berlin", age: 28 },
232
+ { id: 7, name: "Omar", city: "Kyiv", age: 31 },
233
+ ]);
234
+
235
+ const filterEngine = new FilterEngine<User>({
236
+ data: users,
237
+ fields: ["city", "age"],
238
+ });
239
+
240
+ filterEngine.add([
241
+ { id: 6, name: "Lia", city: "Berlin", age: 28 },
242
+ { id: 7, name: "Omar", city: "Kyiv", age: 31 },
243
+ ]);
244
+
245
+ const sortEngine = new SortEngine<User>({
246
+ data: users,
247
+ fields: ["age", "name"],
248
+ });
249
+
250
+ sortEngine.add([
251
+ { id: 6, name: "Lia", city: "Berlin", age: 28 },
252
+ { id: 7, name: "Omar", city: "Kyiv", age: 31 },
253
+ ]);
165
254
  ```
166
255
 
167
256
  ---
@@ -169,7 +258,7 @@ engine.getOriginData();
169
258
  ### Search only
170
259
 
171
260
  Use `TextSearchEngine` when you only need text search.
172
- The examples below show the difference between simple fields and nested fields.
261
+ The examples below show search by simple fields and nested fields.
173
262
 
174
263
  #### Flat collections search
175
264
 
@@ -177,16 +266,16 @@ The examples below show the difference between simple fields and nested fields.
177
266
  import { TextSearchEngine } from "@devisfuture/mega-collection/search";
178
267
 
179
268
  // `fields` tells the engine which fields should use indexed search.
180
- // The index is built lazily on first use. If you skip `fields`, search still
181
- // works, but it will scan the full dataset.
269
+ // The index is built only when it is needed for the first time.
270
+ // If you skip `fields`, search still works, but it scans the full dataset.
182
271
  const engine = new TextSearchEngine<User>({
183
272
  data: users,
184
273
  fields: ["name", "city"],
185
274
  minQueryLength: 2, // begins searching when query length >= 2
186
275
  });
187
276
 
188
- // If the query is shorter than `minQueryLength`, the engine returns the
189
- // original dataset. Empty or blank queries also return the original dataset.
277
+ // If the query is shorter than `minQueryLength`, the engine returns
278
+ // the original dataset. Empty or blank queries do the same.
190
279
 
191
280
  engine.search("john"); // searches all indexed fields, deduplicated
192
281
  engine.search("name", "john"); // searches a specific field
@@ -222,16 +311,16 @@ nestedSearch.search("orders.status", "delivered"); // search a specific nested f
222
311
  ### Filter only
223
312
 
224
313
  Use `FilterEngine` when you only need filtering.
225
- The examples below show the difference between simple fields and nested fields.
314
+ The examples below show filtering by simple fields and nested fields.
226
315
 
227
316
  #### Flat collections filter
228
317
 
229
318
  ```ts
230
319
  import { FilterEngine } from "@devisfuture/mega-collection/filter";
231
320
 
232
- // `fields` tells the engine which fields should use indexed filter lookups.
233
- // The index is built lazily on first use. Without `fields`, filtering still
234
- // works, but it will scan the data.
321
+ // `fields` tells the engine which fields should use indexes for filtering.
322
+ // The index is built only when it is needed for the first time.
323
+ // Without `fields`, filtering still works, but it scans the data.
235
324
  const engine = new FilterEngine<User>({
236
325
  data: users,
237
326
  fields: ["city", "age"],
@@ -243,24 +332,118 @@ engine.filter([
243
332
  { field: "age", values: [25, 30, 35] },
244
333
  ]);
245
334
 
246
- // replace dataset without re-initializing
335
+ // Replace dataset without creating a new engine.
247
336
  engine.data(users);
248
337
 
249
- // access original dataset stored in the engine
338
+ // Get original stored dataset.
250
339
  engine.getOriginData();
251
340
 
252
- engine
253
- .filter([{ field: "city", values: ["Miami", "New York"] }])
254
- .filter([{ field: "age", values: [25, 30, 35] }])
255
- .clearIndexes()
256
- .resetFilterState()
257
- .clearData();
258
-
259
341
  // Sequential mode example:
260
- // 1) First call filters by city
342
+ // 1) First call filters by city.
261
343
  const byCity = engine.filter([{ field: "city", values: ["Miami"] }]);
262
- // 2) Second call filters only inside the previous result
344
+ // 2) Second call works only on the previous result.
263
345
  const byCityAndAge = engine.filter([{ field: "age", values: [22] }]);
346
+ // 3) Returning to an earlier criteria state restores its previous result.
347
+ const byCityAgain = engine.filter([{ field: "city", values: ["Miami"] }]);
348
+ ```
349
+
350
+ #### Exclude items with `exclude`
351
+
352
+ Use `exclude` when you want to remove items from the result by exact field values.
353
+ This is useful when you already know which `id` values or other field values should not be in the result.
354
+
355
+ `exclude` changes only the returned result. It does not change the stored dataset inside the engine.
356
+
357
+ There are two ways to work with `exclude`:
358
+
359
+ - Result-only exclude: returns a filtered array and leaves the stored dataset unchanged.
360
+ - Mutable exclude with `mutableExcludeField`: removes items from the stored dataset with **swap-pop**.
361
+
362
+ **Swap-pop** is an efficient array removal technique where the element to be removed is swapped with the last element in the array, and then the array length is decreased by one. This provides O(1) time complexity for removal but does not preserve the original order of elements.
363
+
364
+ #### Result-only exclude
365
+
366
+ If the engine already stores the full dataset, `exclude` alone is enough. For example,
367
+ `engine.filter([{ field: "id", exclude: [1, 4] }])` returns all stored users except users with `id` `1` and `4`.
368
+
369
+ This mode does not use swap-pop on the stored dataset. `filter(...)` returns a new array,
370
+ so the engine still needs one pass over the current data to build the result.
371
+ If `id` is indexed, the engine does not scan the full dataset again for each excluded `id`,
372
+ but it still has to build the final array.
373
+
374
+ If you need repeated removals from a large collection and do not want O(n) work for each removed item,
375
+ use mutable exclude mode.
376
+ In this mode the engine removes items from the stored dataset with swap-pop.
377
+ Order is not preserved.
378
+
379
+ If the field is listed in `fields`, the engine uses indexes for exclude values
380
+ instead of scanning the full dataset again for every removed value.
381
+
382
+ ```ts
383
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
384
+
385
+ const engine = new FilterEngine<User>({
386
+ data: users,
387
+ fields: ["id", "city"],
388
+ });
389
+
390
+ // Returns all users except users with ids 1 and 3.
391
+ const visibleUsers = engine.filter([{ field: "id", exclude: [1, 3] }]);
392
+
393
+ // You can combine normal filtering and exclude.
394
+ engine.filter([
395
+ { field: "city", values: ["Miami", "New York"] },
396
+ { field: "id", exclude: [1, 3] },
397
+ ]);
398
+ ```
399
+
400
+ #### Mutable exclude with `mutableExcludeField`
401
+
402
+ If you need repeated fast removals from a large stored dataset, use `mutableExcludeField`.
403
+ In this mode the engine removes items from the stored dataset with swap-pop.
404
+
405
+ Use this mode when all of these points are true:
406
+
407
+ - the engine already stores the full dataset
408
+ - the exclude field is unique, for example `id`
409
+ - order does not need to be preserved
410
+ - you want repeated removals without O(n) per removed id
411
+
412
+ This mode changes the stored dataset.
413
+ After exclusion, `getOriginData()` returns the reduced collection.
414
+
415
+ ```ts
416
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
417
+
418
+ const mutableEngine = new FilterEngine<User>({
419
+ data: users,
420
+ fields: ["id", "city"],
421
+ mutableExcludeField: "id",
422
+ });
423
+
424
+ // Removes items from the stored dataset with swap-pop.
425
+ mutableEngine.filter([{ field: "id", exclude: [1, 4] }]);
426
+
427
+ // The stored dataset is now smaller.
428
+ mutableEngine.getOriginData();
429
+ ```
430
+
431
+ The same mode also works through `MergeEngines`:
432
+
433
+ ```ts
434
+ import { MergeEngines } from "@devisfuture/mega-collection";
435
+ import { FilterEngine } from "@devisfuture/mega-collection/filter";
436
+
437
+ const mutableMerge = new MergeEngines<User>({
438
+ imports: [FilterEngine],
439
+ data: users,
440
+ filter: {
441
+ fields: ["id", "city"],
442
+ mutableExcludeField: "id",
443
+ },
444
+ });
445
+
446
+ mutableMerge.filter([{ field: "id", exclude: [1, 4] }]);
264
447
  ```
265
448
 
266
449
  #### Nested collections filter
@@ -324,17 +507,17 @@ engine.sort([
324
507
 
325
508
  ### `MergeEngines<T>` (root module)
326
509
 
327
- One engine that brings search, filter, and sort together on the same dataset.
510
+ One class that combines search, filter, and sort for the same dataset.
328
511
 
329
512
  **Constructor options:**
330
513
 
331
- | Option | Type | Description |
332
- | --------- | ----------------------------------------------------------- | -------------------------------------------- |
333
- | `imports` | `(typeof TextSearchEngine \| SortEngine \| FilterEngine)[]` | Engine classes to activate |
334
- | `data` | `T[]` | Shared dataset — passed once at construction |
335
- | `search` | `{ fields, nestedFields?, minQueryLength? }` | Config for TextSearchEngine |
336
- | `filter` | `{ fields, nestedFields?, filterByPreviousResult? }` | Config for FilterEngine |
337
- | `sort` | `{ fields }` | Config for SortEngine |
514
+ | Option | Type | Description |
515
+ | --------- | -------------------------------------------------------------------------- | -------------------------------------------- |
516
+ | `imports` | `(typeof TextSearchEngine \| SortEngine \| FilterEngine)[]` | Engine classes to create |
517
+ | `data` | `T[]` | Shared dataset — passed once at construction |
518
+ | `search` | `{ fields, nestedFields?, minQueryLength? }` | Config for TextSearchEngine |
519
+ | `filter` | `{ fields, nestedFields?, filterByPreviousResult?, mutableExcludeField? }` | Config for FilterEngine |
520
+ | `sort` | `{ fields }` | Config for SortEngine |
338
521
 
339
522
  **Methods:**
340
523
 
@@ -347,37 +530,44 @@ One engine that brings search, filter, and sort together on the same dataset.
347
530
  | `filter(criteria)` | Filter using stored dataset |
348
531
  | `filter(data, criteria)` | Filter with an explicit dataset |
349
532
  | `getOriginData()` | Get the shared original dataset |
533
+ | `add(items)` | Append multiple items to the stored dataset and update existing indexes or caches for new items only |
350
534
  | `data(data)` | Replace stored dataset for all imported modules, rebuilding configured indexes and resetting filter state where applicable |
351
535
  | `clearIndexes(module)` | Clear indexes for one module (`"search"`, `"sort"`, `"filter"`) |
352
536
  | `clearData(module)` | Clear stored data for one module (`"search"`, `"sort"`, `"filter"`) |
353
537
 
538
+ If `filter.mutableExcludeField` is configured, `filter([{ field, exclude }])` on that field removes items from the stored filter dataset with swap-pop.
539
+ This changes the stored filter dataset and does not preserve order.
540
+
354
541
  ---
355
542
 
356
543
  ### `TextSearchEngine<T>` (search module)
357
544
 
358
- Text search engine. It supports `nestedFields` if you need to search inside
359
- nested collections such as `["orders.status"]`. Search methods return plain
360
- arrays.
545
+ Text search engine.
546
+ It supports `nestedFields` if you need to search inside nested collections such as `["orders.status"]`.
547
+ Search methods return plain arrays.
361
548
 
362
549
  | Method | Description |
363
550
  | ---------------------- | ---------------------------------------------------------- |
364
551
  | `search(query)` | Search all indexed fields (including nested), deduplicated |
365
552
  | `search(field, query)` | Search a specific indexed field or nested field path |
366
553
  | `getOriginData()` | Get the original stored dataset |
554
+ | `add(items)` | Append multiple items to the stored dataset |
367
555
  | `data(data)` | Replace stored dataset and rebuild configured indexes |
368
556
  | `clearIndexes()` | Clear n-gram indexes (including nested) |
369
557
  | `clearData()` | Clear stored data |
370
558
 
371
559
  ### `FilterEngine<T>` (filter module)
372
560
 
373
- Filter engine for one or more rules. It supports `nestedFields` if you need to
374
- filter by values inside nested collections such as `["orders.status"]`.
561
+ Filter engine for one or more rules.
562
+ It supports `nestedFields` if you need to filter by values inside nested collections such as `["orders.status"]`.
563
+ Each criterion can use `values`, `exclude`, or both in the same rule.
375
564
 
376
- Constructor option highlights:
565
+ Main constructor options:
377
566
 
378
567
  | Option | Type | Description |
379
568
  | ------------------------ | ---------- | ---------------------------------------------------------------------------------------------------------------------------------- |
380
569
  | `filterByPreviousResult` | `boolean` | When `true`, the next `filter(criteria)` call works on the previous result. By default each call starts from the original dataset. |
570
+ | `mutableExcludeField` | `string` | Optional field for removing items from stored data with swap-pop. This changes the stored dataset and does not preserve order. |
381
571
  | `nestedFields` | `string[]` | Nested field paths in dot notation, for example `["orders.status"]`. |
382
572
 
383
573
  | Method | Description |
@@ -385,6 +575,7 @@ Constructor option highlights:
385
575
  | `filter(criteria)` | Filter using stored dataset (supports nested field criteria) |
386
576
  | `filter(data, criteria)` | Filter with an explicit dataset |
387
577
  | `getOriginData()` | Get the original stored dataset |
578
+ | `add(items)` | Append multiple items to the stored dataset |
388
579
  | `data(data)` | Replace stored dataset, rebuild configured indexes, and reset filter state |
389
580
  | `resetFilterState()` | Reset previous-result state for sequential filtering |
390
581
  | `clearIndexes()` | Free all index memory (including nested indexes) |
@@ -392,21 +583,22 @@ Constructor option highlights:
392
583
 
393
584
  ### `SortEngine<T>` (sort module)
394
585
 
395
- Sort engine with prepared indexes for faster sorting in common cases. Sort
396
- methods return plain arrays.
586
+ Sort engine with prepared indexes for faster sorting in common cases.
587
+ Sort methods return plain arrays.
397
588
 
398
589
  | Method | Description |
399
590
  | ----------------------------------- | ----------------------------------------------------- |
400
591
  | `sort(descriptors)` | Sort using stored dataset |
401
592
  | `sort(data, descriptors, inPlace?)` | Sort with an explicit dataset |
402
593
  | `getOriginData()` | Get the original stored dataset |
594
+ | `add(items)` | Append multiple items to the stored dataset |
403
595
  | `data(data)` | Replace stored dataset and rebuild configured indexes |
404
596
  | `clearIndexes()` | Free all cached indexes |
405
597
  | `clearData()` | Clear stored data |
406
598
 
407
599
  ---
408
600
 
409
- **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.
601
+ **Note on `data` method:** Calling `data` updates the stored dataset. It also rebuilds configured indexes and resets internal state when needed, so usually you do not need to call `clearIndexes` before it.
410
602
 
411
603
  ## Types
412
604
 
@@ -423,7 +615,7 @@ import type {
423
615
  } from "@devisfuture/mega-collection";
424
616
  ```
425
617
 
426
- Or from individual sub-modules:
618
+ You can also import them from individual sub-modules:
427
619
 
428
620
  ```ts
429
621
  import type {
@@ -0,0 +1 @@
1
+ import {a}from'./chunk-Y7NSBYLS.mjs';import {a as a$2}from'./chunk-U3IU4NI6.mjs';import {a as a$1}from'./chunk-Z4QO4GOM.mjs';function c(o){return {value:o,enumerable:false,configurable:true,writable:true}}var p=class{constructor(e){this.callbacks=e;}create(e){let a=e;return Object.defineProperties(a,{search:c((t,r)=>r===void 0?this.callbacks.search(t):this.callbacks.search(t,r)),sort:c((t,r,i)=>r===void 0?this.callbacks.sort(e,t,i):this.callbacks.sort(t,r,i)),filter:c((t,r)=>r===void 0?this.callbacks.filter(e,t):this.callbacks.filter(t,r)),add:c(t=>this.callbacks.add(t)),clearIndexes:c(t=>(this.callbacks.clearIndexes(t),this.create(e))),clearData:c(t=>(this.callbacks.clearData(t),this.create(e))),data:c(t=>this.callbacks.data(t)),getOriginData:c(()=>this.callbacks.getOriginData())}),a}};var v=o=>o===a?"search":o===a$1?"sort":o===a$2?"filter":null,w=(o,e,a$3)=>{if(o===a){let t=new a({data:e,...a$3});return {moduleName:"search",executeSearch:(r,i)=>i===void 0?t.search(r):t.search(r,i),add:(r,i=true)=>t.applyAddedItems(r,i),clearIndexes:()=>t.clearIndexes(),clearData:()=>t.clearData(),data:r=>t.data(r),getOriginData:()=>t.getOriginData()}}if(o===a$1){let t=new a$1({data:e,...a$3});return {moduleName:"sort",executeSort:(r,i,s)=>i===void 0?t.sort(r):t.sort(r,i,s),add:(r,i=true)=>t.applyAddedItems(r,i),clearIndexes:()=>t.clearIndexes(),clearData:()=>t.clearData(),data:r=>t.data(r),getOriginData:()=>t.getOriginData()}}if(o===a$2){let t=new a$2({data:e,...a$3});return {moduleName:"filter",executeFilter:(r,i)=>i===void 0?t.rawFilter(r):t.rawFilter(r,i),add:(r,i=true)=>t.applyAddedItems(r,i),clearIndexes:()=>t.clearIndexes(),clearData:()=>t.clearData(),data:r=>t.data(r),getOriginData:()=>t.getOriginData()}}return null};var b={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.`)}static unavailableEngine(e){let a=b[e];return new o(`MergeEngines: ${a} is not available.`)}static unavailableGetOriginData(){return new o("MergeEngines: getOriginData is not available.")}};var f=class{constructor(e){this.previousSearchState=null;this.previousSortState=null;this.chainBuilder=new p({search:(e,a)=>a===void 0?this.search(e):this.search(e,a),sort:(e,a,t)=>a===void 0?this.sort(e):this.sort(e,a,t),filter:(e,a)=>a===void 0?this.filter(e):this.filter(e,a),getOriginData:()=>this.getOriginData(),add:e=>this.add(e),data:e=>this.data(e),clearIndexes:e=>this.clearIndexes(e),clearData:e=>this.clearData(e)});let{imports:a,data:t,...r}=e,i=new Set(a),s=null,d=null,u=null,S={},D={},C={},E={},x={},y=null;for(let T of i){let N=v(T);if(!N)continue;let k=this.getModuleInitOptions(N,T.name,r),n=w(T,t,k);n&&(n.moduleName==="search"&&!s&&(s=n),n.moduleName==="sort"&&!d&&(d=n),n.moduleName==="filter"&&!u&&(u=n),S[n.moduleName]=()=>n.clearIndexes(),D[n.moduleName]=()=>n.clearData(),C[n.moduleName]=(m,O)=>n.add(m,O),E[n.moduleName]=()=>n.getOriginData(),x[n.moduleName]=m=>n.data(m),y??(y=()=>n.getOriginData()));}this.searchModule=s,this.sortModule=d,this.filterModule=u,this.clearIndexMethods=S,this.clearDataMethods=D,this.addMethods=C,this.getOriginDataMethods=E,this.setDataMethods=x,this.getOriginDataMethod=y;}getModuleInitOptions(e,a,t){let r={};for(let i of [e,a]){let s=t[i];this.isRecord(s)&&Object.assign(r,s);}return r}isRecord(e){return typeof e=="object"&&e!==null}clearOperationState(e){(!e||e==="search")&&(this.previousSearchState=null),(!e||e==="sort")&&(this.previousSortState=null);}createSearchCacheKey(e,a){return JSON.stringify([e,a??null])}createSortCacheKey(e,a){return JSON.stringify({descriptors:e,inPlace:a??false})}search(e,a){if(!this.searchModule)throw l.unavailableEngine("search");let t=this.searchModule.getOriginData(),r=this.createSearchCacheKey(e,a);if(this.previousSearchState?.originData===t&&this.previousSearchState.key===r)return this.withChain(this.previousSearchState.result);let i;return a===void 0?i=this.searchModule.executeSearch(e):i=this.searchModule.executeSearch(e,a),this.previousSearchState={key:r,originData:t,result:i},this.withChain(i)}sort(e,a,t){if(!this.sortModule)throw l.unavailableEngine("sort");if(a===void 0)return this.withChain(this.sortModule.executeSort(e));let r=e;if(!t){let i=this.createSortCacheKey(a,t);if(this.previousSortState?.sourceData===r&&this.previousSortState.key===i)return this.withChain(this.previousSortState.result);let s=this.sortModule.executeSort(r,a,t);return this.previousSortState={key:i,sourceData:r,result:s},this.withChain(s)}return this.withChain(this.sortModule.executeSort(r,a,t))}filter(e,a){if(!this.filterModule)throw l.unavailableEngine("filter");return a===void 0?this.withChain(this.filterModule.executeFilter(e)):this.withChain(this.filterModule.executeFilter(e,a))}withChain(e){return this.chainBuilder.create(e)}getOriginData(){if(this.getOriginDataMethod)return this.getOriginDataMethod();throw l.unavailableGetOriginData()}add(e){if(e.length===0)return this;let a=["search","sort","filter"],t=new Set;this.clearOperationState();for(let r of a){let i=this.addMethods[r],s=this.getOriginDataMethods[r];if(!i)continue;let d=s?.(),u=d===void 0||!t.has(d);i(e,u),d!==void 0&&t.add(d);}return this}clearIndexes(e){let a=this.clearIndexMethods[e];if(a)return a(),this.clearOperationState(e),this;throw l.unavailableEngine(e)}data(e){let a=["search","sort","filter"];this.clearOperationState();for(let t of a){let r=this.setDataMethods[t];r&&r(e);}return this}clearData(e){let a=this.clearDataMethods[e];if(a)return a(),this.clearOperationState(e),this;throw l.unavailableEngine(e)}};export{f as a};
@@ -0,0 +1 @@
1
+ var I=class{constructor(){this.indexes=new Map;this.itemPositions=new Map;}addItems(e){if(!(e.length===0||this.indexes.size===0))for(let t=0;t<e.length;t++)this.addItem(e[t]);}buildIndex(e,t){let n=new Map,i=new Map;for(let s=0,r=e.length;s<r;s++){let l=e[s],a=l[t];if(a==null)continue;let u=n.get(a);if(u)u.push(l),i.get(a).set(l,u.length-1);else {n.set(a,[l]);let o=new WeakMap;o.set(l,0),i.set(a,o);}}this.indexes.set(t,n),this.itemPositions.set(t,i);}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 i=new Set,s=[];for(let r=0;r<t.length;r++){let l=n.get(t[r]);if(l!==void 0)for(let a=0;a<l.length;a++){let u=l[a];i.has(u)||(i.add(u),s.push(u));}}return s}hasIndex(e){return this.indexes.has(e)}addItem(e){for(let t of this.indexes.keys())this.addItemToField(t,e);}removeItem(e){for(let t of this.indexes.keys())this.removeItemFromField(t,e);}clear(){this.indexes.clear(),this.itemPositions.clear();}getIndexMap(e){return this.indexes.get(e)}removeItemFromField(e,t){let n=t[e];if(n==null)return;let i=this.indexes.get(e),s=this.itemPositions.get(e),r=i?.get(n),l=s?.get(n),a=l?.get(t);if(!i||!s||!r||!l||a===void 0)return;let u=r.length-1,o=r[u];a!==u&&(r[a]=o,l.set(o,a)),r.pop(),l.delete(t),r.length===0&&(i.delete(n),s.delete(n));}addItemToField(e,t){let n=t[e];if(n==null)return;let i=this.indexes.get(e),s=this.itemPositions.get(e);if(!i||!s)return;let r=i.get(n),l=s.get(n);if(r&&l){r.push(t),l.set(t,r.length-1);return}i.set(n,[t]);let a=new WeakMap;a.set(t,0),s.set(n,a);}};function g(x){return {value:x,enumerable:false,configurable:true,writable:true}}var y=class{constructor(e){this.callbacks=e;}create(e){let t=e;return Object.defineProperties(t,{filter:g((n,i)=>i===void 0?this.callbacks.filter(e,n):this.callbacks.filter(n,i)),add:g(n=>this.callbacks.add(n)),clearIndexes:g(()=>this.callbacks.clearIndexes()),data:g(n=>this.callbacks.data(n)),getOriginData:g(()=>this.callbacks.getOriginData()),clearData:g(()=>this.callbacks.clearData()),resetFilterState:g(()=>this.callbacks.resetFilterState())}),t}};var v=class x extends Error{constructor(e){super(e),this.name="FilterEngineError";}static missingDatasetForBuildIndex(){return new x("FilterEngine: no dataset in memory.")}static missingDatasetForFilter(){return new x("FilterEngine: no dataset in memory.")}static duplicateMutableExcludeField(e){return new x(`FilterEngine: cannot use mutable exclude on field \`${e}\` because it contains duplicate values.`)}};function m(x){let e=[];for(let t=0;t<x.length;t++){let n=x[t],i=Array.isArray(n.values),s=E(n.exclude),r=s!==null,l=i?R(n.values,s):null;!i&&!r||e.push({field:n.field,values:l?[...l]:[],exclude:s?[...s]:[],hasValues:i,hasExclude:r,includedValues:l,excludedValues:s});}return e}function C(x,e){return !(x.hasValues&&!x.includedValues.has(e)||x.hasExclude&&x.excludedValues.has(e))}function b(x){return x.hasValues&&x.values.length===0}function E(x){return !Array.isArray(x)||x.length===0?null:new Set(x)}function R(x,e){let t=new Set;for(let n=0;n<x.length;n++){let i=x[n];e?.has(i)||t.add(i);}return t}var F=class{constructor(){this.registeredFields=new Set;this.fieldDescriptors=new Map;this.indexes=new Map;this.itemPositions=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(),this.itemPositions.clear();}buildIndexes(e){this.indexes.clear(),this.itemPositions.clear();for(let t of this.registeredFields)this.buildIndex(e,t);}addItems(e){if(!(e.length===0||this.indexes.size===0))for(let t=0;t<e.length;t++)this.addItem(e[t]);}removeItem(e){for(let t of this.indexes.keys())this.removeItemFromIndex(t,e);}filter(e,t,n){let i=this.resolveCriteria(t);if(e.length===0||i.length===0)return e;let s=[],r=[];for(let a=0;a<i.length;a++){let u=i[a];if(this.indexes.has(u.field)){s.push(u);continue}r.push(u);}let l=e;if(s.length>0&&(l=this.filterByIndexes(s,e,n),l.length===0))return l;for(let a=0;a<r.length;a++)if(l=this.filterLinearly(l,r[a]),l.length===0)return l;return l}resolveCriteria(e){if(e.length===0)return [];let t=e[0];return "hasValues"in t&&"hasExclude"in t&&"includedValues"in t?e:m(e)}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:i,nestedKey:s}=n,r=new Map,l=new Map;for(let a=0,u=e.length;a<u;a++){let o=e[a],h=o[i];if(Array.isArray(h))for(let d=0;d<h.length;d++){let c=h[d][s];if(c==null)continue;let f=r.get(c);if(f){f[f.length-1]!==o&&(f.push(o),l.get(c).set(o,f.length-1));continue}r.set(c,[o]);let p=new WeakMap;p.set(o,0),l.set(c,p);}}this.indexes.set(t,r),this.itemPositions.set(t,l);}removeItemFromIndex(e,t){let n=this.fieldDescriptors.get(e),i=this.indexes.get(e),s=this.itemPositions.get(e);if(!n||!i||!s)return;let{collectionKey:r,nestedKey:l}=n,a=t[r];if(!Array.isArray(a)||a.length===0)return;let u=new Set;for(let o=0;o<a.length;o++){let h=a[o][l];h!=null&&u.add(h);}for(let o of u){let h=i.get(o),d=s.get(o),c=d?.get(t);if(!h||!d||c===void 0)continue;let f=h.length-1,p=h[f];c!==f&&(h[c]=p,d.set(p,c)),h.pop(),d.delete(t),h.length===0&&(i.delete(o),s.delete(o));}}addItem(e){for(let t of this.indexes.keys())this.addItemToIndex(t,e);}addItemToIndex(e,t){let n=this.fieldDescriptors.get(e),i=this.indexes.get(e),s=this.itemPositions.get(e);if(!n||!i||!s)return;let{collectionKey:r,nestedKey:l}=n,a=t[r];if(!Array.isArray(a)||a.length===0)return;let u=new Set;for(let o=0;o<a.length;o++){let h=a[o][l];h!=null&&u.add(h);}for(let o of u){let h=i.get(o),d=s.get(o);if(h&&d){h.push(t),d.set(t,h.length-1);continue}i.set(o,[t]);let c=new WeakMap;c.set(t,0),s.set(o,c);}}filterByIndexes(e,t,n){let i=e.filter(o=>o.hasValues),s=e.filter(o=>o.hasExclude),r=t===n?null:new Set(t);if(i.length===0)return this.applyIndexedExclusions(t,s);if(i.length===1){let o=this.indexes.get(i[0].field);if(!o)return [];let h=this.getItemsByValues(o,i[0].values);if(h.length===0)return [];let d=[];for(let c=0;c<h.length;c++){let f=h[c];r&&!r.has(f)||d.push(f);}return this.applyIndexedExclusions(d,s)}let l=i.map(o=>({criterion:o,size:this.estimateIndexSize(o)})).sort((o,h)=>o.size-h.size),a=r,u=[];for(let o=0;o<l.length;o++){let{criterion:h}=l[o],d=this.indexes.get(h.field);if(!d)return [];let c=this.getItemsByValues(d,h.values);if(c.length===0)return [];if(a===null)u=c;else {u=[];for(let f=0;f<c.length;f++){let p=c[f];a.has(p)&&u.push(p);}}if(u.length===0)return [];a=new Set(u);}return this.applyIndexedExclusions(u,s)}getItemsByValues(e,t){if(t.length===1)return e.get(t[0])??[];let n=new Set,i=[];for(let s=0;s<t.length;s++){let r=e.get(t[s]);if(r)for(let l=0;l<r.length;l++){let a=r[l];n.has(a)||(n.add(a),i.push(a));}}return i}estimateIndexSize(e){let t=this.indexes.get(e.field);return t?e.values.reduce((n,i)=>{let s=t.get(i);return s?n+s.length:n},0):1/0}filterLinearly(e,t){let n=this.fieldDescriptors.get(t.field);if(!n)return e;let{collectionKey:i,nestedKey:s}=n,r=[];for(let l=0;l<e.length;l++){let a=e[l],u=a[i];if(!Array.isArray(u))continue;let o=!t.hasValues,h=false;for(let d=0;d<u.length;d++){let c=u[d][s];if(t.hasExclude&&t.excludedValues.has(c)){h=true;break}t.hasValues&&t.includedValues.has(c)&&(o=true);}!h&&o&&r.push(a);}return r}applyIndexedExclusions(e,t){if(t.length===0||e.length===0)return e;let n=new Set;for(let s=0;s<t.length;s++){let r=t[s],l=this.indexes.get(r.field);if(!l)continue;let a=this.getItemsByValues(l,r.exclude);for(let u=0;u<a.length;u++)n.add(a[u]);}if(n.size===0)return e;let i=[];for(let s=0;s<e.length;s++){let r=e[s];n.has(r)||i.push(r);}return i}};var V=class{constructor(e={}){this.dataset=[];this.datasetPositions=new Map;this.hasDuplicateMutableExcludeValues=false;this.indexedFields=new Set;this.nestedCollection=new F;this.previousResult=null;this.previousCriteria=null;this.previousBaseData=null;this.previousResultsByCriteria=new Map;this.chainBuilder=new y({filter:(e,t)=>t===void 0?this.filter(e):this.filter(e,t),getOriginData:()=>this.getOriginData(),add:e=>this.add(e),data:e=>this.data(e),clearIndexes:()=>this.clearIndexes(),clearData:()=>this.clearData(),resetFilterState:()=>this.resetFilterState()});if(this.indexer=new I,this.filterByPreviousResult=e.filterByPreviousResult??false,this.mutableExcludeField=e.mutableExcludeField??null,this.nestedCollection.registerFields(e.nestedFields),!e.data)return;this.dataset=e.data,this.rebuildMutableExcludeState();let t=e.fields?.length,n=this.nestedCollection.hasRegisteredFields();if(t)for(let i of e.fields)this.indexedFields.add(i);(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 v.missingDatasetForBuildIndex();return this.indexer.buildIndex(this.dataset,e),this}return this.dataset=e,this.resetFilterState(),this.rebuildMutableExcludeState(),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.previousResultsByCriteria.clear(),this}clearData(){return this.dataset=[],this.datasetPositions.clear(),this.hasDuplicateMutableExcludeValues=false,this.indexer.clear(),this.nestedCollection.clearIndexes(),this.resetFilterState(),this}data(e){return this.dataset=e,this.rebuildMutableExcludeState(),this.resetFilterState(),this.rebuildConfiguredIndexes(),this}add(e){return this.applyAddedItems(e,true)}applyAddedItems(e,t){if(e.length===0)return this;let n=t?this.dataset.length:this.dataset.length-e.length;return t&&this.dataset.push(...e),this.updateMutableExcludeStateForAddedItems(e,n),this.resetFilterState(),this.indexer.addItems(e),this.nestedCollection.addItems(e),this}getOriginData(){return this.dataset}filter(e,t){return t===void 0?this.withChain(this.rawFilter(e)):this.withChain(this.rawFilter(e,t))}rawFilter(e,t){let n=t===void 0,i,s,r;if(n){if(!this.dataset.length)throw v.missingDatasetForFilter();s=m(e);let d=this.applyMutableExclude(s);if(d!==void 0)return d;if(this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===this.dataset){let c=this.createCriteriaCacheKey(s),f=this.createCriteriaCacheKey(this.previousCriteria),p=this.previousResultsByCriteria.get(c);if(c===f)return this.previousResult;if(p!==void 0)return this.storePreviousResult(p,true,this.dataset,s),p;let T=this.hasCriteriaBacktrack(this.previousCriteria,s)||this.previousResult.length===0&&!this.canApplySequentiallyToEmptyResult(this.previousCriteria,s);i=T?this.dataset:this.previousResult,r=T?s:this.createSequentialExecutionCriteria(this.previousCriteria,s);}else i=this.dataset,r=s;}else if(s=m(t),i=e,this.filterByPreviousResult&&this.previousResult!==null&&this.previousCriteria!==null&&this.previousBaseData===i){let d=this.createCriteriaCacheKey(s),c=this.createCriteriaCacheKey(this.previousCriteria),f=this.previousResultsByCriteria.get(d);if(d===c)return this.previousResult;if(f!==void 0)return this.storePreviousResult(f,false,e,s),f;let p=this.hasCriteriaBacktrack(this.previousCriteria,s)||this.previousResult.length===0&&!this.canApplySequentiallyToEmptyResult(this.previousCriteria,s);i=p?e:this.previousResult,r=p?s:this.createSequentialExecutionCriteria(this.previousCriteria,s);}else r=s;if(s.length===0)return this.filterByPreviousResult&&this.resetFilterState(),n?this.dataset:e;for(let d=0;d<r.length;d++)if(b(r[d]))return this.createEmptyResult(n,e,s);let l=[],a=[];for(let d=0;d<r.length;d++){let c=r[d];this.nestedCollection.hasField(c.field)?l.push(c):a.push(c);}if(l.length>0&&(i=this.nestedCollection.filter(i,l,this.dataset),i.length===0))return this.createEmptyResult(n,e,s);if(a.length===0)return this.storePreviousResult(i,n,e,s),i;let{indexedCriteria:u,linearCriteria:o}=a.reduce((d,c)=>(this.indexer.hasIndex(c.field)?d.indexedCriteria.push(c):d.linearCriteria.push(c),d),{indexedCriteria:[],linearCriteria:[]}),h;if(u.length>0&&o.length===0)return h=this.filterViaIndex(u,i),this.storePreviousResult(h,n,e,s),h;if(u.length>0&&o.length>0){let d=this.filterViaIndex(u,i);return h=this.linearFilter(d,o),this.storePreviousResult(h,n,e,s),h}return h=this.linearFilter(i,a),this.storePreviousResult(h,n,e,s),h}withChain(e){return this.chainBuilder.create(e)}rebuildMutableExcludeState(){if(this.datasetPositions.clear(),this.hasDuplicateMutableExcludeValues=false,this.mutableExcludeField!==null)for(let e=0;e<this.dataset.length;e++){let n=this.dataset[e][this.mutableExcludeField];n!=null&&(this.datasetPositions.has(n)&&(this.hasDuplicateMutableExcludeValues=true),this.datasetPositions.set(n,e));}}updateMutableExcludeStateForAddedItems(e,t){if(this.mutableExcludeField!==null)for(let n=0;n<e.length;n++){let i=e[n][this.mutableExcludeField];i!=null&&(this.datasetPositions.has(i)&&(this.hasDuplicateMutableExcludeValues=true),this.datasetPositions.set(i,t+n));}}applyMutableExclude(e){if(this.mutableExcludeField===null||e.length!==1)return;let t=e[0];if(!(t.field!==this.mutableExcludeField||t.hasValues||!t.hasExclude)){if(this.hasDuplicateMutableExcludeValues)throw v.duplicateMutableExcludeField(this.mutableExcludeField);for(let n=0;n<t.exclude.length;n++)this.removeStoredItem(t.exclude[n]);return this.resetFilterState(),this.dataset}}removeStoredItem(e){let t=this.datasetPositions.get(e);if(t===void 0)return;let n=this.dataset.length-1,i=this.dataset[t],s=this.dataset[n];if(this.indexer.removeItem(i),this.nestedCollection.removeItem(i),t!==n&&(this.dataset[t]=s,this.mutableExcludeField!==null)){let r=s[this.mutableExcludeField];r!=null&&this.datasetPositions.set(r,t);}this.dataset.pop(),this.datasetPositions.delete(e);}linearFilter(e,t){let n=[];for(let i=0;i<e.length;i++){let s=e[i],r=true;for(let l=0;l<t.length;l++){let a=t[l];if(!C(a,s[a.field])){r=false;break}}r&&n.push(s);}return n}filterViaIndex(e,t){let i=t!==this.dataset?new Set(t):null,s=e.filter(d=>d.hasValues),r=e.filter(d=>d.hasExclude);if(s.length===0)return this.applyIndexedExclusions(t,r);if(s.length===1){let d=this.indexer.getByValues(s[0].field,s[0].values),c=[];for(let f=0;f<d.length;f++){let p=d[f];i&&!i.has(p)||C(s[0],p[s[0].field])&&c.push(p);}return this.applyIndexedExclusions(c,r)}let l=s.map(d=>({criterion:d,size:this.estimateIndexSize(d)})).sort((d,c)=>d.size-c.size),{field:a,values:u}=l[0].criterion,o=this.indexer.getByValues(a,u);if(o.length===0)return [];let h=[];for(let d=0;d<o.length;d++){let c=o[d],f=true;if(!(i&&!i.has(c))){for(let p=0;p<l.length;p++){let T=l[p].criterion;if(!C(T,c[T.field])){f=false;break}}f&&h.push(c);}}return this.applyIndexedExclusions(h,r)}estimateIndexSize(e){let t=this.indexer.getIndexMap(e.field);return t?e.values.reduce((n,i)=>{let s=t.get(i);return s?n+s.length:n},0):1/0}applyIndexedExclusions(e,t){if(t.length===0||e.length===0)return e.slice();let n=new Set;for(let s=0;s<t.length;s++){let r=t[s],l=this.indexer.getIndexMap(r.field);if(l)for(let a=0;a<r.exclude.length;a++){let u=l.get(r.exclude[a]);if(u)for(let o=0;o<u.length;o++)n.add(u[o]);}}if(n.size===0)return e.slice();let i=[];for(let s=0;s<e.length;s++){let r=e[s];n.has(r)||i.push(r);}return i}createEmptyResult(e,t,n){let i=[];return this.storePreviousResult(i,e,t,n),i}storePreviousResult(e,t,n,i){this.filterByPreviousResult&&(this.previousResult=e,this.previousCriteria=i,this.previousBaseData=t?this.dataset:n,this.previousResultsByCriteria.set(this.createCriteriaCacheKey(i),e));}createCriteriaCacheKey(e){let t=this.createCriteriaStateMap(e);return JSON.stringify([...t.entries()].sort(([n],[i])=>n.localeCompare(i)).map(([n,i])=>({field:n,hasValues:i.hasValues,hasExclude:i.hasExclude,values:i.values.map(s=>JSON.stringify(s)).sort(),exclude:i.exclude.map(s=>JSON.stringify(s)).sort()})))}createSequentialExecutionCriteria(e,t){let n=new Map;for(let i=0;i<e.length;i++){let s=e[i];n.set(s.field,s);}return t.map(i=>{let s=n.get(i.field);if(!s||!s.hasValues||!i.hasValues||s.hasExclude||i.hasExclude||i.includedValues.size<=s.includedValues.size)return i;for(let l of s.includedValues)if(!i.includedValues.has(l))return i;let r=[];for(let l of i.includedValues)s.includedValues.has(l)||r.push(l);return r.length===0?i:{...i,values:r,includedValues:new Set(r)}})}hasCriteriaBacktrack(e,t){let n=this.createCriteriaStateMap(e),i=this.createCriteriaStateMap(t);for(let[s,r]of n){let l=i.get(s);if(!l||this.isCriterionBacktracked(r,l))return true}return false}canApplySequentiallyToEmptyResult(e,t){let n=this.createCriteriaStateMap(e),i=this.createCriteriaStateMap(t);for(let[s,r]of i){let l=n.get(s);if(l){if(l.hasValues!==r.hasValues&&(l.hasValues||!r.hasValues))return false;if(l.hasValues&&r.hasValues){let a=l.includedValues,u=r.includedValues;if(!this.areSetsEqual(a,u)&&!this.isSubset(a,u))return false}if(l.hasExclude!==r.hasExclude&&(l.hasExclude||!r.hasExclude))return false;if(l.hasExclude&&r.hasExclude){let a=l.excludedValues,u=r.excludedValues;if(!this.areSetsEqual(a,u)&&!this.isSubset(a,u))return false}}}return true}createCriteriaStateMap(e){let t=new Map;for(let n=0;n<e.length;n++){let i=e[n],s=t.get(i.field);if(!s){t.set(i.field,{...i,values:i.values.slice(),exclude:i.exclude.slice(),includedValues:i.includedValues?new Set(i.includedValues):null,excludedValues:i.excludedValues?new Set(i.excludedValues):null});continue}if(i.hasValues)if(!s.hasValues)s.hasValues=true,s.includedValues=new Set(i.includedValues);else for(let r of s.includedValues)i.includedValues.has(r)||s.includedValues.delete(r);if(i.hasExclude)if(!s.hasExclude)s.hasExclude=true,s.excludedValues=new Set(i.excludedValues);else for(let r of i.excludedValues)s.excludedValues.add(r);if(s.hasValues&&s.hasExclude)for(let r of s.excludedValues)s.includedValues.delete(r);s.values=s.includedValues?[...s.includedValues]:[],s.exclude=s.excludedValues?[...s.excludedValues]:[];}return t}isCriterionBacktracked(e,t){if(e.hasValues&&!t.hasValues)return true;if(e.hasValues&&t.hasValues){let n=e.includedValues,i=t.includedValues;if(!this.areSetsEqual(n,i))return this.isSubset(i,n)}if(!e.hasValues&&t.hasValues)return false;if(e.hasExclude&&!t.hasExclude)return true;if(!e.hasExclude&&t.hasExclude)return false;if(e.hasExclude&&t.hasExclude){let n=e.excludedValues,i=t.excludedValues;if(!this.areSetsEqual(n,i))return this.isSubset(i,n)}return false}isSubset(e,t){for(let n of e)if(!t.has(n))return false;return true}areSetsEqual(e,t){if(e===t)return true;if(!e||!t||e.size!==t.size)return false;for(let n of e)if(!t.has(n))return false;return true}};export{V as a};
@@ -0,0 +1,3 @@
1
+ var I=class g extends Error{constructor(e){super(e),this.name="TextSearchEngineError";}static missingDatasetForBuildIndex(){return new g("TextSearchEngine: no dataset in memory.")}};function L(g){let e=g.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 w(g,e){let t=g.get(e);if(t)return t;let s=new Set;return g.set(e,s),s}function x(g){let e=L(g);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 r=Math.round(i*s/n);t.add(e[r]);}return t}function p(g,e,t){for(let s=0,n=e.length;s<n;s++){let i=n-s,r=Math.min(3,i);for(let o=1;o<=r;o++){let a=e.substring(s,s+o);w(g,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);}addItems(e,t){if(!(e.length===0||this.ngramIndexes.size===0))for(let s of this.ngramIndexes.keys())this.addItemsToField(s,e,t);}searchAllIndexedFields(e,t,s){let n=new Set,i=[];for(let r of this.registeredFields)for(let o of this.searchIndexedField(e,r,t,s))n.has(o)||(n.add(o),i.push(o));return i}searchIndexedField(e,t,s,n){let i=this.ngramIndexes.get(t);if(!i)return [];let r=[];for(let d of n){let c=i.get(d);if(!c)return [];r.push(c);}r.sort((d,c)=>d.size-c.size);let o=r[0],a=r.length,l=[],h=this.normalizedFieldValues.get(t);for(let d of o){let c=true;for(let m=1;m<a;m++)if(!r[m].has(d)){c=false;break}if(!c)continue;let u=e[d];!u||!h?.[d]?.includes(s)||l.push(u);}return l}searchFieldLinear(e,t,s){let n=this.fieldDescriptors.get(t);if(!n)return [];let{collectionKey:i,nestedKey:r}=n,o=[];for(let a=0;a<e.length;a++){let l=e[a],h=l[i];if(!Array.isArray(h))continue;let d=false;for(let c=0;c<h.length;c++){let u=h[c][r];if(typeof u=="string"&&u.toLowerCase().includes(s)){d=true;break}}d&&o.push(l);}return o}matchesAnyField(e,t){for(let s of this.registeredFields){let n=this.fieldDescriptors.get(s);if(!n)continue;let{collectionKey:i,nestedKey:r}=n,o=e[i];if(Array.isArray(o))for(let a=0;a<o.length;a++){let l=o[a][r];if(typeof l=="string"&&l.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,r=new Map,o=new Array(e.length);for(let a=0,l=e.length;a<l;a++){let d=e[a][n];if(!Array.isArray(d))continue;let c=[];for(let u=0;u<d.length;u++){let f=d[u][i];if(typeof f!="string")continue;let m=f.toLowerCase();c.push(m),p(r,m,a);}c.length!==0&&(o[a]=c.join(`
2
+ `));}this.ngramIndexes.set(t,r),this.normalizedFieldValues.set(t,o);}addItemsToField(e,t,s){let n=this.fieldDescriptors.get(e),i=this.ngramIndexes.get(e);if(!n||!i)return;let r=this.normalizedFieldValues.get(e)??[],{collectionKey:o,nestedKey:a}=n;for(let l=0;l<t.length;l++){let d=t[l][o];if(!Array.isArray(d))continue;let c=[],u=s+l;for(let f=0;f<d.length;f++){let m=d[f][a];if(typeof m!="string")continue;let F=m.toLowerCase();c.push(F),p(i,F,u);}c.length!==0&&(r[u]=c.join(`
3
+ `));}this.normalizedFieldValues.set(e,r);}};var T=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 I.missingDatasetForBuildIndex();s=this.dataset,n=e;}this.dataset=s;let i=new Map,r=new Array(s.length);for(let o=0,a=s.length;o<a;o++){let l=s[o][n];if(typeof l!="string")continue;let h=l.toLowerCase();r[o]=h,p(i,h,o);}return this.ngramIndexes.set(n,i),this.normalizedFieldValues.set(n,r),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=x(t);if(!s.size)return [];let n=new Set,i=[];for(let r of this.ngramIndexes.keys())for(let o of this.searchFieldWithPreparedQuery(r,t,s))n.has(o)||(n.add(o),i.push(o));for(let r of this.nestedCollection.searchAllIndexedFields(this.dataset,t,s))n.has(r)||(n.add(r),i.push(r));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=x(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=x(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 h of s){let d=n.get(h);if(!d)return [];i.push(d);}i.sort((h,d)=>h.size-d.size);let r=i[0],o=i.length,a=[],l=this.normalizedFieldValues.get(e);for(let h of r){let d=true;for(let f=1;f<o;f++)if(!i[f].has(h)){d=false;break}if(!d)continue;let c=this.dataset[h];if(!c)continue;l?.[h]?.includes(t)&&a.push(c);}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 r in n){let o=n[r];if(typeof o=="string"&&o.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}add(e){return this.applyAddedItems(e,true)}applyAddedItems(e,t){if(e.length===0)return this;let s=t?this.dataset.length:this.dataset.length-e.length;t&&this.dataset.push(...e);for(let n of this.indexedFields)this.addItemsToField(n,e,s);return this.nestedCollection.addItems(e,s),this}clearData(){return this.dataset=[],this.ngramIndexes.clear(),this.normalizedFieldValues.clear(),this.nestedCollection.clearIndexes(),this}addItemsToField(e,t,s){let n=this.ngramIndexes.get(e);if(!n)return;let i=this.normalizedFieldValues.get(e)??[];for(let r=0;r<t.length;r++){let o=t[r][e];if(typeof o!="string")continue;let a=o.toLowerCase(),l=s+r;i[l]=a,p(n,a,l);}this.normalizedFieldValues.set(e,i);}};export{T as a};
@@ -0,0 +1 @@
1
+ var h=class u extends Error{constructor(r){super(r),this.name="SortEngineError";}static missingDatasetForBuildIndex(){return new u("SortEngine: no dataset in memory.")}static missingDatasetForSort(){return new u("SortEngine: no dataset in memory.")}};var g=class{constructor(r={}){this.cache=new Map;this.dataset=[];this.indexedFields=new Set;this.dirtyIndexedFields=new Set;if(r.data&&(this.dataset=r.data,r.fields?.length)){for(let n of r.fields)this.indexedFields.add(n);this.rebuildConfiguredIndexes();}}rebuildConfiguredIndexes(){this.cache.clear();for(let r of this.indexedFields)this.buildIndex(this.dataset,r);}buildIndex(r,n){let s,e;if(Array.isArray(r))s=r,e=n;else {if(!this.dataset.length)throw h.missingDatasetForBuildIndex();s=this.dataset,e=r;}this.dataset=s;let t=s.length,i=new Uint32Array(t),a=new Array(t);for(let d=0;d<t;d++)i[d]=d,a[d]=s[d][e];if(typeof a[0]=="number"){let d=new Float64Array(t);for(let l=0;l<t;l++)d[l]=a[l];i.sort((l,c)=>d[l]-d[c]);}else i.sort((d,l)=>{let c=a[d],f=a[l];return c<f?-1:c>f?1:0});return this.cache.set(e,{indexes:i,dataRef:s,itemCount:t}),this.dirtyIndexedFields.delete(e),this}clearIndexes(){return this.cache.clear(),this.dirtyIndexedFields.clear(),this}clearData(){return this.dataset=[],this.cache.clear(),this.dirtyIndexedFields.clear(),this}data(r){return this.dataset=r,this.dirtyIndexedFields.clear(),this.rebuildConfiguredIndexes(),this}add(r){return this.applyAddedItems(r,true)}applyAddedItems(r,n){if(r.length===0)return this;let s=n?this.dataset.length:this.dataset.length-r.length;n&&this.dataset.push(...r);for(let e of this.indexedFields){let t=this.cache.get(e);if(t){if(this.dirtyIndexedFields.has(e)||t.dataRef!==this.dataset||t.itemCount!==s){this.dirtyIndexedFields.add(e);continue}this.updateCachedIndexForAddedItems(e,s,r.length);}}return this}getOriginData(){return this.dataset}sort(r,n,s=false){let e,t,i=n===void 0;if(n===void 0){if(!this.dataset.length)throw h.missingDatasetForSort();e=this.dataset,t=r;}else e=r,t=n;if(t.length===0||e.length===0)return e;if(t.length===1){let{field:d,direction:l}=t[0];i&&this.dirtyIndexedFields.has(d)&&this.cache.has(d)&&this.buildIndex(d);let c=this.cache.get(d);if(i&&c?.dataRef===e&&c.itemCount===e.length)return this.reconstructFromIndex(e,c.indexes,l)}let a=s?e:e.slice();if(t.length===1&&e.length>0&&typeof e[0][t[0].field]=="number")return this.radixSortNumeric(a,t[0].field,t[0].direction);let o=this.buildComparator(t);return a.sort(o),a}reconstructFromIndex(r,n,s){let e=r.length,t=new Array(e);if(s==="asc")for(let i=0;i<e;i++)t[i]=r[n[i]];else for(let i=0;i<e;i++)t[i]=r[n[e-1-i]];return t}updateCachedIndexForAddedItems(r,n,s){let e=this.cache.get(r);if(!e)return;let t=this.buildSortedIndexesForRange(r,n,s);this.cache.set(r,{indexes:this.mergeSortedIndexes(r,e.indexes,t),dataRef:this.dataset,itemCount:n+s}),this.dirtyIndexedFields.delete(r);}buildSortedIndexesForRange(r,n,s){let e=new Uint32Array(s);for(let t=0;t<s;t++)e[t]=n+t;return e.sort((t,i)=>this.compareIndexesByField(r,t,i)),e}mergeSortedIndexes(r,n,s){let e=new Uint32Array(n.length+s.length),t=0,i=0,a=0;for(;t<n.length&&i<s.length;){if(this.compareIndexesByField(r,n[t],s[i])<=0){e[a++]=n[t++];continue}e[a++]=s[i++];}for(;t<n.length;)e[a++]=n[t++];for(;i<s.length;)e[a++]=s[i++];return e}compareIndexesByField(r,n,s){let e=this.dataset[n]?.[r],t=this.dataset[s]?.[r];if(typeof e=="number"&&typeof t=="number"){let i=e-t;if(i!==0)return i}else {if(e<t)return -1;if(e>t)return 1}return n-s}buildComparator(r){let n=r.map(({field:t})=>t),s=r.map(({direction:t})=>t==="asc"?1:-1),e=n.length;return (t,i)=>{for(let a=0;a<e;a++){let o=t[n[a]],d=i[n[a]];if(o<d)return -s[a];if(o>d)return s[a]}return 0}}radixSortNumeric(r,n,s){let e=r.length,t=new Float64Array(e);for(let o=0;o<e;o++)t[o]=r[o][n];let i=new Uint32Array(e);for(let o=0;o<e;o++)i[o]=o;i.sort((o,d)=>t[o]-t[d]);let a=new Array(e);if(s==="asc")for(let o=0;o<e;o++)a[o]=r[i[o]];else for(let o=0;o<e;o++)a[e-1-o]=r[i[o]];return a}};export{g as a};
@@ -1,10 +1,11 @@
1
- import { C as CollectionItem, F as FilterCriterion } from '../types-DuQWbqOC.mjs';
2
- export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
1
+ import { C as CollectionItem, F as FilterCriterion } from '../types-4YEflcxU.mjs';
2
+ export { I as IndexableKey } from '../types-4YEflcxU.mjs';
3
3
 
4
4
  interface FilterEngineChain<T extends CollectionItem> {
5
5
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
6
6
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
7
7
  getOriginData(): T[];
8
+ add(items: T[]): FilterEngine<T>;
8
9
  data(data: T[]): FilterEngine<T>;
9
10
  clearIndexes(): FilterEngine<T>;
10
11
  clearData(): FilterEngine<T>;
@@ -12,13 +13,12 @@ interface FilterEngineChain<T extends CollectionItem> {
12
13
  }
13
14
  interface FilterEngineOptions<T extends CollectionItem = CollectionItem> {
14
15
  data?: T[];
16
+ mutableExcludeField?: keyof T & string;
15
17
  fields?: (keyof T & string)[];
16
18
  nestedFields?: string[];
17
19
  filterByPreviousResult?: boolean;
18
20
  }
19
21
 
20
- declare const FILTER_ENGINE_EXECUTE: unique symbol;
21
-
22
22
  /**
23
23
  * FilterEngine class for filtering collections by multiple criteria,
24
24
  * supporting indexed lookups and linear scans for large datasets.
@@ -27,12 +27,16 @@ declare const FILTER_ENGINE_EXECUTE: unique symbol;
27
27
  declare class FilterEngine<T extends CollectionItem> {
28
28
  private indexer;
29
29
  private readonly filterByPreviousResult;
30
+ private readonly mutableExcludeField;
30
31
  private dataset;
32
+ private readonly datasetPositions;
33
+ private hasDuplicateMutableExcludeValues;
31
34
  private readonly indexedFields;
32
35
  private readonly nestedCollection;
33
36
  private previousResult;
34
37
  private previousCriteria;
35
38
  private previousBaseData;
39
+ private readonly previousResultsByCriteria;
36
40
  private readonly chainBuilder;
37
41
  /**
38
42
  * Creates a new FilterEngine with optional data and fields to index.
@@ -47,28 +51,21 @@ declare class FilterEngine<T extends CollectionItem> {
47
51
  resetFilterState(): this;
48
52
  clearData(): this;
49
53
  data(data: T[]): this;
54
+ add(items: T[]): this;
55
+ private applyAddedItems;
50
56
  getOriginData(): T[];
51
57
  /**
52
58
  * Filters the data based on the given criteria.
53
59
  */
54
60
  filter(criteria: FilterCriterion<T>[]): T[] & FilterEngineChain<T>;
55
61
  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[];
62
+ rawFilter(criteria: FilterCriterion<T>[]): T[];
63
+ rawFilter(data: T[], criteria: FilterCriterion<T>[]): T[];
58
64
  private withChain;
59
- private cloneCriteria;
60
- /**
61
- * Checks if there are new criteria added.
62
- */
63
- private hasCriteriaAdditions;
64
- /**
65
- * Checks if there are criteria removed.
66
- */
67
- private hasCriteriaRemovals;
68
- /**
69
- * Gets the newly added criteria.
70
- */
71
- private getAddedCriteria;
65
+ private rebuildMutableExcludeState;
66
+ private updateMutableExcludeStateForAddedItems;
67
+ private applyMutableExclude;
68
+ private removeStoredItem;
72
69
  /**
73
70
  * Filters data linearly without index.
74
71
  */
@@ -81,6 +78,17 @@ declare class FilterEngine<T extends CollectionItem> {
81
78
  * Estimates the size of the index for a criterion.
82
79
  */
83
80
  private estimateIndexSize;
81
+ private applyIndexedExclusions;
82
+ private createEmptyResult;
83
+ private storePreviousResult;
84
+ private createCriteriaCacheKey;
85
+ private createSequentialExecutionCriteria;
86
+ private hasCriteriaBacktrack;
87
+ private canApplySequentiallyToEmptyResult;
88
+ private createCriteriaStateMap;
89
+ private isCriterionBacktracked;
90
+ private isSubset;
91
+ private areSetsEqual;
84
92
  }
85
93
 
86
94
  export { CollectionItem, FilterCriterion, FilterEngine, type FilterEngineChain, type FilterEngineOptions };
@@ -1 +1 @@
1
- export{b as FilterEngine}from'../chunk-5B7Y4C22.mjs';
1
+ export{a as FilterEngine}from'../chunk-U3IU4NI6.mjs';
package/dist/index.d.mts CHANGED
@@ -2,4 +2,6 @@ export { TextSearchEngine } from './search/index.mjs';
2
2
  export { FilterEngine } from './filter/index.mjs';
3
3
  export { SortEngine } from './sort/index.mjs';
4
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';
5
+ export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-4YEflcxU.mjs';
6
+ import './types-MIbN5Q4o.mjs';
7
+ import './types-C7pwTB0o.mjs';
package/dist/index.mjs CHANGED
@@ -1 +1 @@
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
+ export{a as MergeEngines}from'./chunk-5FVJLC7Y.mjs';export{a as TextSearchEngine}from'./chunk-Y7NSBYLS.mjs';export{a as FilterEngine}from'./chunk-U3IU4NI6.mjs';export{a as SortEngine}from'./chunk-Z4QO4GOM.mjs';
@@ -1,5 +1,8 @@
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';
1
+ import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-4YEflcxU.mjs';
2
+ export { I as IndexableKey, a as SortDirection } from '../types-4YEflcxU.mjs';
3
+ import { FilterEngineOptions } from '../filter/index.mjs';
4
+ import { T as TextSearchEngineOptions } from '../types-MIbN5Q4o.mjs';
5
+ import { S as SortEngineOptions } from '../types-C7pwTB0o.mjs';
3
6
 
4
7
  type MergeModuleName = "search" | "sort" | "filter";
5
8
  interface MergeEnginesChain<T extends CollectionItem> {
@@ -10,6 +13,7 @@ interface MergeEnginesChain<T extends CollectionItem> {
10
13
  filter(criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
11
14
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
12
15
  getOriginData(): T[];
16
+ add(items: T[]): MergeEngines<T>;
13
17
  data(data: T[]): MergeEngines<T>;
14
18
  clearIndexes(module: MergeModuleName): T[] & MergeEnginesChain<T>;
15
19
  clearData(module: MergeModuleName): T[] & MergeEnginesChain<T>;
@@ -25,6 +29,9 @@ interface EngineApi {
25
29
  interface MergeEnginesOptions<T extends CollectionItem> {
26
30
  imports: EngineConstructor[];
27
31
  data: T[];
32
+ search?: TextSearchEngineOptions<T>;
33
+ filter?: FilterEngineOptions<T>;
34
+ sort?: SortEngineOptions<T>;
28
35
  [key: string]: unknown;
29
36
  }
30
37
 
@@ -39,8 +46,12 @@ declare class MergeEngines<T extends CollectionItem> {
39
46
  private readonly filterModule;
40
47
  private readonly clearIndexMethods;
41
48
  private readonly clearDataMethods;
49
+ private readonly addMethods;
50
+ private readonly getOriginDataMethods;
42
51
  private readonly setDataMethods;
43
52
  private readonly getOriginDataMethod;
53
+ private previousSearchState;
54
+ private previousSortState;
44
55
  private readonly chainBuilder;
45
56
  /**
46
57
  * Creates a new MergeEngines instance with the given options.
@@ -52,6 +63,9 @@ declare class MergeEngines<T extends CollectionItem> {
52
63
  */
53
64
  private getModuleInitOptions;
54
65
  private isRecord;
66
+ private clearOperationState;
67
+ private createSearchCacheKey;
68
+ private createSortCacheKey;
55
69
  search(query: string): T[] & MergeEnginesChain<T>;
56
70
  search(field: (keyof T & string) | (string & {}), query: string): T[] & MergeEnginesChain<T>;
57
71
  sort(descriptors: SortDescriptor<T>[]): T[] & MergeEnginesChain<T>;
@@ -60,6 +74,7 @@ declare class MergeEngines<T extends CollectionItem> {
60
74
  filter(data: T[], criteria: FilterCriterion<T>[]): T[] & MergeEnginesChain<T>;
61
75
  private withChain;
62
76
  getOriginData(): T[];
77
+ add(items: T[]): this;
63
78
  clearIndexes(module: MergeModuleName): this;
64
79
  data(data: T[]): this;
65
80
  clearData(module: MergeModuleName): this;
@@ -1 +1 @@
1
- export{a as MergeEngines}from'../chunk-LKNVBEFD.mjs';import'../chunk-4PTQMQ73.mjs';import'../chunk-5B7Y4C22.mjs';import'../chunk-IYELLKOW.mjs';
1
+ export{a as MergeEngines}from'../chunk-5FVJLC7Y.mjs';import'../chunk-Y7NSBYLS.mjs';import'../chunk-U3IU4NI6.mjs';import'../chunk-Z4QO4GOM.mjs';
@@ -1,12 +1,6 @@
1
- import { C as CollectionItem } from '../types-DuQWbqOC.mjs';
2
- export { I as IndexableKey } from '../types-DuQWbqOC.mjs';
3
-
4
- interface TextSearchEngineOptions<T extends CollectionItem = CollectionItem> {
5
- data?: T[];
6
- fields?: (keyof T & string)[];
7
- nestedFields?: string[];
8
- minQueryLength?: number;
9
- }
1
+ import { C as CollectionItem } from '../types-4YEflcxU.mjs';
2
+ export { I as IndexableKey } from '../types-4YEflcxU.mjs';
3
+ import { T as TextSearchEngineOptions } from '../types-MIbN5Q4o.mjs';
10
4
 
11
5
  /**
12
6
  * TextSearchEngine class for performing fast substring search on string fields
@@ -55,7 +49,10 @@ declare class TextSearchEngine<T extends CollectionItem> {
55
49
  clearIndexes(): this;
56
50
  getOriginData(): T[];
57
51
  data(data: T[]): this;
52
+ add(items: T[]): this;
53
+ private applyAddedItems;
58
54
  clearData(): this;
55
+ private addItemsToField;
59
56
  }
60
57
 
61
- export { CollectionItem, TextSearchEngine, type TextSearchEngineOptions };
58
+ export { CollectionItem, TextSearchEngine, TextSearchEngineOptions };
@@ -1 +1 @@
1
- export{a as TextSearchEngine}from'../chunk-4PTQMQ73.mjs';
1
+ export{a as TextSearchEngine}from'../chunk-Y7NSBYLS.mjs';
@@ -1,10 +1,6 @@
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
- }
1
+ import { C as CollectionItem, S as SortDescriptor } from '../types-4YEflcxU.mjs';
2
+ export { a as SortDirection } from '../types-4YEflcxU.mjs';
3
+ import { S as SortEngineOptions } from '../types-C7pwTB0o.mjs';
8
4
 
9
5
  /**
10
6
  * SortEngine class for sorting large datasets using pre-built indexes
@@ -15,6 +11,7 @@ declare class SortEngine<T extends CollectionItem> {
15
11
  private cache;
16
12
  private dataset;
17
13
  private readonly indexedFields;
14
+ private readonly dirtyIndexedFields;
18
15
  /**
19
16
  * Creates a new SortEngine with optional data and fields to index.
20
17
  */
@@ -30,6 +27,8 @@ declare class SortEngine<T extends CollectionItem> {
30
27
  clearIndexes(): this;
31
28
  clearData(): this;
32
29
  data(data: T[]): this;
30
+ add(items: T[]): this;
31
+ private applyAddedItems;
33
32
  getOriginData(): T[];
34
33
  /**
35
34
  * Sorts the data based on the given descriptors.
@@ -40,6 +39,10 @@ declare class SortEngine<T extends CollectionItem> {
40
39
  * Reconstructs the sorted array from the cached index.
41
40
  */
42
41
  private reconstructFromIndex;
42
+ private updateCachedIndexForAddedItems;
43
+ private buildSortedIndexesForRange;
44
+ private mergeSortedIndexes;
45
+ private compareIndexesByField;
43
46
  /**
44
47
  * Builds a comparator function for sorting.
45
48
  */
@@ -50,4 +53,4 @@ declare class SortEngine<T extends CollectionItem> {
50
53
  private radixSortNumeric;
51
54
  }
52
55
 
53
- export { CollectionItem, SortDescriptor, SortEngine, type SortEngineOptions };
56
+ export { CollectionItem, SortDescriptor, SortEngine, SortEngineOptions };
@@ -1 +1 @@
1
- export{a as SortEngine}from'../chunk-IYELLKOW.mjs';
1
+ export{a as SortEngine}from'../chunk-Z4QO4GOM.mjs';
@@ -4,7 +4,8 @@ type IndexableKey<T> = {
4
4
  }[keyof T];
5
5
  interface FilterCriterion<T extends CollectionItem> {
6
6
  field: (keyof T & string) | (string & {});
7
- values: any[];
7
+ values?: any[];
8
+ exclude?: any[];
8
9
  }
9
10
  type SortDirection = "asc" | "desc";
10
11
  interface SortDescriptor<T extends CollectionItem> {
@@ -0,0 +1,8 @@
1
+ import { C as CollectionItem } from './types-4YEflcxU.mjs';
2
+
3
+ interface SortEngineOptions<T extends CollectionItem = CollectionItem> {
4
+ data?: T[];
5
+ fields?: (keyof T & string)[];
6
+ }
7
+
8
+ export type { SortEngineOptions as S };
@@ -0,0 +1,10 @@
1
+ import { C as CollectionItem } from './types-4YEflcxU.mjs';
2
+
3
+ interface TextSearchEngineOptions<T extends CollectionItem = CollectionItem> {
4
+ data?: T[];
5
+ fields?: (keyof T & string)[];
6
+ nestedFields?: string[];
7
+ minQueryLength?: number;
8
+ }
9
+
10
+ export type { TextSearchEngineOptions as T };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devisfuture/mega-collection",
3
- "version": "2.1.4",
3
+ "version": "2.3.0",
4
4
  "description": "High-performance search, filter & sort engine for 100K+ item collections in JavaScript/TypeScript",
5
5
  "exports": {
6
6
  ".": {
@@ -1,2 +0,0 @@
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};
@@ -1 +0,0 @@
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};
@@ -1 +0,0 @@
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};
@@ -1 +0,0 @@
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};