@based/db 0.0.35 → 0.0.37
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/lib/darwin_aarch64/include/selva/fields.h +11 -0
- package/dist/lib/darwin_aarch64/include/selva/types.h +2 -0
- package/dist/lib/darwin_aarch64/include/selva_lang_code.h +1 -0
- package/dist/lib/darwin_aarch64/libdeflate.dylib +0 -0
- package/dist/lib/darwin_aarch64/libjemalloc_selva.2.dylib +0 -0
- package/dist/lib/darwin_aarch64/libnode-v20.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v21.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v22.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v23.node +0 -0
- package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
- package/dist/lib/linux_aarch64/include/selva/fields.h +11 -0
- package/dist/lib/linux_aarch64/include/selva/types.h +2 -0
- package/dist/lib/linux_aarch64/include/selva_lang_code.h +1 -0
- package/dist/lib/linux_aarch64/libjemalloc_selva.so.2 +0 -0
- package/dist/lib/linux_aarch64/libnode-v20.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v21.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v22.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v23.node +0 -0
- package/dist/lib/linux_aarch64/libselva.so +0 -0
- package/dist/lib/linux_x86_64/include/selva/fields.h +11 -0
- package/dist/lib/linux_x86_64/include/selva/types.h +2 -0
- package/dist/lib/linux_x86_64/include/selva_lang_code.h +1 -0
- package/dist/lib/linux_x86_64/libnode-v20.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v21.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v22.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v23.node +0 -0
- package/dist/lib/linux_x86_64/libselva.so +0 -0
- package/dist/src/client/index.d.ts +2 -1
- package/dist/src/client/index.js +30 -33
- package/dist/src/client/modify/fixed.js +1 -0
- package/dist/src/client/query/BasedDbQuery.d.ts +2 -1
- package/dist/src/client/query/BasedDbQuery.js +11 -4
- package/dist/src/client/query/BasedIterable.d.ts +2 -1
- package/dist/src/client/query/BasedIterable.js +4 -16
- package/dist/src/client/query/aggregates/aggregation.d.ts +5 -0
- package/dist/src/client/query/aggregates/aggregation.js +96 -0
- package/dist/src/client/query/aggregates/types.d.ts +7 -0
- package/dist/src/client/query/aggregates/types.js +2 -0
- package/dist/src/client/query/display.js +1 -10
- package/dist/src/client/query/query.d.ts +1 -1
- package/dist/src/client/query/query.js +1 -1
- package/dist/src/client/query/queryDef.js +1 -1
- package/dist/src/client/query/read/read.d.ts +1 -1
- package/dist/src/client/query/read/read.js +63 -65
- package/dist/src/client/query/toBuffer.js +38 -7
- package/dist/src/client/query/types.d.ts +13 -19
- package/dist/src/client/query/types.js +0 -2
- package/dist/src/index.d.ts +1 -1
- package/dist/src/index.js +4 -1
- package/dist/src/server/index.js +15 -3
- package/dist/src/utils.d.ts +1 -0
- package/dist/src/utils.js +47 -0
- package/package.json +3 -3
- package/dist/src/client/query/aggregation.d.ts +0 -3
- package/dist/src/client/query/aggregation.js +0 -9
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
|
+
#include <stdint.h>
|
|
7
8
|
#include <sys/types.h>
|
|
8
9
|
#include "selva/_export.h"
|
|
9
10
|
#ifdef __zig
|
|
@@ -81,6 +82,16 @@ struct SelvaFieldsPointer {
|
|
|
81
82
|
size_t len;
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Precalculated empty strings for text translations.
|
|
87
|
+
* The size of the string is 8 for better alignment but they are only
|
|
88
|
+
* filled upto 6 bytes.
|
|
89
|
+
*/
|
|
90
|
+
SELVA_EXPORT
|
|
91
|
+
extern const uint8_t selva_fields_text_tl_empty[_selva_lang_last][8];
|
|
92
|
+
|
|
93
|
+
#define SELVA_FIELDS_TEXT_TL_EMPTY_LEN 6
|
|
94
|
+
|
|
84
95
|
#if __has_c_attribute(unsequenced)
|
|
85
96
|
[[unsequenced]]
|
|
86
97
|
#else
|
|
@@ -45,6 +45,8 @@ struct EdgeFieldConstraint {
|
|
|
45
45
|
* Skip saving this field while dumping.
|
|
46
46
|
* If the field is of type SELVA_FIELD_TYPE_REFERENCES it's saved
|
|
47
47
|
* regardless of this flag to preserve the original order of references.
|
|
48
|
+
* However, the meta is only save from one side, i.e. the side that's
|
|
49
|
+
* not skipped.
|
|
48
50
|
*/
|
|
49
51
|
EDGE_FIELD_CONSTRAINT_FLAG_SKIP_DUMP = 0x80,
|
|
50
52
|
} __packed flags;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
|
+
#include <stdint.h>
|
|
7
8
|
#include <sys/types.h>
|
|
8
9
|
#include "selva/_export.h"
|
|
9
10
|
#ifdef __zig
|
|
@@ -81,6 +82,16 @@ struct SelvaFieldsPointer {
|
|
|
81
82
|
size_t len;
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Precalculated empty strings for text translations.
|
|
87
|
+
* The size of the string is 8 for better alignment but they are only
|
|
88
|
+
* filled upto 6 bytes.
|
|
89
|
+
*/
|
|
90
|
+
SELVA_EXPORT
|
|
91
|
+
extern const uint8_t selva_fields_text_tl_empty[_selva_lang_last][8];
|
|
92
|
+
|
|
93
|
+
#define SELVA_FIELDS_TEXT_TL_EMPTY_LEN 6
|
|
94
|
+
|
|
84
95
|
#if __has_c_attribute(unsequenced)
|
|
85
96
|
[[unsequenced]]
|
|
86
97
|
#else
|
|
@@ -45,6 +45,8 @@ struct EdgeFieldConstraint {
|
|
|
45
45
|
* Skip saving this field while dumping.
|
|
46
46
|
* If the field is of type SELVA_FIELD_TYPE_REFERENCES it's saved
|
|
47
47
|
* regardless of this flag to preserve the original order of references.
|
|
48
|
+
* However, the meta is only save from one side, i.e. the side that's
|
|
49
|
+
* not skipped.
|
|
48
50
|
*/
|
|
49
51
|
EDGE_FIELD_CONSTRAINT_FLAG_SKIP_DUMP = 0x80,
|
|
50
52
|
} __packed flags;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
#pragma once
|
|
6
6
|
|
|
7
|
+
#include <stdint.h>
|
|
7
8
|
#include <sys/types.h>
|
|
8
9
|
#include "selva/_export.h"
|
|
9
10
|
#ifdef __zig
|
|
@@ -81,6 +82,16 @@ struct SelvaFieldsPointer {
|
|
|
81
82
|
size_t len;
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Precalculated empty strings for text translations.
|
|
87
|
+
* The size of the string is 8 for better alignment but they are only
|
|
88
|
+
* filled upto 6 bytes.
|
|
89
|
+
*/
|
|
90
|
+
SELVA_EXPORT
|
|
91
|
+
extern const uint8_t selva_fields_text_tl_empty[_selva_lang_last][8];
|
|
92
|
+
|
|
93
|
+
#define SELVA_FIELDS_TEXT_TL_EMPTY_LEN 6
|
|
94
|
+
|
|
84
95
|
#if __has_c_attribute(unsequenced)
|
|
85
96
|
[[unsequenced]]
|
|
86
97
|
#else
|
|
@@ -45,6 +45,8 @@ struct EdgeFieldConstraint {
|
|
|
45
45
|
* Skip saving this field while dumping.
|
|
46
46
|
* If the field is of type SELVA_FIELD_TYPE_REFERENCES it's saved
|
|
47
47
|
* regardless of this flag to preserve the original order of references.
|
|
48
|
+
* However, the meta is only save from one side, i.e. the side that's
|
|
49
|
+
* not skipped.
|
|
48
50
|
*/
|
|
49
51
|
EDGE_FIELD_CONSTRAINT_FLAG_SKIP_DUMP = 0x80,
|
|
50
52
|
} __packed flags;
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -33,7 +33,6 @@ export declare class DbClient {
|
|
|
33
33
|
flushIsReady: Promise<void>;
|
|
34
34
|
hooks: DbClientHooks;
|
|
35
35
|
schema: DbClientSchema;
|
|
36
|
-
schemaIsSetValue: boolean;
|
|
37
36
|
schemaTypesParsed: Record<string, SchemaTypeDef>;
|
|
38
37
|
schemaTypesParsedById: Record<number, SchemaTypeDef>;
|
|
39
38
|
writeTime: number;
|
|
@@ -45,6 +44,8 @@ export declare class DbClient {
|
|
|
45
44
|
p: Promise<number | ModifyRes>;
|
|
46
45
|
}>;
|
|
47
46
|
schemaChecksum: number;
|
|
47
|
+
schemaProcessing: number;
|
|
48
|
+
schemaPromise: Promise<DbServer['schema']>;
|
|
48
49
|
setSchema(schema: Schema, fromStart?: boolean, transformFns?: TransformFns): Promise<StrictSchema>;
|
|
49
50
|
putLocalSchema(schema: any): DbClientSchema;
|
|
50
51
|
create(type: string, obj?: CreateObj, opts?: ModifyOpts): ModifyRes;
|
package/dist/src/client/index.js
CHANGED
|
@@ -7,10 +7,10 @@ import { ModifyState } from './modify/ModifyRes.js';
|
|
|
7
7
|
import { upsert } from './modify/upsert.js';
|
|
8
8
|
import { update } from './modify/update.js';
|
|
9
9
|
import { deleteFn } from './modify/delete.js';
|
|
10
|
-
import {
|
|
10
|
+
import { wait } from '@saulx/utils';
|
|
11
11
|
import { hash } from '@saulx/hash';
|
|
12
12
|
import { expire } from './modify/expire.js';
|
|
13
|
-
import { debugMode } from '../utils.js';
|
|
13
|
+
import { debugMode, schemaLooseEqual } from '../utils.js';
|
|
14
14
|
const makeFlushIsReady = (dbClient) => {
|
|
15
15
|
dbClient.flushIsReady = new Promise((resolve) => {
|
|
16
16
|
dbClient.flushReady = () => {
|
|
@@ -34,11 +34,7 @@ export class DbClient {
|
|
|
34
34
|
flushReady;
|
|
35
35
|
flushIsReady;
|
|
36
36
|
hooks;
|
|
37
|
-
schema
|
|
38
|
-
lastId: 1, // we reserve one for root props
|
|
39
|
-
types: {},
|
|
40
|
-
};
|
|
41
|
-
schemaIsSetValue = false;
|
|
37
|
+
schema;
|
|
42
38
|
schemaTypesParsed = {};
|
|
43
39
|
schemaTypesParsedById = {};
|
|
44
40
|
// modify
|
|
@@ -48,29 +44,31 @@ export class DbClient {
|
|
|
48
44
|
maxModifySize;
|
|
49
45
|
upserting = new Map();
|
|
50
46
|
schemaChecksum;
|
|
47
|
+
schemaProcessing;
|
|
48
|
+
schemaPromise;
|
|
51
49
|
async setSchema(schema, fromStart, transformFns) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if (
|
|
50
|
+
const strictSchema = fromStart ? schema : parse(schema).schema;
|
|
51
|
+
// this one excludes all the ids
|
|
52
|
+
if (schemaLooseEqual(strictSchema, this.schema)) {
|
|
55
53
|
return this.schema;
|
|
56
54
|
}
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
55
|
+
const checksum = hash(strictSchema);
|
|
56
|
+
if (checksum !== this.schemaProcessing) {
|
|
57
|
+
this.schemaProcessing = checksum;
|
|
58
|
+
this.schemaPromise = this.hooks.setSchema(strictSchema, fromStart, transformFns);
|
|
59
|
+
}
|
|
60
|
+
const remoteSchema = await this.schemaPromise;
|
|
61
|
+
this.schemaProcessing = null;
|
|
62
|
+
this.schemaPromise = null;
|
|
60
63
|
return this.putLocalSchema(remoteSchema);
|
|
61
64
|
}
|
|
62
65
|
putLocalSchema(schema) {
|
|
63
|
-
|
|
64
|
-
if (
|
|
66
|
+
const checksum = hash(schema);
|
|
67
|
+
if (this.schemaChecksum === checksum) {
|
|
65
68
|
return this.schema;
|
|
66
69
|
}
|
|
70
|
+
this.schemaChecksum = checksum;
|
|
67
71
|
this.schema = schema;
|
|
68
|
-
for (const field in this.schema.types) {
|
|
69
|
-
if (!('id' in this.schema.types[field])) {
|
|
70
|
-
this.schema.lastId++;
|
|
71
|
-
this.schema.types[field].id = this.schema.lastId;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
72
|
updateTypeDefs(this.schema, this.schemaTypesParsed, this.schemaTypesParsedById);
|
|
75
73
|
// Adds bidrectional refs on defs
|
|
76
74
|
schemaToSelvaBuffer(this.schemaTypesParsed);
|
|
@@ -199,18 +197,17 @@ export class DbClient {
|
|
|
199
197
|
await this.flushIsReady;
|
|
200
198
|
return;
|
|
201
199
|
}
|
|
202
|
-
schemaIsSet() {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
});
|
|
200
|
+
async schemaIsSet() {
|
|
201
|
+
if (this.schema) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
if (this.schemaPromise) {
|
|
205
|
+
await this.schemaPromise;
|
|
206
|
+
}
|
|
207
|
+
else {
|
|
208
|
+
await wait(12);
|
|
209
|
+
}
|
|
210
|
+
return this.schemaIsSet();
|
|
214
211
|
}
|
|
215
212
|
listeners;
|
|
216
213
|
on(event, cb) {
|
|
@@ -96,6 +96,7 @@ map[TIMESTAMP] = (ctx, val, def) => {
|
|
|
96
96
|
}
|
|
97
97
|
const view = new DataView(ctx.buf.buffer, ctx.buf.byteOffset + ctx.len, 8);
|
|
98
98
|
ctx.len += 8;
|
|
99
|
+
// Todo use new utils and store as uint64
|
|
99
100
|
view.setFloat64(0, parsedValue, true);
|
|
100
101
|
const ts = view.getFloat64(0);
|
|
101
102
|
};
|
|
@@ -17,7 +17,8 @@ export declare class QueryBranch<T> {
|
|
|
17
17
|
filterBatch(f: FilterAst): this;
|
|
18
18
|
search(query: string, ...fields: Search[]): T;
|
|
19
19
|
search(query: ArrayBufferView, field: string, opts?: Omit<FilterOpts, 'lowerCase'>): T;
|
|
20
|
-
|
|
20
|
+
groupBy(field: string): T;
|
|
21
|
+
sum(...fields: (string | string[])[]): T;
|
|
21
22
|
or(fn: FilterBranchFn): T;
|
|
22
23
|
or(field: string, operator?: Operator | boolean, value?: any, opts?: FilterOpts): T;
|
|
23
24
|
range(start: number, end?: number): T;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { createQueryDef, QueryDefType, filter, sort, defToBuffer, filterOr, isAlias, includeField, includeFields,
|
|
1
|
+
import { createQueryDef, QueryDefType, filter, sort, defToBuffer, filterOr, isAlias, includeField, includeFields, addAggregate, groupBy, } from './query.js';
|
|
2
2
|
import { BasedQueryResponse } from './BasedIterable.js';
|
|
3
3
|
import { createOrGetEdgeRefQueryDef, createOrGetRefQueryDef, } from './include/utils.js';
|
|
4
4
|
import { FilterBranch } from './filter/FilterBranch.js';
|
|
@@ -94,8 +94,15 @@ export class QueryBranch {
|
|
|
94
94
|
// @ts-ignore
|
|
95
95
|
return this;
|
|
96
96
|
}
|
|
97
|
-
|
|
98
|
-
|
|
97
|
+
groupBy(field) {
|
|
98
|
+
groupBy(this.def, field);
|
|
99
|
+
// only works with aggregates for now
|
|
100
|
+
// @ts-ignore
|
|
101
|
+
return this;
|
|
102
|
+
}
|
|
103
|
+
// x
|
|
104
|
+
sum(...fields) {
|
|
105
|
+
addAggregate(1 /* AggregateType.SUM */, this.def, fields);
|
|
99
106
|
// @ts-ignore
|
|
100
107
|
return this;
|
|
101
108
|
}
|
|
@@ -213,7 +220,7 @@ export class BasedDbQuery extends QueryBranch {
|
|
|
213
220
|
}
|
|
214
221
|
}
|
|
215
222
|
}
|
|
216
|
-
if (!db.
|
|
223
|
+
if (!db.schema) {
|
|
217
224
|
throw new Error('Query: No schema yet - use await db.schemaIsSet()');
|
|
218
225
|
}
|
|
219
226
|
const def = createQueryDef(db, QueryDefType.Root, target, skipValidation);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { inspect } from 'node:util';
|
|
2
2
|
import { QueryDef } from './types.js';
|
|
3
|
+
import { Item } from './query.js';
|
|
3
4
|
import { size, time, inspectData } from './display.js';
|
|
4
5
|
export { time, size, inspectData };
|
|
5
6
|
export declare class BasedQueryResponse {
|
|
@@ -14,7 +15,7 @@ export declare class BasedQueryResponse {
|
|
|
14
15
|
[inspect.custom](depth: number): string;
|
|
15
16
|
debug(): this;
|
|
16
17
|
node(index?: number): any;
|
|
17
|
-
[Symbol.iterator](): Generator<
|
|
18
|
+
[Symbol.iterator](): Generator<Item, void, unknown>;
|
|
18
19
|
inspect(depth?: number, raw?: boolean): this;
|
|
19
20
|
forEach(fn: (item: any, key: number) => void): void;
|
|
20
21
|
map(fn: (item: any, key: number) => any): any[];
|
|
@@ -65,16 +65,9 @@ export class BasedQueryResponse {
|
|
|
65
65
|
while (i < result.byteLength - 4) {
|
|
66
66
|
let id = readUint32(result, i);
|
|
67
67
|
i += 4;
|
|
68
|
-
let item
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
this.def.aggregation = 4 /* AggFlag.COUNT */;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
item = {
|
|
75
|
-
id,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
68
|
+
let item = {
|
|
69
|
+
id,
|
|
70
|
+
};
|
|
78
71
|
if (this.def.search) {
|
|
79
72
|
item.$searchScore = readFloatLE(result, i);
|
|
80
73
|
i += 4;
|
|
@@ -114,12 +107,7 @@ export class BasedQueryResponse {
|
|
|
114
107
|
}
|
|
115
108
|
get length() {
|
|
116
109
|
const l = readUint32(this.result, 0);
|
|
117
|
-
|
|
118
|
-
return l + 1;
|
|
119
|
-
}
|
|
120
|
-
else {
|
|
121
|
-
return l;
|
|
122
|
-
}
|
|
110
|
+
return l;
|
|
123
111
|
}
|
|
124
112
|
toObject() {
|
|
125
113
|
return resultToObject(this.def, this.result, this.end - 4, 0);
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { QueryDef, QueryDefAggregation } from '../types.js';
|
|
2
|
+
import { AggregateType } from './types.js';
|
|
3
|
+
export declare const aggregateToBuffer: (aggregates: QueryDefAggregation) => Uint8Array;
|
|
4
|
+
export declare const groupBy: (def: QueryDef, field: string) => void;
|
|
5
|
+
export declare const addAggregate: (type: AggregateType, def: QueryDef, fields: (string | string[])[]) => void;
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { writeUint16 } from '@saulx/utils';
|
|
2
|
+
export const aggregateToBuffer = (aggregates) => {
|
|
3
|
+
const aggBuffer = new Uint8Array(aggregates.size);
|
|
4
|
+
let i = 0;
|
|
5
|
+
if (aggregates.groupBy) {
|
|
6
|
+
aggBuffer[i] = 255 /* GroupBy.HAS_GROUP */;
|
|
7
|
+
i += 1;
|
|
8
|
+
aggBuffer[i] = aggregates.groupBy.prop;
|
|
9
|
+
i += 1;
|
|
10
|
+
aggBuffer[i] = aggregates.groupBy.typeIndex;
|
|
11
|
+
i += 1;
|
|
12
|
+
writeUint16(aggBuffer, aggregates.groupBy.start, i);
|
|
13
|
+
i += 2;
|
|
14
|
+
writeUint16(aggBuffer, aggregates.groupBy.len, i);
|
|
15
|
+
i += 2;
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
aggBuffer[i] = 1 /* GroupBy.NONE */;
|
|
19
|
+
i += 1;
|
|
20
|
+
}
|
|
21
|
+
writeUint16(aggBuffer, aggregates.totalResultsPos, i);
|
|
22
|
+
i += 2;
|
|
23
|
+
for (const [prop, aggregatesArray] of aggregates.aggregates.entries()) {
|
|
24
|
+
aggBuffer[i] = prop;
|
|
25
|
+
i += 1;
|
|
26
|
+
let sizeIndex = i;
|
|
27
|
+
let size = 0;
|
|
28
|
+
i += 2;
|
|
29
|
+
for (const agg of aggregatesArray) {
|
|
30
|
+
let startI = i;
|
|
31
|
+
aggBuffer[i] = agg.type;
|
|
32
|
+
i += 1;
|
|
33
|
+
aggBuffer[i] = agg.propDef.typeIndex;
|
|
34
|
+
i += 1;
|
|
35
|
+
writeUint16(aggBuffer, agg.propDef.start, i);
|
|
36
|
+
i += 2;
|
|
37
|
+
writeUint16(aggBuffer, agg.resultPos, i);
|
|
38
|
+
i += 2;
|
|
39
|
+
size += i - startI;
|
|
40
|
+
}
|
|
41
|
+
writeUint16(aggBuffer, size, sizeIndex);
|
|
42
|
+
}
|
|
43
|
+
return aggBuffer;
|
|
44
|
+
};
|
|
45
|
+
const ensureAggregate = (def) => {
|
|
46
|
+
if (!def.aggregate) {
|
|
47
|
+
def.aggregate = {
|
|
48
|
+
size: 3,
|
|
49
|
+
aggregates: new Map(),
|
|
50
|
+
totalResultsPos: 0,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
// Group by is great for normal stuff as well (do later)
|
|
55
|
+
export const groupBy = (def, field) => {
|
|
56
|
+
const fieldDef = def.schema.props[field];
|
|
57
|
+
if (!fieldDef) {
|
|
58
|
+
throw new Error(`Field for agg:groupBy does not exists "${field}" make better error later...`);
|
|
59
|
+
}
|
|
60
|
+
ensureAggregate(def);
|
|
61
|
+
if (!def.aggregate.groupBy) {
|
|
62
|
+
def.aggregate.size += 6;
|
|
63
|
+
}
|
|
64
|
+
def.aggregate.groupBy = fieldDef;
|
|
65
|
+
};
|
|
66
|
+
export const addAggregate = (type, def, fields) => {
|
|
67
|
+
ensureAggregate(def);
|
|
68
|
+
const aggregates = def.aggregate.aggregates;
|
|
69
|
+
for (const field of fields) {
|
|
70
|
+
if (Array.isArray(field)) {
|
|
71
|
+
addAggregate(type, def, field);
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
const fieldDef = def.schema.props[field];
|
|
75
|
+
if (!fieldDef) {
|
|
76
|
+
throw new Error(`Field for agg does not exists ${field} make better error later...`);
|
|
77
|
+
}
|
|
78
|
+
if (!aggregates.get(fieldDef.prop)) {
|
|
79
|
+
aggregates.set(fieldDef.prop, []);
|
|
80
|
+
def.aggregate.size += 3;
|
|
81
|
+
}
|
|
82
|
+
const aggregateField = aggregates.get(fieldDef.prop);
|
|
83
|
+
aggregateField.push({
|
|
84
|
+
propDef: fieldDef,
|
|
85
|
+
type,
|
|
86
|
+
resultPos: def.aggregate.totalResultsPos,
|
|
87
|
+
});
|
|
88
|
+
// IF FLOAT // NUMBER ETC USE 8!
|
|
89
|
+
// do this better
|
|
90
|
+
def.aggregate.totalResultsPos += 4;
|
|
91
|
+
// needs to add an extra field WRITE TO
|
|
92
|
+
def.aggregate.size += 6;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
//# sourceMappingURL=aggregation.js.map
|
|
@@ -145,16 +145,7 @@ const inspectObject = (object, q, path, level, isLast, isFirst, isObject, depth)
|
|
|
145
145
|
str += ',\n';
|
|
146
146
|
}
|
|
147
147
|
else if (!def) {
|
|
148
|
-
|
|
149
|
-
// TODO: to flag the agg someway. This is ugly as hell!!!
|
|
150
|
-
str += picocolors.blue(v);
|
|
151
|
-
str += picocolors.italic(picocolors.dim(' count'));
|
|
152
|
-
str += ',\n';
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
str +=
|
|
156
|
-
inspectObject(v, q, key, level + 2, false, false, true, depth) + '';
|
|
157
|
-
}
|
|
148
|
+
str += inspectObject(v, q, key, level + 2, false, false, true, depth) + '';
|
|
158
149
|
}
|
|
159
150
|
else if ('__isPropDef' in def) {
|
|
160
151
|
if (def.typeIndex === REFERENCES) {
|
|
@@ -5,5 +5,5 @@ export type Item = {
|
|
|
5
5
|
[key: string]: any;
|
|
6
6
|
};
|
|
7
7
|
export type AggItem = Partial<Item>;
|
|
8
|
-
export declare const readAllFields: (q: QueryDef, result: Uint8Array, offset: number, end: number, item: Item
|
|
8
|
+
export declare const readAllFields: (q: QueryDef, result: Uint8Array, offset: number, end: number, item: Item, id: number) => number;
|
|
9
9
|
export declare const resultToObject: (q: QueryDef, result: Uint8Array, end: number, offset?: number) => any;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { ALIAS, ALIASES, BINARY, BOOLEAN, ENUM, INT16, INT32, INT8, NUMBER, STRING, TEXT, TIMESTAMP, UINT16, UINT32, UINT8, VECTOR, JSON, CARDINALITY, } from '@based/schema/def';
|
|
2
2
|
import { QueryDefType } from '../types.js';
|
|
3
3
|
import { read, readUtf8 } from '../../string.js';
|
|
4
|
-
import { readDoubleLE, readFloatLE, readInt16, readInt32, readUint16, readUint32, } from '@saulx/utils';
|
|
4
|
+
import { DECODER, readDoubleLE, readFloatLE, readInt16, readInt32, readUint16, readUint32, setByPath, } from '@saulx/utils';
|
|
5
5
|
import { inverseLangMap } from '@based/schema';
|
|
6
|
-
import { READ_EDGE, READ_ID, READ_REFERENCE, READ_REFERENCES,
|
|
6
|
+
import { READ_EDGE, READ_ID, READ_REFERENCE, READ_REFERENCES,
|
|
7
|
+
// AggFlag,
|
|
8
|
+
} from '../types.js';
|
|
7
9
|
const addField = (p, value, item, defaultOnly = false, lang = 0) => {
|
|
8
10
|
let i = p.__isEdge === true ? 1 : 0;
|
|
9
11
|
// TODO OPTMIZE
|
|
@@ -125,45 +127,43 @@ const readMain = (q, result, offset, item) => {
|
|
|
125
127
|
return i - offset;
|
|
126
128
|
};
|
|
127
129
|
const handleUndefinedProps = (id, q, item) => {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
lan[locale] = prop.default[locale] || '';
|
|
143
|
-
}
|
|
130
|
+
for (const k in q.include.propsRead) {
|
|
131
|
+
if (q.include.propsRead[k] !== id) {
|
|
132
|
+
// Only relevant for seperate props
|
|
133
|
+
const prop = q.schema.reverseProps[k];
|
|
134
|
+
if (prop.typeIndex === CARDINALITY) {
|
|
135
|
+
addField(prop, 0, item);
|
|
136
|
+
}
|
|
137
|
+
else if (prop.typeIndex === TEXT && q.lang == 0) {
|
|
138
|
+
const lan = getEmptyField(prop, item);
|
|
139
|
+
const lang = q.include.langTextFields.get(prop.prop).codes;
|
|
140
|
+
if (lang.has(0)) {
|
|
141
|
+
for (const locale in q.schema.locales) {
|
|
142
|
+
if (lan[locale] == undefined) {
|
|
143
|
+
lan[locale] = prop.default[locale] || '';
|
|
144
144
|
}
|
|
145
145
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
for (const code of lang) {
|
|
149
|
+
const locale = inverseLangMap.get(code);
|
|
150
|
+
if (!lan[locale]) {
|
|
151
|
+
lan[locale] = prop.default[locale] || '';
|
|
152
152
|
}
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
}
|
|
156
|
+
else if (prop.typeIndex === BINARY) {
|
|
157
|
+
addField(prop, prop.default, item);
|
|
158
|
+
}
|
|
159
|
+
else if (prop.typeIndex === TEXT) {
|
|
160
|
+
addField(prop, '', item);
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
1;
|
|
164
|
+
if (prop.default !== undefined) {
|
|
156
165
|
addField(prop, prop.default, item);
|
|
157
166
|
}
|
|
158
|
-
else if (prop.typeIndex === TEXT) {
|
|
159
|
-
addField(prop, '', item);
|
|
160
|
-
}
|
|
161
|
-
else {
|
|
162
|
-
1;
|
|
163
|
-
if (prop.default !== undefined) {
|
|
164
|
-
addField(prop, prop.default, item);
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
167
|
}
|
|
168
168
|
}
|
|
169
169
|
}
|
|
@@ -290,27 +290,6 @@ export const readAllFields = (q, result, offset, end, item, id) => {
|
|
|
290
290
|
addField(ref.target.propDef, refs, item);
|
|
291
291
|
i += size + 4;
|
|
292
292
|
}
|
|
293
|
-
else if (index === CREATE_AGGREGATION) {
|
|
294
|
-
i--;
|
|
295
|
-
result[i] = READ_AGGREGATION;
|
|
296
|
-
result[0] = result[0] + 1;
|
|
297
|
-
q.aggregation = 255 /* AggFlag.TEMP */;
|
|
298
|
-
return i - offset - 4 - (q.search ? 4 : 0);
|
|
299
|
-
}
|
|
300
|
-
else if (index === READ_AGGREGATION) {
|
|
301
|
-
// TODO: Change to a map and also to get the aggregate field name from a query function parameter
|
|
302
|
-
const propAgg = {
|
|
303
|
-
name: 'count',
|
|
304
|
-
path: ['count'],
|
|
305
|
-
typeIndex: UINT32,
|
|
306
|
-
};
|
|
307
|
-
const size = readUint32(result, i);
|
|
308
|
-
addField(propAgg, readUint32(result, i + 4), item);
|
|
309
|
-
result[0] = result[0] - 1;
|
|
310
|
-
i--;
|
|
311
|
-
result[i] = CREATE_AGGREGATION;
|
|
312
|
-
i += 4 + size + 4;
|
|
313
|
-
}
|
|
314
293
|
else if (index === 0) {
|
|
315
294
|
i += readMain(q, result, i, item);
|
|
316
295
|
}
|
|
@@ -391,6 +370,32 @@ export const readAllFields = (q, result, offset, end, item, id) => {
|
|
|
391
370
|
};
|
|
392
371
|
let cnt = 0;
|
|
393
372
|
export const resultToObject = (q, result, end, offset = 0) => {
|
|
373
|
+
if (q.aggregate) {
|
|
374
|
+
const results = {};
|
|
375
|
+
if (q.aggregate.groupBy) {
|
|
376
|
+
// key size = 2 for now... not perfect...
|
|
377
|
+
let i = 0;
|
|
378
|
+
while (i < result.byteLength - 4) {
|
|
379
|
+
const key = DECODER.decode(result.subarray(i, i + 2));
|
|
380
|
+
i += 2;
|
|
381
|
+
const resultKey = (results[key] = {});
|
|
382
|
+
for (const aggregatesArray of q.aggregate.aggregates.values()) {
|
|
383
|
+
for (const agg of aggregatesArray) {
|
|
384
|
+
setByPath(resultKey, agg.propDef.path, readUint32(result, agg.resultPos + i));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
i += q.aggregate.totalResultsPos;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
for (const aggregatesArray of q.aggregate.aggregates.values()) {
|
|
392
|
+
for (const agg of aggregatesArray) {
|
|
393
|
+
setByPath(results, agg.propDef.path, readUint32(result, agg.resultPos));
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
return results;
|
|
398
|
+
}
|
|
394
399
|
const len = readUint32(result, offset);
|
|
395
400
|
if (len === 0) {
|
|
396
401
|
if ('id' in q.target || 'alias' in q.target) {
|
|
@@ -403,16 +408,9 @@ export const resultToObject = (q, result, end, offset = 0) => {
|
|
|
403
408
|
while (i < end) {
|
|
404
409
|
const id = readUint32(result, i);
|
|
405
410
|
i += 4;
|
|
406
|
-
let item
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
q.aggregation = 4 /* AggFlag.COUNT */;
|
|
410
|
-
}
|
|
411
|
-
else {
|
|
412
|
-
item = {
|
|
413
|
-
id,
|
|
414
|
-
};
|
|
415
|
-
}
|
|
411
|
+
let item = {
|
|
412
|
+
id,
|
|
413
|
+
};
|
|
416
414
|
if (q.search) {
|
|
417
415
|
item.$searchScore = readFloatLE(result, i);
|
|
418
416
|
i += 4;
|
|
@@ -3,8 +3,8 @@ import { QueryDefType, QueryType } from './types.js';
|
|
|
3
3
|
import { includeToBuffer } from './include/toBuffer.js';
|
|
4
4
|
import { filterToBuffer } from './query.js';
|
|
5
5
|
import { searchToBuffer } from './search/index.js';
|
|
6
|
-
import { createAggFlagBuffer } from './aggregation.js';
|
|
7
6
|
import { ENCODER } from '@saulx/utils';
|
|
7
|
+
import { aggregateToBuffer } from './aggregates/aggregation.js';
|
|
8
8
|
const byteSize = (arr) => {
|
|
9
9
|
return arr.reduce((a, b) => {
|
|
10
10
|
return a + b.byteLength;
|
|
@@ -20,7 +20,6 @@ export function defToBuffer(db, def) {
|
|
|
20
20
|
def.errors.push(...ref.errors);
|
|
21
21
|
}
|
|
22
22
|
});
|
|
23
|
-
const aggregation = createAggFlagBuffer(def.aggregation || 0 /* AggFlag.NONE */);
|
|
24
23
|
let edges;
|
|
25
24
|
let edgesSize = 0;
|
|
26
25
|
if (def.edges) {
|
|
@@ -37,6 +36,40 @@ export function defToBuffer(db, def) {
|
|
|
37
36
|
// ---------------------------------------
|
|
38
37
|
// move down and will handle size after store the size Var
|
|
39
38
|
// only for references | edges
|
|
39
|
+
if (def.aggregate) {
|
|
40
|
+
const aggregateSize = def.aggregate.size || 0;
|
|
41
|
+
if (aggregateSize === 0) {
|
|
42
|
+
throw new Error('Wrong aggregate size (0)');
|
|
43
|
+
}
|
|
44
|
+
const filterSize = def.filter.size || 0;
|
|
45
|
+
const buf = new Uint8Array(16 + filterSize + aggregateSize);
|
|
46
|
+
buf[0] = QueryType.aggregates;
|
|
47
|
+
buf[1] = def.schema.idUint8[0];
|
|
48
|
+
buf[2] = def.schema.idUint8[1];
|
|
49
|
+
buf[3] = def.range.offset;
|
|
50
|
+
buf[4] = def.range.offset >>> 8;
|
|
51
|
+
buf[5] = def.range.offset >>> 16;
|
|
52
|
+
buf[6] = def.range.offset >>> 24;
|
|
53
|
+
buf[7] = def.range.limit;
|
|
54
|
+
buf[8] = def.range.limit >>> 8;
|
|
55
|
+
buf[9] = def.range.limit >>> 16;
|
|
56
|
+
buf[10] = def.range.limit >>> 24;
|
|
57
|
+
buf[11] = filterSize;
|
|
58
|
+
buf[12] = filterSize >>> 8;
|
|
59
|
+
if (filterSize) {
|
|
60
|
+
buf.set(filterToBuffer(def.filter), 13);
|
|
61
|
+
}
|
|
62
|
+
const aggregateBuffer = aggregateToBuffer(def.aggregate);
|
|
63
|
+
buf[14 + filterSize] = aggregateSize;
|
|
64
|
+
buf[15 + filterSize] = aggregateSize >>> 8;
|
|
65
|
+
buf.set(aggregateBuffer, 16 + filterSize);
|
|
66
|
+
result.push(buf);
|
|
67
|
+
// ignore this for now...
|
|
68
|
+
// result.push(...include)
|
|
69
|
+
// console.log('toBuffer > result: ', result)
|
|
70
|
+
// later this has to be a branch
|
|
71
|
+
return result;
|
|
72
|
+
}
|
|
40
73
|
if (def.type === QueryDefType.Root) {
|
|
41
74
|
let search;
|
|
42
75
|
let searchSize = 0;
|
|
@@ -130,7 +163,7 @@ export function defToBuffer(db, def) {
|
|
|
130
163
|
result.push(buf);
|
|
131
164
|
}
|
|
132
165
|
else {
|
|
133
|
-
const buf = new Uint8Array(
|
|
166
|
+
const buf = new Uint8Array(17 + filterSize + sortSize + searchSize);
|
|
134
167
|
buf[0] = QueryType.default;
|
|
135
168
|
buf[1] = def.schema.idUint8[0];
|
|
136
169
|
buf[2] = def.schema.idUint8[1];
|
|
@@ -157,7 +190,6 @@ export function defToBuffer(db, def) {
|
|
|
157
190
|
if (searchSize) {
|
|
158
191
|
buf.set(search, 17 + filterSize + sortSize);
|
|
159
192
|
}
|
|
160
|
-
buf.set(aggregation, 17 + filterSize + sortSize + searchSize);
|
|
161
193
|
result.push(buf);
|
|
162
194
|
}
|
|
163
195
|
}
|
|
@@ -170,8 +202,8 @@ export function defToBuffer(db, def) {
|
|
|
170
202
|
}
|
|
171
203
|
const sortSize = sort?.byteLength ?? 0;
|
|
172
204
|
const modsSize = filterSize + sortSize;
|
|
173
|
-
const meta = new Uint8Array(modsSize + 10 + 8
|
|
174
|
-
const sz = size + 7 + modsSize + 8
|
|
205
|
+
const meta = new Uint8Array(modsSize + 10 + 8);
|
|
206
|
+
const sz = size + 7 + modsSize + 8;
|
|
175
207
|
meta[0] = 254;
|
|
176
208
|
meta[1] = sz;
|
|
177
209
|
meta[2] = sz >>> 8;
|
|
@@ -196,7 +228,6 @@ export function defToBuffer(db, def) {
|
|
|
196
228
|
meta[15 + modsSize] = def.schema.idUint8[0];
|
|
197
229
|
meta[15 + 1 + modsSize] = def.schema.idUint8[1];
|
|
198
230
|
meta[15 + 2 + modsSize] = def.target.propDef.prop;
|
|
199
|
-
meta[15 + 3 + modsSize] = aggregation[0];
|
|
200
231
|
result.push(meta);
|
|
201
232
|
}
|
|
202
233
|
else if (def.type === QueryDefType.Reference) {
|
|
@@ -2,6 +2,7 @@ import { LangCode } from '@based/schema';
|
|
|
2
2
|
import { PropDef, PropDefEdge, SchemaTypeDef } from '@based/schema/def';
|
|
3
3
|
import { FilterOpts } from './filter/types.js';
|
|
4
4
|
import { QueryError } from './validation.js';
|
|
5
|
+
import { AggregateType } from './aggregates/types.js';
|
|
5
6
|
export type MainIncludes = {
|
|
6
7
|
[start: string]: [number, PropDef];
|
|
7
8
|
};
|
|
@@ -72,10 +73,22 @@ export type QueryDefSort = {
|
|
|
72
73
|
order: 0 | 1;
|
|
73
74
|
lang: LangCode;
|
|
74
75
|
};
|
|
76
|
+
export type Aggregation = {
|
|
77
|
+
type: AggregateType;
|
|
78
|
+
propDef: PropDef;
|
|
79
|
+
resultPos: number;
|
|
80
|
+
};
|
|
81
|
+
export type QueryDefAggregation = {
|
|
82
|
+
size: number;
|
|
83
|
+
groupBy?: PropDef;
|
|
84
|
+
aggregates: Map<number, Aggregation[]>;
|
|
85
|
+
totalResultsPos: number;
|
|
86
|
+
};
|
|
75
87
|
export type QueryDefShared = {
|
|
76
88
|
errors: QueryError[];
|
|
77
89
|
lang: LangCode;
|
|
78
90
|
filter: QueryDefFilter;
|
|
91
|
+
aggregate: null | QueryDefAggregation;
|
|
79
92
|
search: null | QueryDefSearch;
|
|
80
93
|
sort: null | QueryDefSort;
|
|
81
94
|
skipValidation: boolean;
|
|
@@ -100,7 +113,6 @@ export type QueryDefShared = {
|
|
|
100
113
|
};
|
|
101
114
|
references: Map<number, QueryDef>;
|
|
102
115
|
edges?: QueryDef;
|
|
103
|
-
aggregation: AggFlag;
|
|
104
116
|
};
|
|
105
117
|
export type QueryDefEdges = {
|
|
106
118
|
type: QueryDefType.Edge;
|
|
@@ -126,21 +138,3 @@ export declare const READ_ID = 255;
|
|
|
126
138
|
export declare const READ_EDGE = 252;
|
|
127
139
|
export declare const READ_REFERENCES = 253;
|
|
128
140
|
export declare const READ_REFERENCE = 254;
|
|
129
|
-
export declare const CREATE_AGGREGATION = 250;
|
|
130
|
-
export declare const READ_AGGREGATION = 251;
|
|
131
|
-
export declare const enum AggFlag {
|
|
132
|
-
NONE = 0,
|
|
133
|
-
AVG = 1,
|
|
134
|
-
CARDINALITY = 2,
|
|
135
|
-
CONCAT = 3,// string aggregation, delimiter should be an argument
|
|
136
|
-
COUNT = 4,
|
|
137
|
-
MAX = 5,
|
|
138
|
-
MIN = 6,
|
|
139
|
-
MODE = 7,// ordered-set
|
|
140
|
-
PERCENTILE = 8,// continuous or discrete should be optional parameters, default = discrete
|
|
141
|
-
RANK = 9,// hypothetical-set, dense should be optional parameter
|
|
142
|
-
STDDEV = 10,// population or sample should be optional parameters, default = sample
|
|
143
|
-
SUM = 11,
|
|
144
|
-
VARIANCE = 12,
|
|
145
|
-
TEMP = 255
|
|
146
|
-
}
|
package/dist/src/index.d.ts
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -24,7 +24,10 @@ export class BasedDb {
|
|
|
24
24
|
constructor(opts) {
|
|
25
25
|
this.#init(opts);
|
|
26
26
|
if (opts.debug) {
|
|
27
|
-
if (opts.debug === '
|
|
27
|
+
if (opts.debug === 'client') {
|
|
28
|
+
debugServer(this.server);
|
|
29
|
+
}
|
|
30
|
+
else if (opts.debug === 'server') {
|
|
28
31
|
debugServer(this.server);
|
|
29
32
|
}
|
|
30
33
|
else {
|
package/dist/src/server/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { Worker, MessageChannel } from 'node:worker_threads';
|
|
|
11
11
|
import { fileURLToPath } from 'node:url';
|
|
12
12
|
import { setTimeout } from 'node:timers/promises';
|
|
13
13
|
import { migrate } from './migrate/index.js';
|
|
14
|
-
import { debugServer } from '../utils.js';
|
|
14
|
+
import { debugServer, schemaLooseEqual } from '../utils.js';
|
|
15
15
|
export const SCHEMA_FILE = 'schema.json';
|
|
16
16
|
export const WRITELOG_FILE = 'writelog.json';
|
|
17
17
|
const __filename = fileURLToPath(import.meta.url);
|
|
@@ -87,7 +87,7 @@ export class DbServer {
|
|
|
87
87
|
processingQueries = 0;
|
|
88
88
|
modifyQueue = [];
|
|
89
89
|
queryQueue = new Map();
|
|
90
|
-
stopped;
|
|
90
|
+
stopped; // = true does not work
|
|
91
91
|
onSchemaChange;
|
|
92
92
|
unlistenExit;
|
|
93
93
|
saveIntervalInSeconds;
|
|
@@ -119,6 +119,7 @@ export class DbServer {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
start(opts) {
|
|
122
|
+
this.stopped = false;
|
|
122
123
|
return start(this, opts);
|
|
123
124
|
}
|
|
124
125
|
save(opts) {
|
|
@@ -283,6 +284,9 @@ export class DbServer {
|
|
|
283
284
|
}
|
|
284
285
|
setSchema(strictSchema, fromStart = false, transformFns) {
|
|
285
286
|
if (!fromStart && Object.keys(this.schema.types).length > 0) {
|
|
287
|
+
if (schemaLooseEqual(strictSchema, this.schema)) {
|
|
288
|
+
return this.schema;
|
|
289
|
+
}
|
|
286
290
|
return this.migrateSchema(strictSchema, transformFns);
|
|
287
291
|
}
|
|
288
292
|
const { lastId } = this.schema;
|
|
@@ -404,6 +408,10 @@ export class DbServer {
|
|
|
404
408
|
return offsets;
|
|
405
409
|
}
|
|
406
410
|
#modify(buf) {
|
|
411
|
+
if (this.stopped) {
|
|
412
|
+
console.error('Db is stopped - trying to modify');
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
407
415
|
const end = buf.length - 4;
|
|
408
416
|
const dataLen = readUint32LE(buf, end);
|
|
409
417
|
let typesSize = readUint16LE(buf, dataLen);
|
|
@@ -452,6 +460,10 @@ export class DbServer {
|
|
|
452
460
|
this.queryQueue.set(resolve, buf);
|
|
453
461
|
}
|
|
454
462
|
getQueryBuf(buf, fromQueue = false) {
|
|
463
|
+
if (this.stopped) {
|
|
464
|
+
console.error('Db is stopped - trying to query', buf.byteLength);
|
|
465
|
+
return Promise.resolve(new Uint8Array(8));
|
|
466
|
+
}
|
|
455
467
|
if (this.modifyQueue.length) {
|
|
456
468
|
return new Promise((resolve) => {
|
|
457
469
|
this.addToQueryQueue(resolve, buf);
|
|
@@ -533,7 +545,7 @@ export class DbServer {
|
|
|
533
545
|
await Promise.all(this.workers.map(({ worker }) => worker.terminate()));
|
|
534
546
|
this.workers = [];
|
|
535
547
|
native.stop(this.dbCtxExternal);
|
|
536
|
-
await setTimeout(
|
|
548
|
+
await setTimeout(100);
|
|
537
549
|
}
|
|
538
550
|
catch (e) {
|
|
539
551
|
this.stopped = false;
|
package/dist/src/utils.d.ts
CHANGED
|
@@ -3,3 +3,4 @@ export declare const DECODER: TextDecoder;
|
|
|
3
3
|
export declare const ENCODER: TextEncoder;
|
|
4
4
|
export declare const debugMode: (target: any, getInfo?: any) => void;
|
|
5
5
|
export declare const debugServer: (server: DbServer) => void;
|
|
6
|
+
export declare const schemaLooseEqual: (a: any, b: any, key?: string) => boolean;
|
package/dist/src/utils.js
CHANGED
|
@@ -43,4 +43,51 @@ export const debugMode = (target, getInfo = null) => {
|
|
|
43
43
|
}
|
|
44
44
|
};
|
|
45
45
|
export const debugServer = (server) => debugMode(server, () => `p: ${server.processingQueries} m: ${server.modifyQueue.length} q: ${server.queryQueue.size}`);
|
|
46
|
+
const exclude = new Set(['id', 'lastId']);
|
|
47
|
+
export const schemaLooseEqual = (a, b, key) => {
|
|
48
|
+
if (a === b) {
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
const typeofA = typeof a;
|
|
52
|
+
if (typeofA !== 'object') {
|
|
53
|
+
return exclude.has(key);
|
|
54
|
+
}
|
|
55
|
+
const typeofB = typeof b;
|
|
56
|
+
if (typeofA !== typeofB) {
|
|
57
|
+
return exclude.has(key);
|
|
58
|
+
}
|
|
59
|
+
if (a === null || b === null) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
if (a.constructor !== b.constructor) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
if (Array.isArray(a)) {
|
|
66
|
+
let i = a.length;
|
|
67
|
+
if (i !== b.length) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
while (i--) {
|
|
71
|
+
if (!schemaLooseEqual(a[i], b[i])) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
for (const k in a) {
|
|
78
|
+
if (!schemaLooseEqual(a[k], b[k], k)) {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
for (const k in b) {
|
|
83
|
+
if (k in a) {
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
if (!schemaLooseEqual(a[k], b[k], k)) {
|
|
87
|
+
return false;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
return true;
|
|
92
|
+
};
|
|
46
93
|
//# sourceMappingURL=utils.js.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@based/db",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.37",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/src/index.js",
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
"basedDbNative.cjs"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@based/schema": "5.0.0-alpha.
|
|
41
|
+
"@based/schema": "5.0.0-alpha.13",
|
|
42
42
|
"@saulx/hash": "^3.0.0",
|
|
43
|
-
"@saulx/utils": "^6.
|
|
43
|
+
"@saulx/utils": "^6.6.0",
|
|
44
44
|
"exit-hook": "^4.0.0",
|
|
45
45
|
"picocolors": "^1.1.0",
|
|
46
46
|
"@based/crc32c": "^1.0.0"
|