@wxn0brp/db-core 0.11.5 → 0.12.0-alpha.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/dist/base/actions.d.ts +6 -1
- package/dist/base/actions.js +21 -6
- package/dist/base/custom.d.ts +0 -1
- package/dist/base/custom.js +4 -5
- package/dist/customFileCpu.js +4 -6
- package/dist/db/memory.js +8 -6
- package/dist/db/multiStorage.js +24 -16
- package/dist/db/routedStorage.js +16 -6
- package/dist/db/valthera.d.ts +1 -26
- package/dist/db/valthera.js +21 -21
- package/dist/helpers/addId.js +16 -7
- package/dist/helpers/assignDataPush.js +1 -1
- package/dist/helpers/collection.d.ts +0 -3
- package/dist/helpers/collection.js +54 -12
- package/dist/helpers/executor.js +12 -7
- package/dist/helpers/forge.d.ts +0 -6
- package/dist/helpers/forge.js +1 -4
- package/dist/helpers/gen.js +11 -4
- package/dist/helpers/relation.d.ts +4 -1
- package/dist/helpers/relation.js +82 -23
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/types/action.d.ts +3 -1
- package/dist/types/export.d.ts +1 -0
- package/dist/types/fileCpu.d.ts +4 -4
- package/dist/types/options.d.ts +12 -12
- package/dist/types/query.js +1 -5
- package/dist/types/relation.d.ts +4 -1
- package/dist/types/searchOpts.d.ts +10 -13
- package/dist/types/updater.d.ts +0 -12
- package/dist/utils/action.js +69 -13
- package/dist/utils/compare.js +4 -3
- package/dist/utils/hasFieldsAdvanced.js +6 -1
- package/dist/utils/id.d.ts +10 -0
- package/dist/utils/id.js +10 -2
- package/dist/utils/merge.js +7 -3
- package/dist/utils/process.d.ts +3 -0
- package/dist/utils/process.js +10 -3
- package/dist/utils/updateFindObject.js +3 -2
- package/dist/utils/updateObject.d.ts +1 -1
- package/dist/utils/updateObject.js +15 -11
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/helpers/executor.js
CHANGED
|
@@ -12,9 +12,11 @@ export class Executor {
|
|
|
12
12
|
return await new Promise((resolve, reject) => {
|
|
13
13
|
this.queue.push({
|
|
14
14
|
func,
|
|
15
|
-
param: [
|
|
15
|
+
param: [
|
|
16
|
+
query,
|
|
17
|
+
],
|
|
16
18
|
resolve,
|
|
17
|
-
reject
|
|
19
|
+
reject,
|
|
18
20
|
});
|
|
19
21
|
this.execute();
|
|
20
22
|
});
|
|
@@ -28,7 +30,10 @@ export class Executor {
|
|
|
28
30
|
this.isExecuting = true;
|
|
29
31
|
while (this.queue.length > 0) {
|
|
30
32
|
const q = this.queue.shift();
|
|
31
|
-
await q
|
|
33
|
+
await q
|
|
34
|
+
.func(...q.param)
|
|
35
|
+
.then(q.resolve)
|
|
36
|
+
.catch(q.reject);
|
|
32
37
|
}
|
|
33
38
|
this.isExecuting = false;
|
|
34
39
|
}
|
|
@@ -45,14 +50,14 @@ export class SmartExecutor {
|
|
|
45
50
|
this.aware = aware;
|
|
46
51
|
}
|
|
47
52
|
async addOp(func, query, collection) {
|
|
48
|
-
const key = this.aware
|
|
49
|
-
collection ?? defaultCollection
|
|
50
|
-
defaultCollection;
|
|
53
|
+
const key = this.aware
|
|
54
|
+
? (collection ?? defaultCollection)
|
|
55
|
+
: defaultCollection;
|
|
51
56
|
let entry = this.collections.get(key);
|
|
52
57
|
if (!entry) {
|
|
53
58
|
entry = {
|
|
54
59
|
executor: new Executor(),
|
|
55
|
-
interval: null
|
|
60
|
+
interval: null,
|
|
56
61
|
};
|
|
57
62
|
this.collections.set(key, entry);
|
|
58
63
|
}
|
package/dist/helpers/forge.d.ts
CHANGED
|
@@ -2,12 +2,6 @@ import { Collection } from "./collection.js";
|
|
|
2
2
|
import { Data } from "../types/data.js";
|
|
3
3
|
import { ValtheraCompatible } from "../types/valthera.js";
|
|
4
4
|
import { ValtheraClass } from "../db/valthera.js";
|
|
5
|
-
export declare function forgeValthera<T extends string>(target: ValtheraClass): ValtheraClass & {
|
|
6
|
-
[K in T]: Collection;
|
|
7
|
-
};
|
|
8
|
-
export declare function forgeValthera<T extends string>(target: ValtheraCompatible): ValtheraCompatible & {
|
|
9
|
-
[K in T]: Collection;
|
|
10
|
-
};
|
|
11
5
|
export declare function forgeTypedValthera<T extends Record<string, Data>>(target: ValtheraClass): ValtheraClass & {
|
|
12
6
|
[K in keyof T]: Collection<T[K]>;
|
|
13
7
|
};
|
package/dist/helpers/forge.js
CHANGED
|
@@ -1,7 +1,4 @@
|
|
|
1
1
|
import { Collection } from "./collection.js";
|
|
2
|
-
export function forgeValthera(target) {
|
|
3
|
-
return forgeTypedValthera(target);
|
|
4
|
-
}
|
|
5
2
|
export function forgeTypedValthera(target) {
|
|
6
3
|
return new Proxy(target, {
|
|
7
4
|
get(target, prop, receiver) {
|
|
@@ -14,6 +11,6 @@ export function forgeTypedValthera(target) {
|
|
|
14
11
|
},
|
|
15
12
|
set(target, prop, value, receiver) {
|
|
16
13
|
return Reflect.set(target, prop, value, receiver);
|
|
17
|
-
}
|
|
14
|
+
},
|
|
18
15
|
});
|
|
19
16
|
}
|
package/dist/helpers/gen.js
CHANGED
|
@@ -17,7 +17,10 @@ const defaultIdData = getIdData();
|
|
|
17
17
|
*/
|
|
18
18
|
export function genId(parts = null, idData = defaultIdData) {
|
|
19
19
|
if (parts === null)
|
|
20
|
-
parts = [
|
|
20
|
+
parts = [
|
|
21
|
+
1,
|
|
22
|
+
1,
|
|
23
|
+
];
|
|
21
24
|
const time = getTime();
|
|
22
25
|
const id = getUniqueRandom({
|
|
23
26
|
time,
|
|
@@ -43,7 +46,10 @@ function getUniqueRandom(opts, idData) {
|
|
|
43
46
|
const partsLengthSum = opts.partsSchema.reduce((acc, num) => acc + num, 0);
|
|
44
47
|
const partsData = generateBase36InRange(minValue, partsLengthSum, idData);
|
|
45
48
|
const parts = splitStringByArray(opts.partsSchema, partsData);
|
|
46
|
-
const id = [
|
|
49
|
+
const id = [
|
|
50
|
+
opts.time,
|
|
51
|
+
...parts,
|
|
52
|
+
].join("-");
|
|
47
53
|
if (!idData.usedIds.has(id)) {
|
|
48
54
|
idData.usedIds.set(id, true);
|
|
49
55
|
setTimeout(() => idData.usedIds.delete(id), 1000);
|
|
@@ -130,11 +136,12 @@ function biasedRandomInRange(min, max) {
|
|
|
130
136
|
*/
|
|
131
137
|
function splitStringByArray(arr, str) {
|
|
132
138
|
let start = 0;
|
|
133
|
-
|
|
139
|
+
// biome-ignore lint/suspicious/noAssignInExpressions: false positive
|
|
140
|
+
return arr.map(length => str.slice(start, (start += length)));
|
|
134
141
|
}
|
|
135
142
|
/**
|
|
136
143
|
* Gets the current time in a base36 string format.
|
|
137
144
|
*/
|
|
138
145
|
function getTime() {
|
|
139
|
-
return
|
|
146
|
+
return Date.now().toString(36);
|
|
140
147
|
}
|
|
@@ -2,7 +2,10 @@ import { Search } from "../types/arg.js";
|
|
|
2
2
|
import { DbFindOpts } from "../types/options.js";
|
|
3
3
|
import { RelationTypes } from "../types/relation.js";
|
|
4
4
|
export declare function pickByPath(obj: any, paths: string[][]): any;
|
|
5
|
-
export declare function autoSelect(rel: RelationTypes.RelationConfig, key: string): [
|
|
5
|
+
export declare function autoSelect(rel: RelationTypes.RelationConfig, key: string): [
|
|
6
|
+
string[] | undefined,
|
|
7
|
+
boolean
|
|
8
|
+
];
|
|
6
9
|
export declare function convertSearchObjToSearchArray(obj: Record<string, any>, parentKeys?: string[]): string[][];
|
|
7
10
|
export declare function processRelations(dbs: RelationTypes.DBS, cfg: RelationTypes.Relation, data: any, parentList?: any[] | null): Promise<void>;
|
|
8
11
|
export declare class Relation {
|
package/dist/helpers/relation.js
CHANGED
|
@@ -20,23 +20,41 @@ export function pickByPath(obj, paths) {
|
|
|
20
20
|
return result;
|
|
21
21
|
}
|
|
22
22
|
export function autoSelect(rel, key) {
|
|
23
|
-
const select = Array.isArray(rel.select)
|
|
23
|
+
const select = Array.isArray(rel.select)
|
|
24
|
+
? [
|
|
25
|
+
...rel.select,
|
|
26
|
+
]
|
|
27
|
+
: undefined;
|
|
24
28
|
const shouldDelete = select && !select.includes(key);
|
|
25
29
|
if (shouldDelete)
|
|
26
30
|
select.push(key);
|
|
27
|
-
return [
|
|
31
|
+
return [
|
|
32
|
+
select,
|
|
33
|
+
shouldDelete,
|
|
34
|
+
];
|
|
28
35
|
}
|
|
29
36
|
export function convertSearchObjToSearchArray(obj, parentKeys = []) {
|
|
30
37
|
return Object.entries(obj).reduce((acc, [key, value]) => {
|
|
31
|
-
const currentPath = [
|
|
38
|
+
const currentPath = [
|
|
39
|
+
...parentKeys,
|
|
40
|
+
key,
|
|
41
|
+
];
|
|
32
42
|
if (!value) {
|
|
33
43
|
return acc;
|
|
34
44
|
}
|
|
35
|
-
else if (typeof value === "object" &&
|
|
36
|
-
|
|
45
|
+
else if (typeof value === "object" &&
|
|
46
|
+
value !== null &&
|
|
47
|
+
!Array.isArray(value)) {
|
|
48
|
+
return [
|
|
49
|
+
...acc,
|
|
50
|
+
...convertSearchObjToSearchArray(value, currentPath),
|
|
51
|
+
];
|
|
37
52
|
}
|
|
38
53
|
else {
|
|
39
|
-
return [
|
|
54
|
+
return [
|
|
55
|
+
...acc,
|
|
56
|
+
currentPath,
|
|
57
|
+
];
|
|
40
58
|
}
|
|
41
59
|
}, []);
|
|
42
60
|
}
|
|
@@ -44,23 +62,38 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
44
62
|
if (!data && !parentList)
|
|
45
63
|
return;
|
|
46
64
|
const batchMode = Array.isArray(parentList);
|
|
47
|
-
const targets = batchMode
|
|
65
|
+
const targets = batchMode
|
|
66
|
+
? parentList
|
|
67
|
+
: [
|
|
68
|
+
data,
|
|
69
|
+
];
|
|
48
70
|
for (const key in cfg) {
|
|
49
|
-
if (!
|
|
71
|
+
if (!Object.hasOwn(cfg, key))
|
|
50
72
|
continue;
|
|
51
73
|
const rel = cfg[key];
|
|
52
|
-
const { pk = "_id", fk = "_id", type = "1", path, as = key, select, dbFindOpts = {}, through } = rel;
|
|
74
|
+
const { pk = "_id", fk = "_id", type = "1", path, as = key, select, dbFindOpts = {}, through, } = rel;
|
|
53
75
|
const [dbKey, collection] = path;
|
|
54
76
|
const db = dbs[dbKey];
|
|
55
77
|
if (type === "1") {
|
|
56
|
-
const keys = [
|
|
78
|
+
const keys = [
|
|
79
|
+
...new Set(targets.map(i => i[pk])),
|
|
80
|
+
];
|
|
57
81
|
const [selectSafe, deleteSelect] = autoSelect(rel, fk);
|
|
58
82
|
const results = await db.find({
|
|
59
83
|
collection,
|
|
60
|
-
search: {
|
|
61
|
-
|
|
84
|
+
search: {
|
|
85
|
+
$in: {
|
|
86
|
+
[fk]: keys,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
findOpts: {
|
|
90
|
+
select: selectSafe,
|
|
91
|
+
},
|
|
62
92
|
});
|
|
63
|
-
const map = new Map(results.map(row => [
|
|
93
|
+
const map = new Map(results.map(row => [
|
|
94
|
+
row[fk],
|
|
95
|
+
row,
|
|
96
|
+
]));
|
|
64
97
|
for (const item of targets) {
|
|
65
98
|
const result = map.get(item[pk]) || null;
|
|
66
99
|
if (result && rel.relations)
|
|
@@ -78,8 +111,12 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
78
111
|
if (!cache.has(id))
|
|
79
112
|
cache.set(id, await db.findOne({
|
|
80
113
|
collection,
|
|
81
|
-
search: {
|
|
82
|
-
|
|
114
|
+
search: {
|
|
115
|
+
[fk]: id,
|
|
116
|
+
},
|
|
117
|
+
findOpts: {
|
|
118
|
+
select: selectSafe,
|
|
119
|
+
},
|
|
83
120
|
}));
|
|
84
121
|
const result = cache.get(id) || null;
|
|
85
122
|
if (result && rel.relations)
|
|
@@ -94,13 +131,20 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
94
131
|
const [selectSafe, deleteSelect] = autoSelect(rel, fk);
|
|
95
132
|
const results = await db.find({
|
|
96
133
|
collection,
|
|
97
|
-
search: {
|
|
134
|
+
search: {
|
|
135
|
+
$in: {
|
|
136
|
+
[fk]: ids,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
98
139
|
dbFindOpts,
|
|
99
|
-
findOpts: {
|
|
140
|
+
findOpts: {
|
|
141
|
+
select: selectSafe,
|
|
142
|
+
},
|
|
100
143
|
});
|
|
101
144
|
const grouped = results.reduce((acc, row) => {
|
|
102
145
|
const id = row[fk];
|
|
103
|
-
|
|
146
|
+
acc[id] ||= [];
|
|
147
|
+
acc[id].push(row);
|
|
104
148
|
return acc;
|
|
105
149
|
}, {});
|
|
106
150
|
for (const item of targets)
|
|
@@ -118,13 +162,21 @@ export async function processRelations(dbs, cfg, data, parentList = null) {
|
|
|
118
162
|
const pivotDb = dbs[through.db || dbKey];
|
|
119
163
|
const pivots = await pivotDb.find({
|
|
120
164
|
collection: through.table,
|
|
121
|
-
search: {
|
|
165
|
+
search: {
|
|
166
|
+
[through.pk]: item[pk],
|
|
167
|
+
},
|
|
122
168
|
});
|
|
123
169
|
const ids = pivots.map(p => p[through.fk]);
|
|
124
170
|
const related = await db.find({
|
|
125
171
|
collection,
|
|
126
|
-
search: {
|
|
127
|
-
|
|
172
|
+
search: {
|
|
173
|
+
$in: {
|
|
174
|
+
[fk]: ids,
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
findOpts: {
|
|
178
|
+
select,
|
|
179
|
+
},
|
|
128
180
|
});
|
|
129
181
|
item[as] = related;
|
|
130
182
|
if (rel.relations) {
|
|
@@ -145,7 +197,10 @@ export class Relation {
|
|
|
145
197
|
async findOne(path, search, relations, select) {
|
|
146
198
|
const [dbKey, collection] = path;
|
|
147
199
|
const db = this.dbs[dbKey];
|
|
148
|
-
const data = await db.findOne({
|
|
200
|
+
const data = await db.findOne({
|
|
201
|
+
collection,
|
|
202
|
+
search,
|
|
203
|
+
});
|
|
149
204
|
if (!data)
|
|
150
205
|
return null;
|
|
151
206
|
if (typeof select === "object" && !Array.isArray(select))
|
|
@@ -156,7 +211,11 @@ export class Relation {
|
|
|
156
211
|
async find(path, search, relations, select, dbFindOpts = {}) {
|
|
157
212
|
const [dbKey, collection] = path;
|
|
158
213
|
const db = this.dbs[dbKey];
|
|
159
|
-
const data = await db.find({
|
|
214
|
+
const data = await db.find({
|
|
215
|
+
collection,
|
|
216
|
+
search,
|
|
217
|
+
dbFindOpts,
|
|
218
|
+
});
|
|
160
219
|
if (relations)
|
|
161
220
|
await processRelations(this.dbs, relations, null, data);
|
|
162
221
|
if (typeof select === "object" && !Array.isArray(select))
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CustomFileCpu } from "./customFileCpu.js";
|
|
2
2
|
export { createMemoryValthera, ValtheraMemory } from "./db/memory.js";
|
|
3
3
|
export { ValtheraClass } from "./db/valthera.js";
|
|
4
|
-
export { forgeTypedValthera
|
|
4
|
+
export { forgeTypedValthera } from "./helpers/forge.js";
|
|
5
5
|
export { genId } from "./helpers/gen.js";
|
|
6
6
|
export { Relation } from "./helpers/relation.js";
|
|
7
7
|
export { convertIdToUnix, sortByIds } from "./utils/id.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export { CustomFileCpu } from "./customFileCpu.js";
|
|
2
2
|
export { createMemoryValthera, ValtheraMemory } from "./db/memory.js";
|
|
3
3
|
export { ValtheraClass } from "./db/valthera.js";
|
|
4
|
-
export { forgeTypedValthera
|
|
4
|
+
export { forgeTypedValthera } from "./helpers/forge.js";
|
|
5
5
|
export { genId } from "./helpers/gen.js";
|
|
6
6
|
export { Relation } from "./helpers/relation.js";
|
|
7
7
|
export { convertIdToUnix, sortByIds } from "./utils/id.js";
|
package/dist/types/action.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { DataInternal } from "./data.js";
|
|
2
|
+
import { AdapterOpts } from "./options.js";
|
|
2
3
|
import { VQueryT } from "./query.js";
|
|
3
4
|
export interface ActionsBaseInterface {
|
|
4
5
|
_inited: boolean;
|
|
5
|
-
|
|
6
|
+
adapterOpts: AdapterOpts;
|
|
7
|
+
smartExecutor: boolean;
|
|
6
8
|
init(...args: any[]): Promise<void>;
|
|
7
9
|
close(...args: any[]): Promise<void>;
|
|
8
10
|
getCollections(): Promise<string[]>;
|
package/dist/types/export.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare namespace ValtheraTypes {
|
|
|
5
5
|
type DbFindOpts = import("./options.js").DbFindOpts;
|
|
6
6
|
type FindOpts = import("./options.js").FindOpts;
|
|
7
7
|
type DbOpts = import("./options.js").DbOpts;
|
|
8
|
+
type AdapterOpts = import("./options.js").AdapterOpts;
|
|
8
9
|
type Data = import("./data.js").Data;
|
|
9
10
|
type SearchOptions = import("./searchOpts.js").SearchOptions;
|
|
10
11
|
type Id = import("./Id.js").Id;
|
package/dist/types/fileCpu.d.ts
CHANGED
|
@@ -17,10 +17,10 @@ export interface FileCpu {
|
|
|
17
17
|
*/
|
|
18
18
|
findOne(file: string, config: VQueryT.FindOne, opts?: any): Promise<DataInternal | false>;
|
|
19
19
|
/**
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
20
|
+
* Asynchronously removes entries from a file based on search criteria.
|
|
21
|
+
* @param one If `true`, removes only the first matching entry.
|
|
22
|
+
* @returns A promise resolving to `true` if at least one entry was removed, otherwise `false`.
|
|
23
|
+
*/
|
|
24
24
|
remove(file: string, config: VQueryT.Remove, one: boolean, opts?: any): Promise<DataInternal[]>;
|
|
25
25
|
/**
|
|
26
26
|
* Asynchronously updates entries in a file based on search criteria and an updater function or object.
|
package/dist/types/options.d.ts
CHANGED
|
@@ -2,29 +2,30 @@ import { ActionsBase } from "../base/actions.js";
|
|
|
2
2
|
import { Executor } from "../helpers/executor.js";
|
|
3
3
|
import { KeysMatching } from "./utils.js";
|
|
4
4
|
export type AdapterValue = ActionsBase | (() => Promise<ActionsBase>);
|
|
5
|
-
|
|
5
|
+
export interface DbOpts {
|
|
6
6
|
adapter: AdapterValue;
|
|
7
|
-
/** @deprecated use `adapter` */
|
|
8
|
-
dbAction?: AdapterValue;
|
|
9
|
-
} | {
|
|
10
|
-
adapter?: AdapterValue;
|
|
11
|
-
/** @deprecated use `adapter` */
|
|
12
|
-
dbAction: AdapterValue;
|
|
13
|
-
};
|
|
14
|
-
export type DbOpts = AdapterOption & {
|
|
15
7
|
executor?: Executor;
|
|
8
|
+
adapterOpts?: AdapterOpts;
|
|
9
|
+
}
|
|
10
|
+
export interface AdapterOpts {
|
|
16
11
|
numberId?: boolean;
|
|
17
|
-
|
|
12
|
+
idKey?: string;
|
|
13
|
+
}
|
|
18
14
|
export type FieldPath<T = any> = KeysMatching<T, any> | (string & {}) | string[];
|
|
19
15
|
export interface DbFindOpts<T = any> {
|
|
20
16
|
reverse?: boolean;
|
|
21
17
|
limit?: number;
|
|
22
18
|
offset?: number;
|
|
23
|
-
sortBy?: KeysMatching<T, any
|
|
19
|
+
sortBy?: KeysMatching<T, any> | {
|
|
20
|
+
field: KeysMatching<T, any>;
|
|
21
|
+
asc?: boolean;
|
|
22
|
+
}[];
|
|
24
23
|
sortAsc?: boolean;
|
|
25
24
|
min?: Record<string, KeysMatching<T, number>>;
|
|
26
25
|
max?: Record<string, KeysMatching<T, number>>;
|
|
27
26
|
avg?: Record<string, KeysMatching<T, number>>;
|
|
27
|
+
sum?: Record<string, KeysMatching<T, number>>;
|
|
28
|
+
distinct?: KeysMatching<T, any>;
|
|
28
29
|
groupBy?: KeysMatching<T, any> | KeysMatching<T, any>[];
|
|
29
30
|
count?: Record<string, string>;
|
|
30
31
|
}
|
|
@@ -33,4 +34,3 @@ export interface FindOpts<T = any> {
|
|
|
33
34
|
exclude?: FieldPath<T>[];
|
|
34
35
|
transform?: Function;
|
|
35
36
|
}
|
|
36
|
-
export {};
|
package/dist/types/query.js
CHANGED
package/dist/types/relation.d.ts
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { DbFindOpts } from "./options.js";
|
|
2
2
|
import { ValtheraCompatible } from "./valthera.js";
|
|
3
3
|
export declare namespace RelationTypes {
|
|
4
|
-
type Path = [
|
|
4
|
+
type Path = [
|
|
5
|
+
string,
|
|
6
|
+
string
|
|
7
|
+
];
|
|
5
8
|
type FieldPath = string[];
|
|
6
9
|
interface DBS {
|
|
7
10
|
[key: string]: ValtheraCompatible;
|
|
@@ -20,6 +20,8 @@ export type LogicalOperators<T = any> = {
|
|
|
20
20
|
};
|
|
21
21
|
/** Comparison Operators with nested support */
|
|
22
22
|
export type ComparisonOperators<T = any> = {
|
|
23
|
+
/** 5 != 4 */
|
|
24
|
+
$ne?: NestedValue<T, any>;
|
|
23
25
|
/** 5 > 4 */
|
|
24
26
|
$gt?: NestedValue<T, number, number>;
|
|
25
27
|
/** 5 < 4 */
|
|
@@ -29,7 +31,10 @@ export type ComparisonOperators<T = any> = {
|
|
|
29
31
|
/** 5 <= 4 */
|
|
30
32
|
$lte?: NestedValue<T, number, number>;
|
|
31
33
|
/** 5 between [min, max] */
|
|
32
|
-
$between?: NestedValue<T, [
|
|
34
|
+
$between?: NestedValue<T, [
|
|
35
|
+
number,
|
|
36
|
+
number
|
|
37
|
+
], number>;
|
|
33
38
|
/** 2 in [1, 2, 3] */
|
|
34
39
|
$in?: {
|
|
35
40
|
[K in keyof T]?: T[K] extends any[] ? T[K] : T[K][];
|
|
@@ -60,22 +65,10 @@ export type ArrayOperators<T = any> = {
|
|
|
60
65
|
$arrInc?: {
|
|
61
66
|
[K in keyof T]?: T[K] extends any[] ? T[K][number][] : T[K][];
|
|
62
67
|
};
|
|
63
|
-
/**
|
|
64
|
-
* @deprecated Use $arrInc instead.
|
|
65
|
-
*/
|
|
66
|
-
$arrinc?: {
|
|
67
|
-
[K in keyof T]?: T[K] extends any[] ? T[K][number][] : T[K][];
|
|
68
|
-
};
|
|
69
68
|
/** [1, 2, 3] array includes all elements e.g. [1, 2] */
|
|
70
69
|
$arrIncAll?: {
|
|
71
70
|
[K in keyof T]?: T[K] extends any[] ? T[K][number][] : T[K][];
|
|
72
71
|
};
|
|
73
|
-
/**
|
|
74
|
-
* @deprecated Use $arrIncAll instead.
|
|
75
|
-
*/
|
|
76
|
-
$arrincall?: {
|
|
77
|
-
[K in keyof T]?: T[K] extends any[] ? T[K][number][] : T[K][];
|
|
78
|
-
};
|
|
79
72
|
/** [1, 2, 3] has size 3 */
|
|
80
73
|
$size?: NestedValue<T, number>;
|
|
81
74
|
};
|
|
@@ -87,6 +80,10 @@ export type StringOperators<T = any> = {
|
|
|
87
80
|
$startsWith?: NestedValue<T, string, string>;
|
|
88
81
|
/** "John" ends with "hn" */
|
|
89
82
|
$endsWith?: NestedValue<T, string, string>;
|
|
83
|
+
/** "John" starts with "jo" (case-insensitive) */
|
|
84
|
+
$iStartsWith?: NestedValue<T, string, string>;
|
|
85
|
+
/** "John" ends with "HN" (case-insensitive) */
|
|
86
|
+
$iEndsWith?: NestedValue<T, string, string>;
|
|
90
87
|
};
|
|
91
88
|
/** Other Operators with nested support */
|
|
92
89
|
export type OtherOperators<T = any> = {
|
package/dist/types/updater.d.ts
CHANGED
|
@@ -5,24 +5,12 @@ export type ArrayUpdater<T = any> = {
|
|
|
5
5
|
$push?: NestedValue<T, any>;
|
|
6
6
|
/** [1,2] -> $pushSet 2,3 -> [1,2,3] */
|
|
7
7
|
$pushSet?: NestedValue<T, any>;
|
|
8
|
-
/**
|
|
9
|
-
* @deprecated Use $pushSet instead.
|
|
10
|
-
*/
|
|
11
|
-
$pushset?: NestedValue<T, any>;
|
|
12
8
|
/** [1,2,3] -> $pushAll [2,3] -> [1,2,3] */
|
|
13
9
|
$pushAll?: NestedValue<T, any>;
|
|
14
|
-
/**
|
|
15
|
-
* @deprecated Use $pushAll instead.
|
|
16
|
-
*/
|
|
17
|
-
$pushall?: NestedValue<T, any>;
|
|
18
10
|
/** [1,2,3] -> $pull 2 -> [1,3] */
|
|
19
11
|
$pull?: NestedValue<T, any>;
|
|
20
12
|
/** [1,2,2,3] -> $pullAll [2] -> [1,3] */
|
|
21
13
|
$pullAll?: NestedValue<T, any>;
|
|
22
|
-
/**
|
|
23
|
-
* @deprecated Use $pullAll instead.
|
|
24
|
-
*/
|
|
25
|
-
$pullall?: NestedValue<T, any>;
|
|
26
14
|
};
|
|
27
15
|
/** Objects */
|
|
28
16
|
export type ObjectUpdater<T = any> = {
|
package/dist/utils/action.js
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
import { compareSafe } from "./compare.js";
|
|
2
2
|
export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
3
3
|
const { dbFindOpts = {} } = query;
|
|
4
|
-
const { reverse = false, offset = 0,
|
|
5
|
-
|
|
4
|
+
const { reverse = false, offset = 0, sortBy, sortAsc = true, min, max, avg, sum, distinct, groupBy, count, } = dbFindOpts;
|
|
5
|
+
let limit = dbFindOpts.limit ?? -1;
|
|
6
|
+
if (limit === Infinity)
|
|
7
|
+
limit = -1;
|
|
8
|
+
const needsAllData = min || max || avg || sum || distinct || groupBy || count || sortBy;
|
|
6
9
|
let datas = [];
|
|
7
10
|
let skippedEntries = 0;
|
|
8
11
|
if (Array.isArray(fileCpuOrData)) {
|
|
9
|
-
datas = [
|
|
12
|
+
datas = [
|
|
13
|
+
...fileCpuOrData,
|
|
14
|
+
];
|
|
10
15
|
if (reverse && !sortBy)
|
|
11
16
|
datas.reverse();
|
|
12
17
|
if (!needsAllData) {
|
|
@@ -18,9 +23,13 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
else {
|
|
21
|
-
const filesToProcess = reverse && !sortBy
|
|
26
|
+
const filesToProcess = reverse && !sortBy
|
|
27
|
+
? [
|
|
28
|
+
...files,
|
|
29
|
+
].reverse()
|
|
30
|
+
: files;
|
|
22
31
|
for (const f of filesToProcess) {
|
|
23
|
-
let entries = await fileCpuOrData.find(f, query, fileCpuOpts);
|
|
32
|
+
let entries = (await fileCpuOrData.find(f, query, fileCpuOpts));
|
|
24
33
|
if (reverse && !sortBy)
|
|
25
34
|
entries.reverse();
|
|
26
35
|
if (needsAllData) {
|
|
@@ -48,19 +57,30 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
48
57
|
}
|
|
49
58
|
if (!needsAllData)
|
|
50
59
|
return datas;
|
|
51
|
-
|
|
60
|
+
const hasAggregations = min || max || avg || sum || groupBy || count;
|
|
61
|
+
if (hasAggregations) {
|
|
52
62
|
const groups = new Map();
|
|
53
|
-
const groupKeys = Array.isArray(groupBy)
|
|
63
|
+
const groupKeys = Array.isArray(groupBy)
|
|
64
|
+
? groupBy
|
|
65
|
+
: groupBy
|
|
66
|
+
? [
|
|
67
|
+
groupBy,
|
|
68
|
+
]
|
|
69
|
+
: [];
|
|
54
70
|
if (groupKeys.length) {
|
|
55
71
|
for (const data of datas) {
|
|
56
|
-
const key = groupKeys
|
|
72
|
+
const key = groupKeys
|
|
73
|
+
.map(k => String(data[k] ?? ""))
|
|
74
|
+
.join("|");
|
|
57
75
|
if (!groups.has(key))
|
|
58
76
|
groups.set(key, []);
|
|
59
77
|
groups.get(key).push(data);
|
|
60
78
|
}
|
|
61
79
|
}
|
|
62
80
|
else {
|
|
63
|
-
groups.set("all", [
|
|
81
|
+
groups.set("all", [
|
|
82
|
+
...datas,
|
|
83
|
+
]);
|
|
64
84
|
}
|
|
65
85
|
const aggregated = [];
|
|
66
86
|
for (const [, groupItems] of groups) {
|
|
@@ -76,25 +96,61 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
76
96
|
}
|
|
77
97
|
}
|
|
78
98
|
for (const [outKey, srcField] of Object.entries(min ?? {})) {
|
|
79
|
-
const nums = groupItems
|
|
99
|
+
const nums = groupItems
|
|
100
|
+
.map(d => Number(d[srcField]))
|
|
101
|
+
.filter(n => !Number.isNaN(n));
|
|
80
102
|
result[outKey] = nums.length ? Math.min(...nums) : null;
|
|
81
103
|
}
|
|
82
104
|
for (const [outKey, srcField] of Object.entries(max ?? {})) {
|
|
83
|
-
const nums = groupItems
|
|
105
|
+
const nums = groupItems
|
|
106
|
+
.map(d => Number(d[srcField]))
|
|
107
|
+
.filter(n => !Number.isNaN(n));
|
|
84
108
|
result[outKey] = nums.length ? Math.max(...nums) : null;
|
|
85
109
|
}
|
|
86
110
|
for (const [outKey, srcField] of Object.entries(avg ?? {})) {
|
|
87
|
-
const nums = groupItems
|
|
88
|
-
|
|
111
|
+
const nums = groupItems
|
|
112
|
+
.map(d => Number(d[srcField]))
|
|
113
|
+
.filter(n => !Number.isNaN(n));
|
|
114
|
+
result[outKey] = nums.length
|
|
115
|
+
? nums.reduce((a, b) => a + b, 0) / nums.length
|
|
116
|
+
: null;
|
|
117
|
+
}
|
|
118
|
+
for (const [outKey, srcField] of Object.entries(sum ?? {})) {
|
|
119
|
+
const nums = groupItems
|
|
120
|
+
.map(d => Number(d[srcField]))
|
|
121
|
+
.filter(n => !Number.isNaN(n));
|
|
122
|
+
result[outKey] = nums.length ? nums.reduce((a, b) => a + b, 0) : null;
|
|
89
123
|
}
|
|
90
124
|
aggregated.push(result);
|
|
91
125
|
}
|
|
92
126
|
datas = aggregated;
|
|
93
127
|
}
|
|
128
|
+
if (distinct) {
|
|
129
|
+
const seen = new Set();
|
|
130
|
+
datas = datas.filter(item => {
|
|
131
|
+
const val = item[distinct];
|
|
132
|
+
const key = JSON.stringify(val);
|
|
133
|
+
if (seen.has(key))
|
|
134
|
+
return false;
|
|
135
|
+
seen.add(key);
|
|
136
|
+
return true;
|
|
137
|
+
});
|
|
138
|
+
}
|
|
94
139
|
if (sortBy) {
|
|
95
140
|
if (sortBy === "random()") {
|
|
96
141
|
datas.sort(() => Math.random() - 0.5);
|
|
97
142
|
}
|
|
143
|
+
else if (Array.isArray(sortBy)) {
|
|
144
|
+
datas.sort((a, b) => {
|
|
145
|
+
for (const { field, asc = true } of sortBy) {
|
|
146
|
+
const dir = asc ? 1 : -1;
|
|
147
|
+
const cmp = compareSafe(a[field], b[field]) * dir;
|
|
148
|
+
if (cmp !== 0)
|
|
149
|
+
return cmp;
|
|
150
|
+
}
|
|
151
|
+
return 0;
|
|
152
|
+
});
|
|
153
|
+
}
|
|
98
154
|
else {
|
|
99
155
|
const dir = sortAsc ? 1 : -1;
|
|
100
156
|
datas.sort((a, b) => compareSafe(a[sortBy], b[sortBy]) * dir);
|
package/dist/utils/compare.js
CHANGED
|
@@ -12,11 +12,12 @@ export function compareSafe(a, b) {
|
|
|
12
12
|
if (typeA === "string" && typeB === "string") {
|
|
13
13
|
return a.localeCompare(b);
|
|
14
14
|
}
|
|
15
|
-
if ((typeA === "number" || a instanceof Date) &&
|
|
16
|
-
|
|
15
|
+
if ((typeA === "number" || a instanceof Date) &&
|
|
16
|
+
(typeB === "number" || b instanceof Date)) {
|
|
17
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
17
18
|
}
|
|
18
19
|
if (typeA === "boolean" && typeB === "boolean") {
|
|
19
|
-
return
|
|
20
|
+
return a === b ? 0 : a ? 1 : -1;
|
|
20
21
|
}
|
|
21
22
|
// fallback: if both are objects/arrays or different types, consider equal
|
|
22
23
|
return 0;
|