@devisfuture/mega-collection 1.0.18 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +18 -0
- package/dist/chunk-KMSFPZKF.mjs +1 -0
- package/dist/chunk-VSCCECO2.mjs +1 -0
- package/dist/index.d.mts +5 -44
- package/dist/index.mjs +1 -1
- package/dist/merge/index.d.mts +38 -5
- package/dist/merge/index.mjs +1 -1
- package/dist/search/index.d.mts +4 -0
- package/dist/search/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-4TBU4VWK.mjs +0 -1
- package/dist/chunk-OE5GVXOE.mjs +0 -1
package/README.md
CHANGED
|
@@ -60,6 +60,12 @@ interface User {
|
|
|
60
60
|
Use `MergeEngines` to combine search, filter and sort around a single shared dataset.
|
|
61
61
|
Declare which engines you need in `imports` — only those are initialised.
|
|
62
62
|
|
|
63
|
+
Each engine accepts an optional `fields` array (set via the `search`,
|
|
64
|
+
`filter` or `sort` option) which tells it which properties should be indexed up
|
|
65
|
+
front. Indexes power the fast paths used throughout the library; you can leave
|
|
66
|
+
these options out and everything still works, but the code will fall back to
|
|
67
|
+
simple linear scans.
|
|
68
|
+
|
|
63
69
|
```ts
|
|
64
70
|
import { MergeEngines } from "@devisfuture/mega-collection";
|
|
65
71
|
import { TextSearchEngine } from "@devisfuture/mega-collection/search";
|
|
@@ -87,6 +93,9 @@ engine.filter([{ field: "city", values: ["Kyiv", "Lviv"] }]);
|
|
|
87
93
|
```ts
|
|
88
94
|
import { TextSearchEngine } from "@devisfuture/mega-collection/search";
|
|
89
95
|
|
|
96
|
+
// `fields` tells the engine which properties to index for fast lookups. The
|
|
97
|
+
// index is built once during construction; if you omit `fields` the engine
|
|
98
|
+
// still works but every search will scan the entire dataset.
|
|
90
99
|
const engine = new TextSearchEngine<User>({
|
|
91
100
|
data: users,
|
|
92
101
|
fields: ["name", "city"],
|
|
@@ -102,6 +111,10 @@ engine.search("name", "john"); // searches a specific field
|
|
|
102
111
|
```ts
|
|
103
112
|
import { FilterEngine } from "@devisfuture/mega-collection/filter";
|
|
104
113
|
|
|
114
|
+
// `fields` config tells the filter engine which properties should have an
|
|
115
|
+
// index built. Indexed lookups are O(1) per value, so multi-criteria queries
|
|
116
|
+
// can be orders of magnitude faster. Without `fields` the engine still filters
|
|
117
|
+
// correctly but always does a linear scan.
|
|
105
118
|
const engine = new FilterEngine<User>({
|
|
106
119
|
data: users,
|
|
107
120
|
fields: ["city", "age"],
|
|
@@ -125,6 +138,11 @@ const byCityAndAge = engine.filter([{ field: "age", values: [22] }]);
|
|
|
125
138
|
```ts
|
|
126
139
|
import { SortEngine } from "@devisfuture/mega-collection/sort";
|
|
127
140
|
|
|
141
|
+
// `fields` instructs the engine to pre-build a sorted index for each property.
|
|
142
|
+
// When you later run a single-field sort the result can be pulled directly
|
|
143
|
+
// from that index in linear time. If you leave out `fields` the engine still
|
|
144
|
+
// sorts correctly, it merely falls back to standard `Array.prototype.sort`
|
|
145
|
+
// (O(n log n)).
|
|
128
146
|
const engine = new SortEngine<User>({
|
|
129
147
|
data: users,
|
|
130
148
|
fields: ["age", "name", "city"],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var l=class{constructor(n){let{imports:t,data:e,...i}=n,s=new Set(t),r={};for(let o of s){let d=o.prototype,a=this.getMethodNames(d);if(a.length===0)continue;let u=this.getModuleInitOptions(o.name,a,i),g=new o({data:e,...u});for(let c of a)r[c]||this.hasMethod(g,c)&&(r[c]=g[c].bind(g));}this.engine=Object.keys(r).length>0?r:null;}getModuleInitOptions(n,t,e){let i={},s=e[n];this.isRecord(s)&&Object.assign(i,s);for(let r of t){let o=e[r];this.isRecord(o)&&Object.assign(i,o);}return i}getMethodNames(n){let t=n;return Object.getOwnPropertyNames(t).filter(e=>e==="constructor"?false:typeof t[e]=="function")}hasMethod(n,t){return typeof n=="object"&&n!==null&&typeof n[t]=="function"}isRecord(n){return typeof n=="object"&&n!==null}callEngineMethod(n,t){let e=this.engine?.[n];if(!e)throw new Error(`MergeEngines: Method "${n}" is not available. Add module with method "${n}" to the \`imports\` array.`);return e(...t)}search(n,t){if(!this.engine?.search)throw new Error("MergeEngines: TextSearchEngine is not available. Add TextSearchEngine to the `imports` array.");return t===void 0?this.callEngineMethod("search",[n]):this.callEngineMethod("search",[n,t])}sort(n,t,e){if(!this.engine?.sort)throw new Error("MergeEngines: SortEngine is not available. Add SortEngine to the `imports` array.");return t===void 0?this.callEngineMethod("sort",[n]):this.callEngineMethod("sort",[n,t,e])}filter(n,t){if(!this.engine?.filter)throw new Error("MergeEngines: FilterEngine is not available. Add FilterEngine to the `imports` array.");return t===void 0?this.callEngineMethod("filter",[n]):this.callEngineMethod("filter",[n,t])}};export{l as a};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var T=3,f=12;function x(h){let e=h.toLowerCase(),n=Math.min(T,e.length),t=e.length-n+1,r=new Array(t);for(let s=0;s<t;s++)r[s]=e.substring(s,s+n);return r}function m(h){let e=x(h);if(e.length<=f)return new Set(e);let n=new Set,t=e.length-1,r=f-1;for(let s=0;s<=r;s++){let i=Math.round(s*t/r);n.add(e[i]);}return n}function L(h,e){let n=h.get(e);if(n)return n;let t=new Set;return h.set(e,t),t}var y=class{constructor(e={}){this.ngramIndexes=new Map;this.data=[];if(this.minQueryLength=e.minQueryLength??1,!!e.data&&(this.data=e.data,!!e.fields?.length))for(let n of e.fields)this.buildIndex(e.data,n);}buildIndex(e,n){let t,r;if(Array.isArray(e))t=e,r=n;else {if(!this.data.length)throw new Error("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");t=this.data,r=e;}this.data=t;let s=new Map;for(let i=0,u=t.length;i<u;i++){let l=t[i][r];if(typeof l!="string")continue;let o=l.toLowerCase();for(let a=0,d=o.length;a<d;a++){let c=d-a,p=Math.min(T,c);for(let g=1;g<=p;g++){let I=o.substring(a,a+g);L(s,I).add(i);}}}return this.ngramIndexes.set(r,s),this}search(e,n){return n===void 0?this.searchAllFields(e):this.searchField(e,n)}normalizeQuery(e){return e.trim().toLowerCase()}isQuerySearchable(e){return !(!e||e.length<this.minQueryLength)}searchAllFields(e){let n=[...this.ngramIndexes.keys()],t=this.normalizeQuery(e);if(!this.isQuerySearchable(t))return [];if(!n.length)return this.searchAllFieldsLinear(t);let r=m(t);if(!r.size)return [];let s=new Set,i=[];for(let u of n)for(let l of this.searchFieldWithPreparedQuery(u,t,r))s.has(l)||(s.add(l),i.push(l));return i}searchField(e,n){let t=this.normalizeQuery(n);if(!this.isQuerySearchable(t))return [];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 s=[];for(let o of t){let a=r.get(o);if(!a)return [];s.push(a);}s.sort((o,a)=>o.size-a.size);let i=s[0],u=s.length,l=[];for(let o of i){let a=true;for(let c=1;c<u;c++)if(!s[c].has(o)){a=false;break}if(!a)continue;let d=this.data[o][e];typeof d=="string"&&d.toLowerCase().includes(n)&&l.push(this.data[o]);}return l}searchAllFieldsLinear(e){if(!this.data.length)return [];let n=[];for(let t=0;t<this.data.length;t++){let r=this.data[t],s=false;for(let i of Object.values(r))if(typeof i=="string"&&i.toLowerCase().includes(e)){s=true;break}s&&n.push(r);}return n}searchFieldLinear(e,n){if(!this.data.length)return [];let t=[];for(let r=0;r<this.data.length;r++){let s=this.data[r][e];typeof s=="string"&&s.toLowerCase().includes(n)&&t.push(this.data[r]);}return t}clear(){this.ngramIndexes.clear(),this.data=[];}};export{y as a};
|
package/dist/index.d.mts
CHANGED
|
@@ -1,44 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
export { I as IndexableKey, a as SortDirection } from './types-D24zQWME.mjs';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* MergeEngines class that provides a unified interface for text search,
|
|
9
|
-
* sorting, and filtering operations on collections.
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
interface MergeSearchConfig<T extends CollectionItem> {
|
|
13
|
-
fields: (keyof T & string)[];
|
|
14
|
-
minQueryLength?: TextSearchEngineOptions<T>["minQueryLength"];
|
|
15
|
-
}
|
|
16
|
-
interface MergeFilterConfig<T extends CollectionItem> {
|
|
17
|
-
fields: (keyof T & string)[];
|
|
18
|
-
filterByPreviousResult?: boolean;
|
|
19
|
-
}
|
|
20
|
-
interface MergeSortConfig<T extends CollectionItem> {
|
|
21
|
-
fields: (keyof T & string)[];
|
|
22
|
-
}
|
|
23
|
-
type EngineConstructor = typeof TextSearchEngine | typeof SortEngine | typeof FilterEngine;
|
|
24
|
-
interface MergeEnginesOptions<T extends CollectionItem> {
|
|
25
|
-
imports: EngineConstructor[];
|
|
26
|
-
data: T[];
|
|
27
|
-
search?: MergeSearchConfig<T>;
|
|
28
|
-
filter?: MergeFilterConfig<T>;
|
|
29
|
-
sort?: MergeSortConfig<T>;
|
|
30
|
-
}
|
|
31
|
-
declare class MergeEngines<T extends CollectionItem> {
|
|
32
|
-
private readonly searchEngine;
|
|
33
|
-
private readonly sortEngine;
|
|
34
|
-
private readonly filterEngine;
|
|
35
|
-
constructor(options: MergeEnginesOptions<T>);
|
|
36
|
-
search(query: string): T[];
|
|
37
|
-
search(field: keyof T & string, query: string): T[];
|
|
38
|
-
sort(descriptors: SortDescriptor<T>[]): T[];
|
|
39
|
-
sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
|
|
40
|
-
filter(criteria: FilterCriterion<T>[]): T[];
|
|
41
|
-
filter(data: T[], criteria: FilterCriterion<T>[]): T[];
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export { CollectionItem, type EngineConstructor, FilterCriterion, FilterEngine, MergeEngines, type MergeEnginesOptions, type MergeFilterConfig, type MergeSearchConfig, type MergeSortConfig, SortDescriptor, SortEngine, TextSearchEngine };
|
|
1
|
+
export { TextSearchEngine } from './search/index.mjs';
|
|
2
|
+
export { FilterEngine } from './filter/index.mjs';
|
|
3
|
+
export { SortEngine } from './sort/index.mjs';
|
|
4
|
+
export { EngineApi, EngineConstructor, MergeEngines, MergeEnginesOptions } from './merge/index.mjs';
|
|
5
|
+
export { C as CollectionItem, F as FilterCriterion, I as IndexableKey, S as SortDescriptor, a as SortDirection } from './types-D24zQWME.mjs';
|
package/dist/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as
|
|
1
|
+
export{a as TextSearchEngine}from'./chunk-VSCCECO2.mjs';export{a as FilterEngine}from'./chunk-XFQ56UZU.mjs';export{a as SortEngine}from'./chunk-DBAABXBP.mjs';export{a as MergeEngines}from'./chunk-KMSFPZKF.mjs';
|
package/dist/merge/index.d.mts
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { C as CollectionItem, S as SortDescriptor, F as FilterCriterion } from '../types-D24zQWME.mjs';
|
|
2
|
+
export { I as IndexableKey, a as SortDirection } from '../types-D24zQWME.mjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* MergeEngines class that provides a unified interface for text search,
|
|
6
|
+
* sorting, and filtering operations on collections.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
interface EngineConstructor {
|
|
10
|
+
new (options: Record<string, unknown>): object;
|
|
11
|
+
prototype: object;
|
|
12
|
+
name: string;
|
|
13
|
+
}
|
|
14
|
+
interface EngineApi {
|
|
15
|
+
[methodName: string]: ((...args: unknown[]) => unknown) | undefined;
|
|
16
|
+
}
|
|
17
|
+
interface MergeEnginesOptions<T extends CollectionItem> {
|
|
18
|
+
imports: EngineConstructor[];
|
|
19
|
+
data: T[];
|
|
20
|
+
[key: string]: unknown;
|
|
21
|
+
}
|
|
22
|
+
declare class MergeEngines<T extends CollectionItem> {
|
|
23
|
+
private readonly engine;
|
|
24
|
+
constructor(options: MergeEnginesOptions<T>);
|
|
25
|
+
private getModuleInitOptions;
|
|
26
|
+
private getMethodNames;
|
|
27
|
+
private hasMethod;
|
|
28
|
+
private isRecord;
|
|
29
|
+
private callEngineMethod;
|
|
30
|
+
search(query: string): T[];
|
|
31
|
+
search(field: keyof T & string, query: string): T[];
|
|
32
|
+
sort(descriptors: SortDescriptor<T>[]): T[];
|
|
33
|
+
sort(data: T[], descriptors: SortDescriptor<T>[], inPlace?: boolean): T[];
|
|
34
|
+
filter(criteria: FilterCriterion<T>[]): T[];
|
|
35
|
+
filter(data: T[], criteria: FilterCriterion<T>[]): T[];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { CollectionItem, type EngineApi, type EngineConstructor, FilterCriterion, MergeEngines, type MergeEnginesOptions, SortDescriptor };
|
package/dist/merge/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as MergeEngines}from'../chunk-
|
|
1
|
+
export{a as MergeEngines}from'../chunk-KMSFPZKF.mjs';
|
package/dist/search/index.d.mts
CHANGED
|
@@ -19,9 +19,13 @@ declare class TextSearchEngine<T extends CollectionItem> {
|
|
|
19
19
|
private buildIndex;
|
|
20
20
|
search(query: string): T[];
|
|
21
21
|
search(field: keyof T & string, query: string): T[];
|
|
22
|
+
private normalizeQuery;
|
|
23
|
+
private isQuerySearchable;
|
|
22
24
|
private searchAllFields;
|
|
23
25
|
private searchField;
|
|
24
26
|
private searchFieldWithPreparedQuery;
|
|
27
|
+
private searchAllFieldsLinear;
|
|
28
|
+
private searchFieldLinear;
|
|
25
29
|
clear(): void;
|
|
26
30
|
}
|
|
27
31
|
|
package/dist/search/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export{a as TextSearchEngine}from'../chunk-
|
|
1
|
+
export{a as TextSearchEngine}from'../chunk-VSCCECO2.mjs';
|
package/package.json
CHANGED
package/dist/chunk-4TBU4VWK.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
var T=3,f=12;function I(d){let t=d.toLowerCase(),n=Math.min(T,t.length),e=t.length-n+1,s=new Array(e);for(let r=0;r<e;r++)s[r]=t.substring(r,r+n);return s}function m(d){let t=I(d);if(t.length<=f)return new Set(t);let n=new Set,e=t.length-1,s=f-1;for(let r=0;r<=s;r++){let a=Math.round(r*e/s);n.add(t[a]);}return n}function L(d,t){let n=d.get(t);if(n)return n;let e=new Set;return d.set(t,e),e}var y=class{constructor(t={}){this.ngramIndexes=new Map;this.data=[];if(this.minQueryLength=t.minQueryLength??1,!!t.data&&(this.data=t.data,!!t.fields?.length))for(let n of t.fields)this.buildIndex(t.data,n);}buildIndex(t,n){let e,s;if(Array.isArray(t))e=t,s=n;else {if(!this.data.length)throw new Error("TextSearchEngine: no dataset in memory. Either pass `data` in the constructor options, or call buildIndex(data, field).");e=this.data,s=t;}this.data=e;let r=new Map;for(let a=0,l=e.length;a<l;a++){let g=e[a][s];if(typeof g!="string")continue;let o=g.toLowerCase();for(let i=0,u=o.length;i<u;i++){let c=u-i,p=Math.min(T,c);for(let h=1;h<=p;h++){let x=o.substring(i,i+h);L(r,x).add(a);}}}return this.ngramIndexes.set(s,r),this}search(t,n){return n===void 0?this.searchAllFields(t):this.searchField(t,n)}searchAllFields(t){let n=[...this.ngramIndexes.keys()];if(!n.length)return [];let e=t.trim().toLowerCase();if(!e)return [];if(e.length<this.minQueryLength)return [];let s=m(e);if(!s.size)return [];let r=new Set,a=[];for(let l of n)for(let g of this.searchFieldWithPreparedQuery(l,e,s))r.has(g)||(r.add(g),a.push(g));return a}searchField(t,n){let e=n.trim().toLowerCase();if(!e)return [];if(e.length<this.minQueryLength)return [];let s=m(e);return s.size?this.searchFieldWithPreparedQuery(t,e,s):[]}searchFieldWithPreparedQuery(t,n,e){let s=this.ngramIndexes.get(t);if(!s)return [];let r=[];for(let o of e){let i=s.get(o);if(!i)return [];r.push(i);}r.sort((o,i)=>o.size-i.size);let a=r[0],l=r.length,g=[];for(let o of a){let i=true;for(let c=1;c<l;c++)if(!r[c].has(o)){i=false;break}if(!i)continue;let u=this.data[o][t];typeof u=="string"&&u.toLowerCase().includes(n)&&g.push(this.data[o]);}return g}clear(){this.ngramIndexes.clear(),this.data=[];}};export{y as a};
|
package/dist/chunk-OE5GVXOE.mjs
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
import {a as a$1}from'./chunk-4TBU4VWK.mjs';import {a as a$3}from'./chunk-XFQ56UZU.mjs';import {a as a$2}from'./chunk-DBAABXBP.mjs';var a=class{constructor(e){let{imports:r,data:t,search:l,filter:g,sort:f}=e,i=new Set(r),T=i.has(a$1),c=i.has(a$2),h=i.has(a$3);this.searchEngine=T?new a$1({data:t,fields:l?.fields,minQueryLength:l?.minQueryLength}):null,this.sortEngine=c?new a$2({data:t,fields:f?.fields}):null,this.filterEngine=h?new a$3({data:t,fields:g?.fields,filterByPreviousResult:g?.filterByPreviousResult}):null;}search(e,r){if(!this.searchEngine)throw new Error("MergeEngines: TextSearchEngine is not available. Add TextSearchEngine to the `imports` array.");return r===void 0?this.searchEngine.search(e):this.searchEngine.search(e,r)}sort(e,r,t){if(!this.sortEngine)throw new Error("MergeEngines: SortEngine is not available. Add SortEngine to the `imports` array.");return r===void 0?this.sortEngine.sort(e):this.sortEngine.sort(e,r,t)}filter(e,r){if(!this.filterEngine)throw new Error("MergeEngines: FilterEngine is not available. Add FilterEngine to the `imports` array.");return r===void 0?this.filterEngine.filter(e):this.filterEngine.filter(e,r)}};export{a};
|