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