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