@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.
Files changed (43) hide show
  1. package/dist/base/actions.d.ts +6 -1
  2. package/dist/base/actions.js +21 -6
  3. package/dist/base/custom.d.ts +0 -1
  4. package/dist/base/custom.js +4 -5
  5. package/dist/customFileCpu.js +4 -6
  6. package/dist/db/memory.js +8 -6
  7. package/dist/db/multiStorage.js +24 -16
  8. package/dist/db/routedStorage.js +16 -6
  9. package/dist/db/valthera.d.ts +1 -26
  10. package/dist/db/valthera.js +21 -21
  11. package/dist/helpers/addId.js +16 -7
  12. package/dist/helpers/assignDataPush.js +1 -1
  13. package/dist/helpers/collection.d.ts +0 -3
  14. package/dist/helpers/collection.js +54 -12
  15. package/dist/helpers/executor.js +12 -7
  16. package/dist/helpers/forge.d.ts +0 -6
  17. package/dist/helpers/forge.js +1 -4
  18. package/dist/helpers/gen.js +11 -4
  19. package/dist/helpers/relation.d.ts +4 -1
  20. package/dist/helpers/relation.js +82 -23
  21. package/dist/index.d.ts +1 -1
  22. package/dist/index.js +1 -1
  23. package/dist/types/action.d.ts +3 -1
  24. package/dist/types/export.d.ts +1 -0
  25. package/dist/types/fileCpu.d.ts +4 -4
  26. package/dist/types/options.d.ts +12 -12
  27. package/dist/types/query.js +1 -5
  28. package/dist/types/relation.d.ts +4 -1
  29. package/dist/types/searchOpts.d.ts +10 -13
  30. package/dist/types/updater.d.ts +0 -12
  31. package/dist/utils/action.js +69 -13
  32. package/dist/utils/compare.js +4 -3
  33. package/dist/utils/hasFieldsAdvanced.js +6 -1
  34. package/dist/utils/id.d.ts +10 -0
  35. package/dist/utils/id.js +10 -2
  36. package/dist/utils/merge.js +7 -3
  37. package/dist/utils/process.d.ts +3 -0
  38. package/dist/utils/process.js +10 -3
  39. package/dist/utils/updateFindObject.js +3 -2
  40. package/dist/utils/updateObject.d.ts +1 -1
  41. package/dist/utils/updateObject.js +15 -11
  42. package/dist/version.js +1 -1
  43. package/package.json +1 -1
@@ -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: [query],
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.func(...q.param).then(q.resolve).catch(q.reject);
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
  }
@@ -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
  };
@@ -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
  }
@@ -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 = [1, 1];
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 = [opts.time, ...parts].join("-");
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
- return arr.map(length => str.slice(start, start += length));
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 new Date().getTime().toString(36);
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): [string[] | undefined, boolean];
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 {
@@ -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) ? [...rel.select] : undefined;
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 [select, shouldDelete];
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 = [...parentKeys, key];
38
+ const currentPath = [
39
+ ...parentKeys,
40
+ key,
41
+ ];
32
42
  if (!value) {
33
43
  return acc;
34
44
  }
35
- else if (typeof value === "object" && value !== null && !Array.isArray(value)) {
36
- return [...acc, ...convertSearchObjToSearchArray(value, currentPath)];
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 [...acc, currentPath];
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 ? parentList : [data];
65
+ const targets = batchMode
66
+ ? parentList
67
+ : [
68
+ data,
69
+ ];
48
70
  for (const key in cfg) {
49
- if (!cfg.hasOwnProperty(key))
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 = [...new Set(targets.map(i => i[pk]))];
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: { $in: { [fk]: keys } },
61
- findOpts: { select: selectSafe }
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 => [row[fk], 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: { [fk]: id },
82
- findOpts: { select: selectSafe }
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: { $in: { [fk]: ids } },
134
+ search: {
135
+ $in: {
136
+ [fk]: ids,
137
+ },
138
+ },
98
139
  dbFindOpts,
99
- findOpts: { select: selectSafe }
140
+ findOpts: {
141
+ select: selectSafe,
142
+ },
100
143
  });
101
144
  const grouped = results.reduce((acc, row) => {
102
145
  const id = row[fk];
103
- (acc[id] ||= []).push(row);
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: { [through.pk]: item[pk] }
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: { $in: { [fk]: ids } },
127
- findOpts: { select }
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({ collection, search });
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({ collection, search, dbFindOpts });
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, forgeValthera } from "./helpers/forge.js";
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, forgeValthera } from "./helpers/forge.js";
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";
@@ -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
- numberId: boolean;
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[]>;
@@ -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;
@@ -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
- * 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
- */
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.
@@ -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
- type AdapterOption = {
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 {};
@@ -1,5 +1 @@
1
- export var VQueryT;
2
- (function (VQueryT) {
3
- ;
4
- ;
5
- })(VQueryT || (VQueryT = {}));
1
+ export {};
@@ -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 = [string, string];
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, [number, number], number>;
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> = {
@@ -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> = {
@@ -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, 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, 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 = [...fileCpuOrData];
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 ? [...files].reverse() : files;
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
- if (min || max || avg || groupBy || count) {
60
+ const hasAggregations = min || max || avg || sum || groupBy || count;
61
+ if (hasAggregations) {
52
62
  const groups = new Map();
53
- const groupKeys = Array.isArray(groupBy) ? groupBy : groupBy ? [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.map(k => String(data[k] ?? "")).join("|");
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", [...datas]);
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.map(d => Number(d[srcField])).filter(n => !isNaN(n));
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.map(d => Number(d[srcField])).filter(n => !isNaN(n));
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.map(d => Number(d[srcField])).filter(n => !isNaN(n));
88
- result[outKey] = nums.length ? nums.reduce((a, b) => a + b, 0) / nums.length : null;
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);
@@ -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) && (typeB === "number" || b instanceof Date)) {
16
- return (a < b) ? -1 : (a > b) ? 1 : 0;
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 (a === b) ? 0 : a ? 1 : -1;
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;