@wxn0brp/db-core 0.11.5-alpha.2 → 0.11.6
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 +3 -1
- package/dist/db/multiStorage.js +24 -16
- package/dist/db/routedStorage.js +16 -6
- package/dist/db/valthera.js +21 -5
- package/dist/helpers/addId.js +16 -7
- package/dist/helpers/assignDataPush.js +1 -1
- package/dist/helpers/collection.js +54 -9
- package/dist/helpers/executor.js +12 -7
- package/dist/helpers/forge.js +1 -1
- package/dist/helpers/gen.js +11 -4
- package/dist/helpers/relation.d.ts +4 -1
- package/dist/helpers/relation.js +82 -23
- package/dist/types/action.d.ts +6 -0
- package/dist/types/export.d.ts +1 -0
- package/dist/types/fileCpu.d.ts +4 -4
- package/dist/types/options.d.ts +14 -1
- package/dist/types/query.js +1 -5
- package/dist/types/relation.d.ts +4 -1
- package/dist/types/searchOpts.d.ts +10 -1
- package/dist/utils/action.js +66 -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.js +3 -1
- package/dist/utils/updateFindObject.js +3 -2
- package/dist/utils/updateObject.js +12 -6
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/base/actions.d.ts
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { ActionsBaseInterface } from "../types/action.js";
|
|
2
2
|
import { DataInternal } from "../types/data.js";
|
|
3
|
+
import { AdapterOpts } from "../types/options.js";
|
|
3
4
|
import { VQueryT } from "../types/query.js";
|
|
4
5
|
export declare abstract class ActionsBase implements ActionsBaseInterface {
|
|
5
6
|
_inited: boolean;
|
|
6
|
-
|
|
7
|
+
adapterOpts: AdapterOpts;
|
|
7
8
|
smartExecutor: boolean;
|
|
9
|
+
get numberId(): boolean;
|
|
10
|
+
set numberId(value: boolean);
|
|
11
|
+
get idKey(): string;
|
|
12
|
+
set idKey(value: string);
|
|
8
13
|
init(...args: any[]): Promise<void>;
|
|
9
14
|
close(...args: any[]): Promise<void>;
|
|
10
15
|
abstract getCollections(): Promise<string[]>;
|
package/dist/base/actions.js
CHANGED
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import { setDataForToggleOne, setDataForUpdateOneOrAdd } from "../helpers/assignDataPush.js";
|
|
1
|
+
import { setDataForToggleOne, setDataForUpdateOneOrAdd, } from "../helpers/assignDataPush.js";
|
|
2
2
|
export class ActionsBase {
|
|
3
3
|
_inited = true;
|
|
4
|
-
|
|
4
|
+
adapterOpts = {
|
|
5
|
+
numberId: false,
|
|
6
|
+
idKey: "_id",
|
|
7
|
+
};
|
|
5
8
|
smartExecutor = false;
|
|
9
|
+
get numberId() {
|
|
10
|
+
return this.adapterOpts.numberId ?? false;
|
|
11
|
+
}
|
|
12
|
+
set numberId(value) {
|
|
13
|
+
this.adapterOpts.numberId = value;
|
|
14
|
+
}
|
|
15
|
+
get idKey() {
|
|
16
|
+
return this.adapterOpts.idKey ?? "_id";
|
|
17
|
+
}
|
|
18
|
+
set idKey(value) {
|
|
19
|
+
this.adapterOpts.idKey = value;
|
|
20
|
+
}
|
|
6
21
|
async init(...args) { }
|
|
7
22
|
async close(...args) { }
|
|
8
23
|
async updateOneOrAdd(config) {
|
|
@@ -10,13 +25,13 @@ export class ActionsBase {
|
|
|
10
25
|
if (res)
|
|
11
26
|
return {
|
|
12
27
|
data: res,
|
|
13
|
-
type: "updated"
|
|
28
|
+
type: "updated",
|
|
14
29
|
};
|
|
15
30
|
// transform UpdateOneQuery to AddQuery
|
|
16
31
|
setDataForUpdateOneOrAdd(config);
|
|
17
32
|
return {
|
|
18
33
|
data: await this.add(config),
|
|
19
|
-
type: "added"
|
|
34
|
+
type: "added",
|
|
20
35
|
};
|
|
21
36
|
}
|
|
22
37
|
async toggleOne(config) {
|
|
@@ -24,13 +39,13 @@ export class ActionsBase {
|
|
|
24
39
|
if (res)
|
|
25
40
|
return {
|
|
26
41
|
data: res,
|
|
27
|
-
type: "removed"
|
|
42
|
+
type: "removed",
|
|
28
43
|
};
|
|
29
44
|
// transform ToggleOneQuery to AddQuery
|
|
30
45
|
setDataForToggleOne(config);
|
|
31
46
|
return {
|
|
32
47
|
data: await this.add(config),
|
|
33
|
-
type: "added"
|
|
48
|
+
type: "added",
|
|
34
49
|
};
|
|
35
50
|
}
|
|
36
51
|
}
|
package/dist/base/custom.d.ts
CHANGED
package/dist/base/custom.js
CHANGED
|
@@ -3,9 +3,6 @@ import { findUtil } from "../utils/action.js";
|
|
|
3
3
|
import { ActionsBase } from "./actions.js";
|
|
4
4
|
export class CustomActionsBase extends ActionsBase {
|
|
5
5
|
fileCpu;
|
|
6
|
-
constructor() {
|
|
7
|
-
super();
|
|
8
|
-
}
|
|
9
6
|
/**
|
|
10
7
|
* Add a new entry to the specified database.
|
|
11
8
|
*/
|
|
@@ -21,7 +18,9 @@ export class CustomActionsBase extends ActionsBase {
|
|
|
21
18
|
*/
|
|
22
19
|
async find(query) {
|
|
23
20
|
await this.ensureCollection(query.collection);
|
|
24
|
-
const data = await findUtil(query, this.fileCpu, [
|
|
21
|
+
const data = await findUtil(query, this.fileCpu, [
|
|
22
|
+
query.collection,
|
|
23
|
+
]);
|
|
25
24
|
return data;
|
|
26
25
|
}
|
|
27
26
|
/**
|
|
@@ -29,7 +28,7 @@ export class CustomActionsBase extends ActionsBase {
|
|
|
29
28
|
*/
|
|
30
29
|
async findOne(query) {
|
|
31
30
|
await this.ensureCollection(query.collection);
|
|
32
|
-
|
|
31
|
+
const data = (await this.fileCpu.findOne(query.collection, query));
|
|
33
32
|
return data || null;
|
|
34
33
|
}
|
|
35
34
|
/**
|
package/dist/customFileCpu.js
CHANGED
|
@@ -11,16 +11,14 @@ export class CustomFileCpu {
|
|
|
11
11
|
}
|
|
12
12
|
async add(file, config) {
|
|
13
13
|
file = pathRepair(file);
|
|
14
|
-
|
|
14
|
+
const entries = await this._readFile(file);
|
|
15
15
|
entries.push(config.data);
|
|
16
16
|
await this._writeFile(file, entries);
|
|
17
17
|
}
|
|
18
18
|
async find(file, config) {
|
|
19
19
|
file = pathRepair(file);
|
|
20
20
|
const entries = await this._readFile(file);
|
|
21
|
-
return entries
|
|
22
|
-
.map((entry) => findObj(config, entry))
|
|
23
|
-
.filter(Boolean);
|
|
21
|
+
return entries.map(entry => findObj(config, entry)).filter(Boolean);
|
|
24
22
|
}
|
|
25
23
|
async findOne(file, config) {
|
|
26
24
|
file = pathRepair(file);
|
|
@@ -36,7 +34,7 @@ export class CustomFileCpu {
|
|
|
36
34
|
file = pathRepair(file);
|
|
37
35
|
let entries = await this._readFile(file);
|
|
38
36
|
const removed = [];
|
|
39
|
-
entries = entries.filter(
|
|
37
|
+
entries = entries.filter(entry => {
|
|
40
38
|
if (removed.length && one)
|
|
41
39
|
return true;
|
|
42
40
|
if (matchObj(config, entry)) {
|
|
@@ -53,7 +51,7 @@ export class CustomFileCpu {
|
|
|
53
51
|
file = pathRepair(file);
|
|
54
52
|
let entries = await this._readFile(file);
|
|
55
53
|
const updated = [];
|
|
56
|
-
entries = entries.map(
|
|
54
|
+
entries = entries.map(entry => {
|
|
57
55
|
if (updated.length && one)
|
|
58
56
|
return entry;
|
|
59
57
|
if (matchObj(config, entry)) {
|
package/dist/db/memory.js
CHANGED
|
@@ -40,7 +40,9 @@ export class MemoryAction extends CustomActionsBase {
|
|
|
40
40
|
}
|
|
41
41
|
export class ValtheraMemory extends ValtheraClass {
|
|
42
42
|
constructor(...args) {
|
|
43
|
-
super({
|
|
43
|
+
super({
|
|
44
|
+
adapter: new MemoryAction(),
|
|
45
|
+
});
|
|
44
46
|
}
|
|
45
47
|
}
|
|
46
48
|
export function createMemoryValthera(data) {
|
package/dist/db/multiStorage.js
CHANGED
|
@@ -14,57 +14,65 @@ export class MultiBackend extends ActionsBase {
|
|
|
14
14
|
await Promise.all(this.backends.map(b => b.close(...args)));
|
|
15
15
|
}
|
|
16
16
|
async add(config) {
|
|
17
|
-
return await this.primaryBackend.add({
|
|
17
|
+
return await this.primaryBackend.add({
|
|
18
|
+
...config,
|
|
19
|
+
});
|
|
18
20
|
}
|
|
19
21
|
async find(config) {
|
|
20
|
-
const results = await Promise.all(this.backends.map(b => b.find({
|
|
22
|
+
const results = await Promise.all(this.backends.map(b => b.find({
|
|
23
|
+
...config,
|
|
24
|
+
})));
|
|
21
25
|
return results.flat();
|
|
22
26
|
}
|
|
23
27
|
async findOne(config) {
|
|
24
28
|
for (const backend of this.backends) {
|
|
25
29
|
try {
|
|
26
|
-
const result = await backend.findOne({
|
|
30
|
+
const result = await backend.findOne({
|
|
31
|
+
...config,
|
|
32
|
+
});
|
|
27
33
|
if (result !== null && result !== undefined) {
|
|
28
34
|
return result;
|
|
29
35
|
}
|
|
30
36
|
}
|
|
31
|
-
catch {
|
|
32
|
-
continue;
|
|
33
|
-
}
|
|
37
|
+
catch { }
|
|
34
38
|
}
|
|
35
39
|
return null;
|
|
36
40
|
}
|
|
37
41
|
async update(config) {
|
|
38
|
-
const results = await Promise.all(this.backends.map(b => b.update({
|
|
42
|
+
const results = await Promise.all(this.backends.map(b => b.update({
|
|
43
|
+
...config,
|
|
44
|
+
})));
|
|
39
45
|
return results.flat();
|
|
40
46
|
}
|
|
41
47
|
async updateOne(config) {
|
|
42
48
|
for (const backend of this.backends) {
|
|
43
49
|
try {
|
|
44
|
-
const result = await backend.updateOne({
|
|
50
|
+
const result = await backend.updateOne({
|
|
51
|
+
...config,
|
|
52
|
+
});
|
|
45
53
|
if (result)
|
|
46
54
|
return result;
|
|
47
55
|
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
continue;
|
|
50
|
-
}
|
|
56
|
+
catch (error) { }
|
|
51
57
|
}
|
|
52
58
|
return null;
|
|
53
59
|
}
|
|
54
60
|
async remove(config) {
|
|
55
|
-
const results = await Promise.all(this.backends.map(b => b.remove({
|
|
61
|
+
const results = await Promise.all(this.backends.map(b => b.remove({
|
|
62
|
+
...config,
|
|
63
|
+
})));
|
|
56
64
|
return results.flat();
|
|
57
65
|
}
|
|
58
66
|
async removeOne(config) {
|
|
59
67
|
for (const backend of this.backends) {
|
|
60
68
|
try {
|
|
61
|
-
const result = await backend.removeOne({
|
|
69
|
+
const result = await backend.removeOne({
|
|
70
|
+
...config,
|
|
71
|
+
});
|
|
62
72
|
if (result)
|
|
63
73
|
return result;
|
|
64
74
|
}
|
|
65
|
-
catch (error) {
|
|
66
|
-
continue;
|
|
67
|
-
}
|
|
75
|
+
catch (error) { }
|
|
68
76
|
}
|
|
69
77
|
return null;
|
|
70
78
|
}
|
package/dist/db/routedStorage.js
CHANGED
|
@@ -5,14 +5,19 @@ export class RoutedStorage extends ActionsBase {
|
|
|
5
5
|
constructor(rules, defaultBackend) {
|
|
6
6
|
super();
|
|
7
7
|
this.rules = rules;
|
|
8
|
-
this.defaultBackend = Array.isArray(defaultBackend)
|
|
8
|
+
this.defaultBackend = Array.isArray(defaultBackend)
|
|
9
|
+
? defaultBackend
|
|
10
|
+
: [
|
|
11
|
+
defaultBackend,
|
|
12
|
+
];
|
|
9
13
|
}
|
|
10
14
|
_matchBackends(config) {
|
|
11
15
|
const matched = [];
|
|
12
16
|
const { collection } = config;
|
|
13
17
|
for (const rule of this.rules) {
|
|
14
18
|
const { match, backends } = rule;
|
|
15
|
-
if ((typeof match === "string" &&
|
|
19
|
+
if ((typeof match === "string" && match === collection) ||
|
|
20
|
+
match === "*" ||
|
|
16
21
|
(match instanceof RegExp && match.test(collection)) ||
|
|
17
22
|
(typeof match === "function" && match(config))) {
|
|
18
23
|
matched.push(...backends);
|
|
@@ -44,13 +49,19 @@ export class RoutedStorage extends ActionsBase {
|
|
|
44
49
|
async close(...args) {
|
|
45
50
|
const all = new Set([
|
|
46
51
|
...this.rules.flatMap(r => r.backends),
|
|
47
|
-
...this.defaultBackend
|
|
52
|
+
...this.defaultBackend,
|
|
48
53
|
]);
|
|
49
54
|
await Promise.all(Array.from(all).map(b => b.close(...args)));
|
|
50
55
|
}
|
|
51
56
|
async add(config) {
|
|
52
57
|
const res = await this._withFirst(config, b => b.add(config));
|
|
53
|
-
await this._withAll({
|
|
58
|
+
await this._withAll({
|
|
59
|
+
...config,
|
|
60
|
+
data: res,
|
|
61
|
+
}, b => b.add({
|
|
62
|
+
...config,
|
|
63
|
+
data: res,
|
|
64
|
+
}));
|
|
54
65
|
return res;
|
|
55
66
|
}
|
|
56
67
|
async find(config) {
|
|
@@ -81,8 +92,7 @@ export class RoutedStorage extends ActionsBase {
|
|
|
81
92
|
return this._withAll(collection, b => b.removeCollection(collection), true);
|
|
82
93
|
}
|
|
83
94
|
async getCollections() {
|
|
84
|
-
const all = await Promise.all(Array.from(new Set(this.rules.flatMap(r => r.backends)))
|
|
85
|
-
.map(b => b.getCollections()));
|
|
95
|
+
const all = await Promise.all(Array.from(new Set(this.rules.flatMap(r => r.backends))).map(b => b.getCollections()));
|
|
86
96
|
const merged = new Set(all.flat());
|
|
87
97
|
return Array.from(merged);
|
|
88
98
|
}
|
package/dist/db/valthera.js
CHANGED
|
@@ -37,10 +37,20 @@ export class ValtheraClass {
|
|
|
37
37
|
this.executor = options.executor || new SmartExecutor(undefined, false);
|
|
38
38
|
options.adapter ??= options.dbAction;
|
|
39
39
|
options.dbAction ??= options.adapter;
|
|
40
|
+
options.adapterOpts ??= {};
|
|
41
|
+
if (options.numberId !== undefined &&
|
|
42
|
+
options.adapterOpts.numberId === undefined)
|
|
43
|
+
options.adapterOpts.numberId = options.numberId;
|
|
44
|
+
if (options.idKey !== undefined && options.adapterOpts.idKey === undefined)
|
|
45
|
+
options.adapterOpts.idKey = options.idKey;
|
|
40
46
|
if (typeof options.adapter === "function")
|
|
41
47
|
return;
|
|
42
|
-
else
|
|
48
|
+
else {
|
|
43
49
|
this.adapter = options.adapter;
|
|
50
|
+
if (options.adapterOpts) {
|
|
51
|
+
this.adapter.adapterOpts = options.adapterOpts;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
44
54
|
}
|
|
45
55
|
async init(...args) {
|
|
46
56
|
if (this.adapter?._inited)
|
|
@@ -52,10 +62,16 @@ export class ValtheraClass {
|
|
|
52
62
|
if (typeof self.options.adapter === "function")
|
|
53
63
|
self.adapter = await self.options.adapter();
|
|
54
64
|
// if the executor is not set, and the action wants a smart executor
|
|
55
|
-
if (!self.options.executor &&
|
|
65
|
+
if (!self.options.executor &&
|
|
66
|
+
self.adapter.smartExecutor &&
|
|
67
|
+
self.executor instanceof SmartExecutor)
|
|
56
68
|
self.executor.aware = true;
|
|
57
|
-
if (self.options.
|
|
58
|
-
self.adapter.
|
|
69
|
+
if (self.options.adapterOpts) {
|
|
70
|
+
self.adapter.adapterOpts = {
|
|
71
|
+
...self.adapter.adapterOpts,
|
|
72
|
+
...self.options.adapterOpts,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
59
75
|
await self.adapter.init(...args);
|
|
60
76
|
self.adapter._inited = true;
|
|
61
77
|
});
|
|
@@ -83,7 +99,7 @@ export class ValtheraClass {
|
|
|
83
99
|
if (idx < plugins.length)
|
|
84
100
|
return plugins[idx++].execute(ctx);
|
|
85
101
|
return self.executor.addOp(self.adapter[ctx.op].bind(self.adapter), ctx.query, typeof ctx.query === "string" ? ctx.query : ctx.query.collection);
|
|
86
|
-
}
|
|
102
|
+
},
|
|
87
103
|
};
|
|
88
104
|
const result = await ctx.next();
|
|
89
105
|
this.emitter.emit(ctx.op, ctx.query, result);
|
package/dist/helpers/addId.js
CHANGED
|
@@ -2,22 +2,31 @@ import { genId } from "./gen.js";
|
|
|
2
2
|
export async function addId(query, actions, defaultGen = true) {
|
|
3
3
|
const { collection, data } = query;
|
|
4
4
|
const id_gen = query.id_gen ?? defaultGen;
|
|
5
|
+
const { idKey = "_id", numberId } = actions.adapterOpts;
|
|
5
6
|
if (!id_gen)
|
|
6
7
|
return data;
|
|
7
|
-
if (data
|
|
8
|
+
if (data[idKey])
|
|
8
9
|
return data;
|
|
9
|
-
const { numberId } = actions;
|
|
10
10
|
if (!numberId) {
|
|
11
|
-
data
|
|
11
|
+
data[idKey] = genId();
|
|
12
12
|
return;
|
|
13
13
|
}
|
|
14
|
-
const find = await actions.findOne({
|
|
15
|
-
|
|
14
|
+
const find = (await actions.findOne({
|
|
15
|
+
collection: "__vdb_id",
|
|
16
|
+
search: {
|
|
17
|
+
c: collection,
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
data[idKey] = find?.i ? find.i + 1 : 1;
|
|
16
21
|
await actions.updateOneOrAdd({
|
|
17
22
|
collection: "__vdb_id",
|
|
18
|
-
search: {
|
|
23
|
+
search: {
|
|
24
|
+
c: collection,
|
|
25
|
+
},
|
|
19
26
|
updater: {
|
|
20
|
-
$inc: {
|
|
27
|
+
$inc: {
|
|
28
|
+
i: 1,
|
|
29
|
+
},
|
|
21
30
|
},
|
|
22
31
|
id_gen: false,
|
|
23
32
|
});
|
|
@@ -6,49 +6,89 @@ export class Collection {
|
|
|
6
6
|
this.collection = collection;
|
|
7
7
|
}
|
|
8
8
|
add(data, id_gen) {
|
|
9
|
-
return this.db.add({
|
|
9
|
+
return this.db.add({
|
|
10
|
+
collection: this.collection,
|
|
11
|
+
data,
|
|
12
|
+
id_gen,
|
|
13
|
+
});
|
|
10
14
|
}
|
|
11
15
|
/**
|
|
12
16
|
* Find data in a database.
|
|
13
17
|
*/
|
|
14
18
|
find(search = {}, options = {}, findOpts = {}, context = {}) {
|
|
15
|
-
return this.db.find({
|
|
19
|
+
return this.db.find({
|
|
20
|
+
collection: this.collection,
|
|
21
|
+
search,
|
|
22
|
+
dbFindOpts: options,
|
|
23
|
+
findOpts,
|
|
24
|
+
context,
|
|
25
|
+
});
|
|
16
26
|
}
|
|
17
27
|
/**
|
|
18
28
|
* Find one data entry in a database.
|
|
19
29
|
*/
|
|
20
30
|
findOne(search = {}, findOpts = {}, context = {}) {
|
|
21
|
-
return this.db.findOne({
|
|
31
|
+
return this.db.findOne({
|
|
32
|
+
collection: this.collection,
|
|
33
|
+
search,
|
|
34
|
+
findOpts,
|
|
35
|
+
context,
|
|
36
|
+
});
|
|
22
37
|
}
|
|
23
38
|
/**
|
|
24
39
|
* Update data in a database.
|
|
25
40
|
*/
|
|
26
41
|
update(search, updater, context = {}) {
|
|
27
|
-
return this.db.update({
|
|
42
|
+
return this.db.update({
|
|
43
|
+
collection: this.collection,
|
|
44
|
+
search,
|
|
45
|
+
updater,
|
|
46
|
+
context,
|
|
47
|
+
});
|
|
28
48
|
}
|
|
29
49
|
/**
|
|
30
50
|
* Update one data entry in a database.
|
|
31
51
|
*/
|
|
32
52
|
updateOne(search, updater, context = {}) {
|
|
33
|
-
return this.db.updateOne({
|
|
53
|
+
return this.db.updateOne({
|
|
54
|
+
collection: this.collection,
|
|
55
|
+
search,
|
|
56
|
+
updater,
|
|
57
|
+
context,
|
|
58
|
+
});
|
|
34
59
|
}
|
|
35
60
|
/**
|
|
36
61
|
* Remove data from a database.
|
|
37
62
|
*/
|
|
38
63
|
remove(search, context = {}) {
|
|
39
|
-
return this.db.remove({
|
|
64
|
+
return this.db.remove({
|
|
65
|
+
collection: this.collection,
|
|
66
|
+
search,
|
|
67
|
+
context,
|
|
68
|
+
});
|
|
40
69
|
}
|
|
41
70
|
/**
|
|
42
71
|
* Remove one data entry from a database.
|
|
43
72
|
*/
|
|
44
73
|
removeOne(search, context = {}) {
|
|
45
|
-
return this.db.removeOne({
|
|
74
|
+
return this.db.removeOne({
|
|
75
|
+
collection: this.collection,
|
|
76
|
+
search,
|
|
77
|
+
context,
|
|
78
|
+
});
|
|
46
79
|
}
|
|
47
80
|
/**
|
|
48
81
|
* Asynchronously updates one entry in a database or adds a new one if it doesn't exist.
|
|
49
82
|
*/
|
|
50
83
|
updateOneOrAdd(search, updater, { add_arg = {}, context = {}, id_gen = true } = {}) {
|
|
51
|
-
return this.db.updateOneOrAdd({
|
|
84
|
+
return this.db.updateOneOrAdd({
|
|
85
|
+
collection: this.collection,
|
|
86
|
+
search,
|
|
87
|
+
updater,
|
|
88
|
+
add_arg,
|
|
89
|
+
context,
|
|
90
|
+
id_gen,
|
|
91
|
+
});
|
|
52
92
|
}
|
|
53
93
|
/**
|
|
54
94
|
* Asynchronously removes one entry in a database or adds a new one if it doesn't exist. Usage e.g. for toggling a flag.
|
|
@@ -57,6 +97,11 @@ export class Collection {
|
|
|
57
97
|
* after the operation.
|
|
58
98
|
*/
|
|
59
99
|
toggleOne(search, data = {}, context = {}) {
|
|
60
|
-
return this.db.toggleOne({
|
|
100
|
+
return this.db.toggleOne({
|
|
101
|
+
collection: this.collection,
|
|
102
|
+
search,
|
|
103
|
+
data,
|
|
104
|
+
context,
|
|
105
|
+
});
|
|
61
106
|
}
|
|
62
107
|
}
|
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.js
CHANGED
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/types/action.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
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;
|
|
6
|
+
adapterOpts: AdapterOpts;
|
|
7
|
+
/** @deprecated use `adapterOpts.numberId` */
|
|
5
8
|
numberId: boolean;
|
|
9
|
+
/** @deprecated use `adapterOpts.idKey` */
|
|
10
|
+
idKey: string;
|
|
11
|
+
smartExecutor: boolean;
|
|
6
12
|
init(...args: any[]): Promise<void>;
|
|
7
13
|
close(...args: any[]): Promise<void>;
|
|
8
14
|
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
|
@@ -13,18 +13,31 @@ type AdapterOption = {
|
|
|
13
13
|
};
|
|
14
14
|
export type DbOpts = AdapterOption & {
|
|
15
15
|
executor?: Executor;
|
|
16
|
+
adapterOpts?: AdapterOpts;
|
|
17
|
+
/** @deprecated use `adapterOpts.numberId` */
|
|
16
18
|
numberId?: boolean;
|
|
19
|
+
/** @deprecated use `adapterOpts.idKey` */
|
|
20
|
+
idKey?: string;
|
|
17
21
|
};
|
|
22
|
+
export interface AdapterOpts {
|
|
23
|
+
numberId?: boolean;
|
|
24
|
+
idKey?: string;
|
|
25
|
+
}
|
|
18
26
|
export type FieldPath<T = any> = KeysMatching<T, any> | (string & {}) | string[];
|
|
19
27
|
export interface DbFindOpts<T = any> {
|
|
20
28
|
reverse?: boolean;
|
|
21
29
|
limit?: number;
|
|
22
30
|
offset?: number;
|
|
23
|
-
sortBy?: KeysMatching<T, any
|
|
31
|
+
sortBy?: KeysMatching<T, any> | {
|
|
32
|
+
field: KeysMatching<T, any>;
|
|
33
|
+
asc?: boolean;
|
|
34
|
+
}[];
|
|
24
35
|
sortAsc?: boolean;
|
|
25
36
|
min?: Record<string, KeysMatching<T, number>>;
|
|
26
37
|
max?: Record<string, KeysMatching<T, number>>;
|
|
27
38
|
avg?: Record<string, KeysMatching<T, number>>;
|
|
39
|
+
sum?: Record<string, KeysMatching<T, number>>;
|
|
40
|
+
distinct?: KeysMatching<T, any>;
|
|
28
41
|
groupBy?: KeysMatching<T, any> | KeysMatching<T, any>[];
|
|
29
42
|
count?: Record<string, string>;
|
|
30
43
|
}
|
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][];
|
|
@@ -87,6 +92,10 @@ export type StringOperators<T = any> = {
|
|
|
87
92
|
$startsWith?: NestedValue<T, string, string>;
|
|
88
93
|
/** "John" ends with "hn" */
|
|
89
94
|
$endsWith?: NestedValue<T, string, string>;
|
|
95
|
+
/** "John" starts with "jo" (case-insensitive) */
|
|
96
|
+
$iStartsWith?: NestedValue<T, string, string>;
|
|
97
|
+
/** "John" ends with "HN" (case-insensitive) */
|
|
98
|
+
$iEndsWith?: NestedValue<T, string, string>;
|
|
90
99
|
};
|
|
91
100
|
/** Other Operators with nested support */
|
|
92
101
|
export type OtherOperators<T = any> = {
|
package/dist/utils/action.js
CHANGED
|
@@ -1,12 +1,14 @@
|
|
|
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, limit = -1, sortBy, sortAsc = true, min, max, avg, groupBy, count, } = dbFindOpts;
|
|
5
|
-
const needsAllData = min || max || avg || groupBy || count || sortBy;
|
|
4
|
+
const { reverse = false, offset = 0, limit = -1, sortBy, sortAsc = true, min, max, avg, sum, distinct, groupBy, count, } = dbFindOpts;
|
|
5
|
+
const needsAllData = min || max || avg || sum || distinct || groupBy || count || sortBy;
|
|
6
6
|
let datas = [];
|
|
7
7
|
let skippedEntries = 0;
|
|
8
8
|
if (Array.isArray(fileCpuOrData)) {
|
|
9
|
-
datas = [
|
|
9
|
+
datas = [
|
|
10
|
+
...fileCpuOrData,
|
|
11
|
+
];
|
|
10
12
|
if (reverse && !sortBy)
|
|
11
13
|
datas.reverse();
|
|
12
14
|
if (!needsAllData) {
|
|
@@ -18,9 +20,13 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
18
20
|
}
|
|
19
21
|
}
|
|
20
22
|
else {
|
|
21
|
-
const filesToProcess = reverse && !sortBy
|
|
23
|
+
const filesToProcess = reverse && !sortBy
|
|
24
|
+
? [
|
|
25
|
+
...files,
|
|
26
|
+
].reverse()
|
|
27
|
+
: files;
|
|
22
28
|
for (const f of filesToProcess) {
|
|
23
|
-
let entries = await fileCpuOrData.find(f, query, fileCpuOpts);
|
|
29
|
+
let entries = (await fileCpuOrData.find(f, query, fileCpuOpts));
|
|
24
30
|
if (reverse && !sortBy)
|
|
25
31
|
entries.reverse();
|
|
26
32
|
if (needsAllData) {
|
|
@@ -48,19 +54,30 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
48
54
|
}
|
|
49
55
|
if (!needsAllData)
|
|
50
56
|
return datas;
|
|
51
|
-
|
|
57
|
+
const hasAggregations = min || max || avg || sum || groupBy || count;
|
|
58
|
+
if (hasAggregations) {
|
|
52
59
|
const groups = new Map();
|
|
53
|
-
const groupKeys = Array.isArray(groupBy)
|
|
60
|
+
const groupKeys = Array.isArray(groupBy)
|
|
61
|
+
? groupBy
|
|
62
|
+
: groupBy
|
|
63
|
+
? [
|
|
64
|
+
groupBy,
|
|
65
|
+
]
|
|
66
|
+
: [];
|
|
54
67
|
if (groupKeys.length) {
|
|
55
68
|
for (const data of datas) {
|
|
56
|
-
const key = groupKeys
|
|
69
|
+
const key = groupKeys
|
|
70
|
+
.map(k => String(data[k] ?? ""))
|
|
71
|
+
.join("|");
|
|
57
72
|
if (!groups.has(key))
|
|
58
73
|
groups.set(key, []);
|
|
59
74
|
groups.get(key).push(data);
|
|
60
75
|
}
|
|
61
76
|
}
|
|
62
77
|
else {
|
|
63
|
-
groups.set("all", [
|
|
78
|
+
groups.set("all", [
|
|
79
|
+
...datas,
|
|
80
|
+
]);
|
|
64
81
|
}
|
|
65
82
|
const aggregated = [];
|
|
66
83
|
for (const [, groupItems] of groups) {
|
|
@@ -76,25 +93,61 @@ export async function findUtil(query, fileCpuOrData, files, fileCpuOpts) {
|
|
|
76
93
|
}
|
|
77
94
|
}
|
|
78
95
|
for (const [outKey, srcField] of Object.entries(min ?? {})) {
|
|
79
|
-
const nums = groupItems
|
|
96
|
+
const nums = groupItems
|
|
97
|
+
.map(d => Number(d[srcField]))
|
|
98
|
+
.filter(n => !Number.isNaN(n));
|
|
80
99
|
result[outKey] = nums.length ? Math.min(...nums) : null;
|
|
81
100
|
}
|
|
82
101
|
for (const [outKey, srcField] of Object.entries(max ?? {})) {
|
|
83
|
-
const nums = groupItems
|
|
102
|
+
const nums = groupItems
|
|
103
|
+
.map(d => Number(d[srcField]))
|
|
104
|
+
.filter(n => !Number.isNaN(n));
|
|
84
105
|
result[outKey] = nums.length ? Math.max(...nums) : null;
|
|
85
106
|
}
|
|
86
107
|
for (const [outKey, srcField] of Object.entries(avg ?? {})) {
|
|
87
|
-
const nums = groupItems
|
|
88
|
-
|
|
108
|
+
const nums = groupItems
|
|
109
|
+
.map(d => Number(d[srcField]))
|
|
110
|
+
.filter(n => !Number.isNaN(n));
|
|
111
|
+
result[outKey] = nums.length
|
|
112
|
+
? nums.reduce((a, b) => a + b, 0) / nums.length
|
|
113
|
+
: null;
|
|
114
|
+
}
|
|
115
|
+
for (const [outKey, srcField] of Object.entries(sum ?? {})) {
|
|
116
|
+
const nums = groupItems
|
|
117
|
+
.map(d => Number(d[srcField]))
|
|
118
|
+
.filter(n => !Number.isNaN(n));
|
|
119
|
+
result[outKey] = nums.length ? nums.reduce((a, b) => a + b, 0) : null;
|
|
89
120
|
}
|
|
90
121
|
aggregated.push(result);
|
|
91
122
|
}
|
|
92
123
|
datas = aggregated;
|
|
93
124
|
}
|
|
125
|
+
if (distinct) {
|
|
126
|
+
const seen = new Set();
|
|
127
|
+
datas = datas.filter(item => {
|
|
128
|
+
const val = item[distinct];
|
|
129
|
+
const key = JSON.stringify(val);
|
|
130
|
+
if (seen.has(key))
|
|
131
|
+
return false;
|
|
132
|
+
seen.add(key);
|
|
133
|
+
return true;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
94
136
|
if (sortBy) {
|
|
95
137
|
if (sortBy === "random()") {
|
|
96
138
|
datas.sort(() => Math.random() - 0.5);
|
|
97
139
|
}
|
|
140
|
+
else if (Array.isArray(sortBy)) {
|
|
141
|
+
datas.sort((a, b) => {
|
|
142
|
+
for (const { field, asc = true } of sortBy) {
|
|
143
|
+
const dir = asc ? 1 : -1;
|
|
144
|
+
const cmp = compareSafe(a[field], b[field]) * dir;
|
|
145
|
+
if (cmp !== 0)
|
|
146
|
+
return cmp;
|
|
147
|
+
}
|
|
148
|
+
return 0;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
98
151
|
else {
|
|
99
152
|
const dir = sortAsc ? 1 : -1;
|
|
100
153
|
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;
|
|
@@ -41,6 +41,7 @@ export function hasFieldsAdvanced(obj, fields) {
|
|
|
41
41
|
}
|
|
42
42
|
function mainCheck(obj, fields) {
|
|
43
43
|
return _for(fields, obj, {
|
|
44
|
+
ne: (data, value) => data !== value,
|
|
44
45
|
gt: (data, value) => data > value,
|
|
45
46
|
lt: (data, value) => data < value,
|
|
46
47
|
gte: (data, value) => data >= value,
|
|
@@ -51,7 +52,7 @@ function mainCheck(obj, fields) {
|
|
|
51
52
|
exists: (_, shouldExist, key) => {
|
|
52
53
|
if (shouldExist && !(key in obj))
|
|
53
54
|
return false;
|
|
54
|
-
if (!shouldExist &&
|
|
55
|
+
if (!shouldExist && key in obj)
|
|
55
56
|
return false;
|
|
56
57
|
return true;
|
|
57
58
|
},
|
|
@@ -69,6 +70,10 @@ function mainCheck(obj, fields) {
|
|
|
69
70
|
},
|
|
70
71
|
startswith: (data, value) => typeof data === "string" && data.startsWith(value),
|
|
71
72
|
endswith: (data, value) => typeof data === "string" && data.endsWith(value),
|
|
73
|
+
istartswith: (data, value) => typeof data === "string" &&
|
|
74
|
+
data.toLowerCase().startsWith(value.toLowerCase()),
|
|
75
|
+
iendswith: (data, value) => typeof data === "string" &&
|
|
76
|
+
data.toLowerCase().endsWith(value.toLowerCase()),
|
|
72
77
|
between: (data, [min, max]) => typeof data === "number" && data >= min && data <= max,
|
|
73
78
|
arrinc: (data, values) => Array.isArray(data) && values.some((val) => data.includes(val)),
|
|
74
79
|
arrincall: (data, values) => Array.isArray(data) && values.every((val) => data.includes(val)),
|
package/dist/utils/id.d.ts
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
export declare function convertIdToUnix(id: string): number;
|
|
2
|
+
/**
|
|
3
|
+
* Sorts an array of objects by their _id property
|
|
4
|
+
*/
|
|
2
5
|
export declare function sortByIds<T extends {
|
|
3
6
|
_id: string;
|
|
4
7
|
}>(objects: T[]): T[];
|
|
8
|
+
export declare function sortByIds<T extends Record<string, any>>(objects: T[], key: string): T[];
|
|
9
|
+
/**
|
|
10
|
+
* Sorts an array of objects using reference
|
|
11
|
+
* @param objects
|
|
12
|
+
* @param key default "_id"
|
|
13
|
+
*/
|
|
14
|
+
export declare function sortByIdsRef(objects: Record<string, any>[], key?: string): void;
|
|
5
15
|
export declare function compareIds(a: string | number, b: string | number): number;
|
package/dist/utils/id.js
CHANGED
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
export function convertIdToUnix(id) {
|
|
2
2
|
return parseInt(id.split("-")[0], 36);
|
|
3
3
|
}
|
|
4
|
-
export function sortByIds(objects) {
|
|
5
|
-
return objects.slice().sort((a, b) => compareIds(a
|
|
4
|
+
export function sortByIds(objects, key = "_id") {
|
|
5
|
+
return objects.slice().sort((a, b) => compareIds(a[key], b[key]));
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Sorts an array of objects using reference
|
|
9
|
+
* @param objects
|
|
10
|
+
* @param key default "_id"
|
|
11
|
+
*/
|
|
12
|
+
export function sortByIdsRef(objects, key = "_id") {
|
|
13
|
+
objects.sort((a, b) => compareIds(a[key], b[key]));
|
|
6
14
|
}
|
|
7
15
|
export function compareIds(a, b) {
|
|
8
16
|
if (typeof a === "string" && typeof b === "string")
|
package/dist/utils/merge.js
CHANGED
|
@@ -8,11 +8,15 @@ export function _deepMerge(target, ...sources) {
|
|
|
8
8
|
for (const key in source) {
|
|
9
9
|
if (isObject(source[key])) {
|
|
10
10
|
if (!target[key])
|
|
11
|
-
Object.assign(target, {
|
|
11
|
+
Object.assign(target, {
|
|
12
|
+
[key]: {},
|
|
13
|
+
});
|
|
12
14
|
_deepMerge(target[key], source[key]);
|
|
13
15
|
}
|
|
14
16
|
else {
|
|
15
|
-
Object.assign(target, {
|
|
17
|
+
Object.assign(target, {
|
|
18
|
+
[key]: source[key],
|
|
19
|
+
});
|
|
16
20
|
}
|
|
17
21
|
}
|
|
18
22
|
}
|
|
@@ -22,5 +26,5 @@ export function deepMerge(target, ...sources) {
|
|
|
22
26
|
return _deepMerge(structuredClone(target), ...sources.map(s => structuredClone(s)));
|
|
23
27
|
}
|
|
24
28
|
function isObject(item) {
|
|
25
|
-
return
|
|
29
|
+
return item && typeof item === "object" && !Array.isArray(item);
|
|
26
30
|
}
|
package/dist/utils/process.js
CHANGED
|
@@ -12,7 +12,9 @@ export function matchObj(config, obj) {
|
|
|
12
12
|
return true;
|
|
13
13
|
if (typeof search === "function" && search(obj, context))
|
|
14
14
|
return true;
|
|
15
|
-
if (typeof search === "object" &&
|
|
15
|
+
if (typeof search === "object" &&
|
|
16
|
+
!Array.isArray(search) &&
|
|
17
|
+
hasFieldsAdvanced(obj, search))
|
|
16
18
|
return true;
|
|
17
19
|
return false;
|
|
18
20
|
}
|
|
@@ -59,7 +59,7 @@ function deleteNested(obj, path) {
|
|
|
59
59
|
* @returns The updated object.
|
|
60
60
|
*/
|
|
61
61
|
export function updateFindObject(obj, findOpts) {
|
|
62
|
-
const { transform, select, exclude
|
|
62
|
+
const { transform, select, exclude } = findOpts;
|
|
63
63
|
if (typeof transform === "function")
|
|
64
64
|
obj = transform(obj);
|
|
65
65
|
if (Array.isArray(exclude)) {
|
|
@@ -72,7 +72,8 @@ export function updateFindObject(obj, findOpts) {
|
|
|
72
72
|
if (Array.isArray(select)) {
|
|
73
73
|
const result = {};
|
|
74
74
|
for (const field of select) {
|
|
75
|
-
if ((typeof field === "string" || Array.isArray(field)) &&
|
|
75
|
+
if ((typeof field === "string" || Array.isArray(field)) &&
|
|
76
|
+
pathExists(obj, field)) {
|
|
76
77
|
setNested(result, field, getValueAt(obj, field));
|
|
77
78
|
}
|
|
78
79
|
}
|
|
@@ -38,7 +38,9 @@ function mainUpdate(obj, fields) {
|
|
|
38
38
|
item.push(updater);
|
|
39
39
|
return item;
|
|
40
40
|
}
|
|
41
|
-
return [
|
|
41
|
+
return [
|
|
42
|
+
updater,
|
|
43
|
+
];
|
|
42
44
|
},
|
|
43
45
|
pushall: (item, updater) => {
|
|
44
46
|
if (Array.isArray(item)) {
|
|
@@ -50,9 +52,13 @@ function mainUpdate(obj, fields) {
|
|
|
50
52
|
pushset: (item, updater) => {
|
|
51
53
|
if (Array.isArray(item)) {
|
|
52
54
|
item.push(updater);
|
|
53
|
-
return [
|
|
55
|
+
return [
|
|
56
|
+
...new Set(item),
|
|
57
|
+
];
|
|
54
58
|
}
|
|
55
|
-
return [
|
|
59
|
+
return [
|
|
60
|
+
updater,
|
|
61
|
+
];
|
|
56
62
|
},
|
|
57
63
|
pull: (item, updater) => {
|
|
58
64
|
if (Array.isArray(item))
|
|
@@ -95,7 +101,7 @@ function mainUpdate(obj, fields) {
|
|
|
95
101
|
unset: (_, __, key, deepObj) => {
|
|
96
102
|
if (key in deepObj)
|
|
97
103
|
delete deepObj[key];
|
|
98
|
-
}
|
|
104
|
+
},
|
|
99
105
|
});
|
|
100
106
|
}
|
|
101
107
|
/**
|
|
@@ -104,8 +110,8 @@ function mainUpdate(obj, fields) {
|
|
|
104
110
|
* @param newVal - An object containing new values to update in the target object.
|
|
105
111
|
*/
|
|
106
112
|
function updateObject(obj, newVal) {
|
|
107
|
-
for (
|
|
108
|
-
if (
|
|
113
|
+
for (const key in newVal)
|
|
114
|
+
if (Object.hasOwn(newVal, key))
|
|
109
115
|
obj[key] = newVal[key];
|
|
110
116
|
return obj;
|
|
111
117
|
}
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const version = "0.11.
|
|
1
|
+
export const version = "0.11.6";
|