@based/db 0.0.42 → 0.0.44
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 +0 -2
- 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/libnode-v24.node +0 -0
- package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
- package/dist/lib/linux_aarch64/include/selva/fields.h +0 -2
- 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/libnode-v24.node +0 -0
- package/dist/lib/linux_aarch64/libselva.so +0 -0
- package/dist/lib/linux_x86_64/include/selva/fields.h +0 -2
- 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/libnode-v24.node +0 -0
- package/dist/lib/linux_x86_64/libselva.so +0 -0
- package/dist/src/client/flushModify.d.ts +2 -0
- package/dist/src/client/flushModify.js +60 -31
- package/dist/src/client/index.d.ts +5 -5
- package/dist/src/client/index.js +14 -8
- package/dist/src/client/query/BasedDbQuery.d.ts +9 -2
- package/dist/src/client/query/BasedDbQuery.js +78 -7
- package/dist/src/client/query/aggregates/aggregation.d.ts +1 -0
- package/dist/src/client/query/aggregates/aggregation.js +26 -0
- package/dist/src/client/query/display.js +3 -2
- package/dist/src/client/query/registerQuery.js +1 -0
- package/dist/src/client/query/toBuffer.js +15 -8
- package/dist/src/client/query/types.d.ts +3 -1
- package/dist/src/client/query/types.js +1 -0
- package/dist/src/index.js +8 -1
- package/dist/src/server/index.d.ts +2 -2
- package/dist/src/server/index.js +24 -22
- package/package.json +3 -3
|
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
|
|
|
377
377
|
|
|
378
378
|
/**
|
|
379
379
|
* Destroy all fields of a node.
|
|
380
|
-
* This will set nr_fields = 0, making setting new field values impossible
|
|
381
|
-
* regardless wether the schema defines fields for this node.
|
|
382
380
|
*/
|
|
383
381
|
SELVA_EXPORT
|
|
384
382
|
void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
|
|
|
377
377
|
|
|
378
378
|
/**
|
|
379
379
|
* Destroy all fields of a node.
|
|
380
|
-
* This will set nr_fields = 0, making setting new field values impossible
|
|
381
|
-
* regardless wether the schema defines fields for this node.
|
|
382
380
|
*/
|
|
383
381
|
SELVA_EXPORT
|
|
384
382
|
void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -377,8 +377,6 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
|
|
|
377
377
|
|
|
378
378
|
/**
|
|
379
379
|
* Destroy all fields of a node.
|
|
380
|
-
* This will set nr_fields = 0, making setting new field values impossible
|
|
381
|
-
* regardless wether the schema defines fields for this node.
|
|
382
380
|
*/
|
|
383
381
|
SELVA_EXPORT
|
|
384
382
|
void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx)
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -27,6 +27,8 @@ export declare class ModifyCtx {
|
|
|
27
27
|
markTypeDirty(schema: SchemaTypeDef): void;
|
|
28
28
|
updateMax(): void;
|
|
29
29
|
getData(lastIds: Record<number, number>): Uint8Array;
|
|
30
|
+
reset(): void;
|
|
30
31
|
}
|
|
32
|
+
export declare const execCtxQueue: (resCtx: ModifyCtx["ctx"], error?: boolean) => void;
|
|
31
33
|
export declare const flushBuffer: (db: DbClient) => Promise<void>;
|
|
32
34
|
export declare const startDrain: (db: DbClient) => void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { writeUint64 } from '@saulx/utils';
|
|
1
2
|
// TODO This definitely shouldn't be copy-pasted here from server/tree.ts
|
|
2
3
|
const makeCsmtKeyFromNodeId = (typeId, blockCapacity, nodeId) => {
|
|
3
4
|
const tmp = nodeId - +!(nodeId % blockCapacity);
|
|
@@ -8,9 +9,10 @@ export class ModifyCtx {
|
|
|
8
9
|
this.max = db.maxModifySize;
|
|
9
10
|
this.db = db;
|
|
10
11
|
this.buf = new Uint8Array(db.maxModifySize);
|
|
12
|
+
this.reset();
|
|
11
13
|
}
|
|
12
14
|
// default values
|
|
13
|
-
len =
|
|
15
|
+
len = 8;
|
|
14
16
|
id = -1;
|
|
15
17
|
hasSortField = -1;
|
|
16
18
|
hasSortText = -1;
|
|
@@ -79,17 +81,50 @@ export class ModifyCtx {
|
|
|
79
81
|
view.setFloat64(i, key, true);
|
|
80
82
|
i += 8;
|
|
81
83
|
}
|
|
82
|
-
|
|
83
|
-
data[i++] =
|
|
84
|
-
data[i++] =
|
|
85
|
-
data[i++] =
|
|
84
|
+
const lenMinusSchemaHash = this.len - 8;
|
|
85
|
+
data[i++] = lenMinusSchemaHash;
|
|
86
|
+
data[i++] = lenMinusSchemaHash >>> 8;
|
|
87
|
+
data[i++] = lenMinusSchemaHash >>> 16;
|
|
88
|
+
data[i++] = lenMinusSchemaHash >>> 24;
|
|
86
89
|
return data;
|
|
87
90
|
}
|
|
91
|
+
reset() {
|
|
92
|
+
this.dirtyTypes.clear();
|
|
93
|
+
this.dirtyRanges.clear();
|
|
94
|
+
writeUint64(this.buf, this.db.schema?.hash || 0, 0);
|
|
95
|
+
this.len = 8;
|
|
96
|
+
this.prefix0 = -1;
|
|
97
|
+
this.prefix1 = -1;
|
|
98
|
+
this.lastMain = -1;
|
|
99
|
+
// should these also be reset in setcursor?
|
|
100
|
+
this.hasSortText = -1;
|
|
101
|
+
this.hasSortField = -1;
|
|
102
|
+
this.max = this.db.maxModifySize;
|
|
103
|
+
this.ctx = {};
|
|
104
|
+
}
|
|
88
105
|
}
|
|
106
|
+
export const execCtxQueue = (resCtx, error) => {
|
|
107
|
+
if (resCtx.queue?.size) {
|
|
108
|
+
const queue = resCtx.queue;
|
|
109
|
+
resCtx.queue = null;
|
|
110
|
+
if (error) {
|
|
111
|
+
for (const [resolve] of queue) {
|
|
112
|
+
// should we throw?
|
|
113
|
+
resolve(null);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
for (const [resolve, res] of queue) {
|
|
118
|
+
resolve(res.getId());
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
89
123
|
export const flushBuffer = (db) => {
|
|
90
124
|
const ctx = db.modifyCtx;
|
|
91
125
|
let flushPromise;
|
|
92
|
-
if (ctx.len) {
|
|
126
|
+
if (ctx.len > 8) {
|
|
127
|
+
// always has schema hash
|
|
93
128
|
const lastIds = {};
|
|
94
129
|
const data = ctx.getData(lastIds);
|
|
95
130
|
const resCtx = ctx.ctx;
|
|
@@ -98,37 +133,31 @@ export const flushBuffer = (db) => {
|
|
|
98
133
|
.flushModify(data)
|
|
99
134
|
.then(({ offsets, dbWriteTime }) => {
|
|
100
135
|
db.writeTime += dbWriteTime ?? 0;
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
136
|
+
if (offsets) {
|
|
137
|
+
resCtx.offsets = offsets;
|
|
138
|
+
for (const typeId in lastIds) {
|
|
139
|
+
if (typeId in offsets) {
|
|
140
|
+
const lastId = lastIds[typeId] + offsets[typeId];
|
|
141
|
+
const def = db.schemaTypesParsedById[typeId];
|
|
142
|
+
const delta = lastId - def.lastId;
|
|
143
|
+
if (delta > 0) {
|
|
144
|
+
def.lastId += delta;
|
|
145
|
+
def.total += delta;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
else {
|
|
149
|
+
console.error('Panic: No offset returned in flushModify');
|
|
110
150
|
}
|
|
111
151
|
}
|
|
112
|
-
|
|
113
|
-
console.error('Panic: No offset returned in flushModify');
|
|
114
|
-
}
|
|
152
|
+
execCtxQueue(resCtx);
|
|
115
153
|
}
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
resCtx
|
|
119
|
-
for (const [resolve, res] of queue) {
|
|
120
|
-
resolve(res.getId());
|
|
121
|
-
}
|
|
154
|
+
else {
|
|
155
|
+
console.info('Modify cancelled - schema mismatch');
|
|
156
|
+
execCtxQueue(resCtx, true);
|
|
122
157
|
}
|
|
123
158
|
db.flushReady();
|
|
124
159
|
});
|
|
125
|
-
ctx.
|
|
126
|
-
ctx.dirtyRanges.clear();
|
|
127
|
-
ctx.len = 0;
|
|
128
|
-
ctx.prefix0 = -1;
|
|
129
|
-
ctx.prefix1 = -1;
|
|
130
|
-
ctx.max = db.maxModifySize;
|
|
131
|
-
ctx.ctx = {};
|
|
160
|
+
ctx.reset();
|
|
132
161
|
}
|
|
133
162
|
else {
|
|
134
163
|
db.flushReady();
|
|
@@ -23,9 +23,7 @@ type DbClientOpts = {
|
|
|
23
23
|
flushTime?: number;
|
|
24
24
|
debug?: boolean;
|
|
25
25
|
};
|
|
26
|
-
type DbClientSchema =
|
|
27
|
-
lastId: number;
|
|
28
|
-
};
|
|
26
|
+
type DbClientSchema = DbServer['schema'];
|
|
29
27
|
export declare class DbClient {
|
|
30
28
|
constructor({ hooks, maxModifySize, flushTime, debug, }: DbClientOpts);
|
|
31
29
|
flushTime: number;
|
|
@@ -43,11 +41,13 @@ export declare class DbClient {
|
|
|
43
41
|
o: Record<string, any>;
|
|
44
42
|
p: Promise<number | ModifyRes>;
|
|
45
43
|
}>;
|
|
46
|
-
schemaChecksum: number;
|
|
47
44
|
schemaProcessing: number;
|
|
48
45
|
schemaPromise: Promise<DbServer['schema']>;
|
|
49
46
|
setSchema(schema: Schema, fromStart?: boolean, transformFns?: TransformFns): Promise<StrictSchema>;
|
|
50
|
-
putLocalSchema(schema:
|
|
47
|
+
putLocalSchema(schema: DbServer['schema']): StrictSchema & {
|
|
48
|
+
lastId: number;
|
|
49
|
+
hash?: number;
|
|
50
|
+
};
|
|
51
51
|
create(type: string, obj?: CreateObj, opts?: ModifyOpts): ModifyRes;
|
|
52
52
|
copy(type: string, target: number | ModifyRes, objOrTransformFn?: Record<string, any> | ((item: Record<string, any>) => Promise<any>)): Promise<ModifyRes>;
|
|
53
53
|
query(type: string, id?: number | ModifyRes | (number | ModifyRes)[] | QueryByAliasObj | QueryByAliasObj[] | Uint32Array): BasedDbQuery;
|
package/dist/src/client/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parse } from '@based/schema';
|
|
2
2
|
import { create } from './modify/create.js';
|
|
3
3
|
import { updateTypeDefs, schemaToSelvaBuffer, } from '@based/schema/def';
|
|
4
|
-
import { flushBuffer, ModifyCtx, startDrain } from './flushModify.js';
|
|
4
|
+
import { execCtxQueue, flushBuffer, ModifyCtx, startDrain, } from './flushModify.js';
|
|
5
5
|
import { BasedDbQuery } from './query/BasedDbQuery.js';
|
|
6
6
|
import { ModifyState } from './modify/ModifyRes.js';
|
|
7
7
|
import { upsert } from './modify/upsert.js';
|
|
@@ -43,7 +43,6 @@ export class DbClient {
|
|
|
43
43
|
modifyCtx;
|
|
44
44
|
maxModifySize;
|
|
45
45
|
upserting = new Map();
|
|
46
|
-
schemaChecksum;
|
|
47
46
|
schemaProcessing;
|
|
48
47
|
schemaPromise;
|
|
49
48
|
async setSchema(schema, fromStart, transformFns) {
|
|
@@ -52,6 +51,8 @@ export class DbClient {
|
|
|
52
51
|
if (schemaLooseEqual(strictSchema, this.schema)) {
|
|
53
52
|
return this.schema;
|
|
54
53
|
}
|
|
54
|
+
// drain current things
|
|
55
|
+
await this.drain();
|
|
55
56
|
const checksum = hash(strictSchema);
|
|
56
57
|
if (checksum !== this.schemaProcessing) {
|
|
57
58
|
this.schemaProcessing = checksum;
|
|
@@ -63,15 +64,20 @@ export class DbClient {
|
|
|
63
64
|
return this.putLocalSchema(remoteSchema);
|
|
64
65
|
}
|
|
65
66
|
putLocalSchema(schema) {
|
|
66
|
-
|
|
67
|
-
if (this.schemaChecksum === checksum) {
|
|
67
|
+
if (this.schema && this.schema.hash === schema.hash) {
|
|
68
68
|
return this.schema;
|
|
69
69
|
}
|
|
70
|
-
this.schemaChecksum = checksum;
|
|
71
70
|
this.schema = schema;
|
|
72
71
|
updateTypeDefs(this.schema, this.schemaTypesParsed, this.schemaTypesParsedById);
|
|
73
72
|
// Adds bidrectional refs on defs
|
|
74
73
|
schemaToSelvaBuffer(this.schemaTypesParsed);
|
|
74
|
+
// this has to happen before the listeners
|
|
75
|
+
if (this.modifyCtx.len > 8) {
|
|
76
|
+
console.info('Modify cancelled - schema updated');
|
|
77
|
+
}
|
|
78
|
+
const resCtx = this.modifyCtx.ctx;
|
|
79
|
+
this.modifyCtx.reset();
|
|
80
|
+
execCtxQueue(resCtx, true);
|
|
75
81
|
if (this.listeners?.schema) {
|
|
76
82
|
for (const cb of this.listeners.schema) {
|
|
77
83
|
cb(this.schema);
|
|
@@ -88,11 +94,11 @@ export class DbClient {
|
|
|
88
94
|
.get()
|
|
89
95
|
.toObject();
|
|
90
96
|
if (typeof objOrTransformFn === 'function') {
|
|
91
|
-
const { id, ...props } = await objOrTransformFn(item);
|
|
97
|
+
const { id: _, ...props } = await objOrTransformFn(item);
|
|
92
98
|
return this.create(type, props);
|
|
93
99
|
}
|
|
94
100
|
if (typeof objOrTransformFn === 'object' && objOrTransformFn !== null) {
|
|
95
|
-
const { id, ...props } = item;
|
|
101
|
+
const { id: _, ...props } = item;
|
|
96
102
|
await Promise.all(Object.keys(objOrTransformFn).map(async (key) => {
|
|
97
103
|
const val = objOrTransformFn[key];
|
|
98
104
|
if (val === null) {
|
|
@@ -113,7 +119,7 @@ export class DbClient {
|
|
|
113
119
|
}));
|
|
114
120
|
return this.create(type, props);
|
|
115
121
|
}
|
|
116
|
-
const { id, ...props } = item;
|
|
122
|
+
const { id: _, ...props } = item;
|
|
117
123
|
return this.create(type, props);
|
|
118
124
|
}
|
|
119
125
|
query(type, id) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QueryDef, Operator, QueryByAliasObj } from './query.js';
|
|
1
|
+
import { QueryDef, QueryTarget, Operator, QueryByAliasObj } from './query.js';
|
|
2
2
|
import { BasedQueryResponse } from './BasedIterable.js';
|
|
3
3
|
import { Search } from './search/index.js';
|
|
4
4
|
import { OnData, OnError } from './subscription/index.js';
|
|
@@ -8,9 +8,14 @@ import { FilterAst, FilterBranchFn, FilterOpts } from './filter/types.js';
|
|
|
8
8
|
export { QueryByAliasObj };
|
|
9
9
|
export type SelectFn = (field: string) => BasedDbReferenceQuery;
|
|
10
10
|
export type BranchInclude = (select: SelectFn) => any;
|
|
11
|
+
export type QueryCommand = {
|
|
12
|
+
method: string;
|
|
13
|
+
args: any[];
|
|
14
|
+
};
|
|
11
15
|
export declare class QueryBranch<T> {
|
|
12
16
|
db: DbClient;
|
|
13
17
|
def: QueryDef;
|
|
18
|
+
queryCommands: QueryCommand[];
|
|
14
19
|
constructor(db: DbClient, def: QueryDef);
|
|
15
20
|
sort(field: string, order?: 'asc' | 'desc'): T;
|
|
16
21
|
filter<O extends Operator>(field: string, operator?: O | boolean, value?: any, opts?: FilterOpts<O>): T;
|
|
@@ -35,7 +40,9 @@ declare class GetPromise extends Promise<BasedQueryResponse> {
|
|
|
35
40
|
export declare class BasedDbQuery extends QueryBranch<BasedDbQuery> {
|
|
36
41
|
#private;
|
|
37
42
|
skipValidation: boolean;
|
|
38
|
-
|
|
43
|
+
target: QueryTarget;
|
|
44
|
+
constructor(db: DbClient, type: string, rawTarget?: QueryByAliasObj | number | Uint32Array | (QueryByAliasObj | number)[], skipValidation?: boolean);
|
|
45
|
+
reBuildQuery(): void;
|
|
39
46
|
id: number;
|
|
40
47
|
get(): GetPromise;
|
|
41
48
|
buffer: Uint8Array;
|
|
@@ -15,16 +15,29 @@ import { concatUint8Arr } from '@saulx/utils';
|
|
|
15
15
|
export class QueryBranch {
|
|
16
16
|
db;
|
|
17
17
|
def;
|
|
18
|
+
queryCommands;
|
|
18
19
|
constructor(db, def) {
|
|
19
20
|
this.db = db;
|
|
20
21
|
this.def = def;
|
|
21
22
|
}
|
|
22
23
|
sort(field, order = 'asc') {
|
|
24
|
+
if (this.queryCommands) {
|
|
25
|
+
this.queryCommands.push({
|
|
26
|
+
method: 'filter',
|
|
27
|
+
args: [field, order],
|
|
28
|
+
});
|
|
29
|
+
}
|
|
23
30
|
sort(this.def, field, order);
|
|
24
31
|
// @ts-ignore
|
|
25
32
|
return this;
|
|
26
33
|
}
|
|
27
34
|
filter(field, operator, value, opts) {
|
|
35
|
+
if (this.queryCommands) {
|
|
36
|
+
this.queryCommands.push({
|
|
37
|
+
method: 'filter',
|
|
38
|
+
args: [field, operator, value, opts],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
28
41
|
const f = convertFilter(this.def, field, operator, value, opts);
|
|
29
42
|
if (!f) {
|
|
30
43
|
// @ts-ignore
|
|
@@ -35,11 +48,23 @@ export class QueryBranch {
|
|
|
35
48
|
return this;
|
|
36
49
|
}
|
|
37
50
|
filterBatch(f) {
|
|
51
|
+
if (this.queryCommands) {
|
|
52
|
+
this.queryCommands.push({
|
|
53
|
+
method: 'filterBatch',
|
|
54
|
+
args: [f],
|
|
55
|
+
});
|
|
56
|
+
}
|
|
38
57
|
filter(this.db, this.def, f, this.def.filter);
|
|
39
58
|
// @ts-ignore
|
|
40
59
|
return this;
|
|
41
60
|
}
|
|
42
61
|
search(query, field, opts, ...fields) {
|
|
62
|
+
if (this.queryCommands) {
|
|
63
|
+
this.queryCommands.push({
|
|
64
|
+
method: 'search',
|
|
65
|
+
args: [query, field, opts, ...fields],
|
|
66
|
+
});
|
|
67
|
+
}
|
|
43
68
|
if (ArrayBuffer.isView(query)) {
|
|
44
69
|
// @ts-ignore
|
|
45
70
|
vectorSearch(this.def, query, field, opts ?? {});
|
|
@@ -95,23 +120,47 @@ export class QueryBranch {
|
|
|
95
120
|
return this;
|
|
96
121
|
}
|
|
97
122
|
groupBy(field) {
|
|
123
|
+
if (this.queryCommands) {
|
|
124
|
+
this.queryCommands.push({
|
|
125
|
+
method: 'groupBy',
|
|
126
|
+
args: [field],
|
|
127
|
+
});
|
|
128
|
+
}
|
|
98
129
|
groupBy(this.def, field);
|
|
99
130
|
// only works with aggregates for now
|
|
100
131
|
// @ts-ignore
|
|
101
132
|
return this;
|
|
102
133
|
}
|
|
103
134
|
count(field = '$count') {
|
|
135
|
+
if (this.queryCommands) {
|
|
136
|
+
this.queryCommands.push({
|
|
137
|
+
method: 'count',
|
|
138
|
+
args: [field],
|
|
139
|
+
});
|
|
140
|
+
}
|
|
104
141
|
const p = field.split('.');
|
|
105
142
|
addAggregate(2 /* AggregateType.COUNT */, this.def, p);
|
|
106
143
|
// @ts-ignore
|
|
107
144
|
return this;
|
|
108
145
|
}
|
|
109
146
|
sum(...fields) {
|
|
147
|
+
if (this.queryCommands) {
|
|
148
|
+
this.queryCommands.push({
|
|
149
|
+
method: 'sum',
|
|
150
|
+
args: fields,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
110
153
|
addAggregate(1 /* AggregateType.SUM */, this.def, fields);
|
|
111
154
|
// @ts-ignore
|
|
112
155
|
return this;
|
|
113
156
|
}
|
|
114
157
|
or(field, operator, value, opts) {
|
|
158
|
+
if (this.queryCommands) {
|
|
159
|
+
this.queryCommands.push({
|
|
160
|
+
method: 'or',
|
|
161
|
+
args: [field, operator, value, opts],
|
|
162
|
+
});
|
|
163
|
+
}
|
|
115
164
|
if (typeof field === 'function') {
|
|
116
165
|
const f = new FilterBranch(this.db, filterOr(this.db, this.def, [], this.def.filter), this.def);
|
|
117
166
|
field(f);
|
|
@@ -127,6 +176,9 @@ export class QueryBranch {
|
|
|
127
176
|
return this;
|
|
128
177
|
}
|
|
129
178
|
range(start, end = DEF_RANGE_PROP_LIMIT) {
|
|
179
|
+
if (this.queryCommands) {
|
|
180
|
+
this.queryCommands.push({ method: 'range', args: [start, end] });
|
|
181
|
+
}
|
|
130
182
|
const offset = start;
|
|
131
183
|
const limit = end - start;
|
|
132
184
|
if (validateRange(this.def, offset, limit)) {
|
|
@@ -141,6 +193,9 @@ export class QueryBranch {
|
|
|
141
193
|
return this;
|
|
142
194
|
}
|
|
143
195
|
include(...fields) {
|
|
196
|
+
if (this.queryCommands) {
|
|
197
|
+
this.queryCommands.push({ method: 'include', args: fields });
|
|
198
|
+
}
|
|
144
199
|
for (const f of fields) {
|
|
145
200
|
if (typeof f === 'string') {
|
|
146
201
|
includeField(this.def, f);
|
|
@@ -206,22 +261,23 @@ class GetPromise extends Promise {
|
|
|
206
261
|
}
|
|
207
262
|
export class BasedDbQuery extends QueryBranch {
|
|
208
263
|
skipValidation = false;
|
|
209
|
-
|
|
264
|
+
target;
|
|
265
|
+
constructor(db, type, rawTarget, skipValidation) {
|
|
210
266
|
const target = {
|
|
211
267
|
type,
|
|
212
268
|
};
|
|
213
|
-
if (
|
|
214
|
-
if (isAlias(
|
|
215
|
-
target.alias =
|
|
269
|
+
if (rawTarget) {
|
|
270
|
+
if (isAlias(rawTarget)) {
|
|
271
|
+
target.alias = rawTarget;
|
|
216
272
|
}
|
|
217
273
|
else {
|
|
218
|
-
if (Array.isArray(
|
|
274
|
+
if (Array.isArray(rawTarget) || rawTarget instanceof Uint32Array) {
|
|
219
275
|
// TODO ADD MULTI ALIAS
|
|
220
276
|
// @ts-ignore
|
|
221
|
-
target.ids =
|
|
277
|
+
target.ids = rawTarget;
|
|
222
278
|
}
|
|
223
279
|
else {
|
|
224
|
-
target.id =
|
|
280
|
+
target.id = rawTarget;
|
|
225
281
|
}
|
|
226
282
|
}
|
|
227
283
|
}
|
|
@@ -230,6 +286,21 @@ export class BasedDbQuery extends QueryBranch {
|
|
|
230
286
|
}
|
|
231
287
|
const def = createQueryDef(db, QueryDefType.Root, target, skipValidation);
|
|
232
288
|
super(db, def);
|
|
289
|
+
this.db = db;
|
|
290
|
+
this.skipValidation = skipValidation;
|
|
291
|
+
this.queryCommands = [];
|
|
292
|
+
this.target = target;
|
|
293
|
+
}
|
|
294
|
+
reBuildQuery() {
|
|
295
|
+
this.id = undefined;
|
|
296
|
+
this.buffer = undefined;
|
|
297
|
+
const def = createQueryDef(this.db, QueryDefType.Root, this.target, this.skipValidation);
|
|
298
|
+
this.def = def;
|
|
299
|
+
const q = this.queryCommands;
|
|
300
|
+
this.queryCommands = [];
|
|
301
|
+
for (const command of q) {
|
|
302
|
+
this[command.method](...command.args);
|
|
303
|
+
}
|
|
233
304
|
}
|
|
234
305
|
#getInternal = async (resolve, reject) => {
|
|
235
306
|
if (!this.def.include.stringFields.size && !this.def.references.size) {
|
|
@@ -3,3 +3,4 @@ import { AggregateType } from './types.js';
|
|
|
3
3
|
export declare const aggregateToBuffer: (aggregates: QueryDefAggregation) => Uint8Array;
|
|
4
4
|
export declare const groupBy: (def: QueryDef, field: string) => void;
|
|
5
5
|
export declare const addAggregate: (type: AggregateType, def: QueryDef, fields: (string | string[])[]) => void;
|
|
6
|
+
export declare const isRootCountOnly: (def: QueryDef, filterSize: number) => boolean;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeUint16 } from '@saulx/utils';
|
|
2
|
+
import { QueryDefType } from '../types.js';
|
|
2
3
|
import { UINT32 } from '@based/schema/def';
|
|
3
4
|
import { aggregationFieldDoesNotExist } from '../validation.js';
|
|
4
5
|
export const aggregateToBuffer = (aggregates) => {
|
|
@@ -107,4 +108,29 @@ export const addAggregate = (type, def, fields) => {
|
|
|
107
108
|
}
|
|
108
109
|
}
|
|
109
110
|
};
|
|
111
|
+
export const isRootCountOnly = (def, filterSize) => {
|
|
112
|
+
if (filterSize != 0) {
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
if (def.type !== QueryDefType.Root) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
if (def.aggregate.groupBy) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
if (def.aggregate.aggregates.size !== 1) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
if (!def.aggregate.aggregates.has(255)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
const aggs = def.aggregate.aggregates.get(255);
|
|
128
|
+
if (aggs.length !== 1) {
|
|
129
|
+
return false;
|
|
130
|
+
}
|
|
131
|
+
if (aggs[0].type !== 2 /* AggregateType.COUNT */) {
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
return true;
|
|
135
|
+
};
|
|
110
136
|
//# sourceMappingURL=aggregation.js.map
|
|
@@ -215,8 +215,9 @@ const inspectObject = (object, q, path, level, isLast, isFirst, isObject, depth)
|
|
|
215
215
|
str += picocolors.blue(v);
|
|
216
216
|
str += picocolors.italic(picocolors.dim(` ${key.indexOf('count') >= 0 ? ' count' : ' sum'}`));
|
|
217
217
|
}
|
|
218
|
-
|
|
219
|
-
|
|
218
|
+
else {
|
|
219
|
+
str += picocolors.blue(v);
|
|
220
|
+
}
|
|
220
221
|
}
|
|
221
222
|
else if (typeof v === 'object' && v) {
|
|
222
223
|
inspectObject(v, q, key, level + 2, false, false, true, depth) + '';
|
|
@@ -4,6 +4,7 @@ import { defToBuffer } from './toBuffer.js';
|
|
|
4
4
|
import { handleErrors } from './validation.js';
|
|
5
5
|
export const registerQuery = (q) => {
|
|
6
6
|
if (!q.id) {
|
|
7
|
+
q.def.schemaChecksum = q.db.schema.hash;
|
|
7
8
|
const b = defToBuffer(q.db, q.def);
|
|
8
9
|
const buf = concatUint8Arr(b);
|
|
9
10
|
let id = native.crc32(buf);
|
|
@@ -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 { ENCODER } from '@saulx/utils';
|
|
7
|
-
import { aggregateToBuffer } from './aggregates/aggregation.js';
|
|
6
|
+
import { ENCODER, writeUint64 } from '@saulx/utils';
|
|
7
|
+
import { aggregateToBuffer, isRootCountOnly } from './aggregates/aggregation.js';
|
|
8
8
|
const byteSize = (arr) => {
|
|
9
9
|
return arr.reduce((a, b) => {
|
|
10
10
|
return a + b.byteLength;
|
|
@@ -33,9 +33,6 @@ export function defToBuffer(db, def) {
|
|
|
33
33
|
edgesSize = byteSize(edges);
|
|
34
34
|
}
|
|
35
35
|
const size = (edges ? edgesSize + 3 : 0) + byteSize(include);
|
|
36
|
-
// ---------------------------------------
|
|
37
|
-
// move down and will handle size after store the size Var
|
|
38
|
-
// only for references | edges
|
|
39
36
|
if (def.aggregate) {
|
|
40
37
|
const aggregateSize = def.aggregate.size || 0;
|
|
41
38
|
if (aggregateSize === 0) {
|
|
@@ -43,7 +40,9 @@ export function defToBuffer(db, def) {
|
|
|
43
40
|
}
|
|
44
41
|
const filterSize = def.filter.size || 0;
|
|
45
42
|
const buf = new Uint8Array(16 + filterSize + aggregateSize);
|
|
46
|
-
buf[0] =
|
|
43
|
+
buf[0] = isRootCountOnly(def, filterSize)
|
|
44
|
+
? QueryType.aggregatesCountType
|
|
45
|
+
: QueryType.aggregates;
|
|
47
46
|
buf[1] = def.schema.idUint8[0];
|
|
48
47
|
buf[2] = def.schema.idUint8[1];
|
|
49
48
|
buf[3] = def.range.offset;
|
|
@@ -66,8 +65,11 @@ export function defToBuffer(db, def) {
|
|
|
66
65
|
result.push(buf);
|
|
67
66
|
// ignore this for now...
|
|
68
67
|
// result.push(...include)
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
if (def.type === QueryDefType.Root) {
|
|
69
|
+
const checksum = new Uint8Array(8);
|
|
70
|
+
writeUint64(checksum, def.schemaChecksum ?? 0, 0);
|
|
71
|
+
result.push(checksum);
|
|
72
|
+
}
|
|
71
73
|
return result;
|
|
72
74
|
}
|
|
73
75
|
if (def.type === QueryDefType.Root) {
|
|
@@ -249,6 +251,11 @@ export function defToBuffer(db, def) {
|
|
|
249
251
|
metaEdgeBuffer[2] = edgesSize >>> 8;
|
|
250
252
|
result.push(metaEdgeBuffer, ...edges);
|
|
251
253
|
}
|
|
254
|
+
if (def.type === QueryDefType.Root) {
|
|
255
|
+
const checksum = new Uint8Array(8);
|
|
256
|
+
writeUint64(checksum, def.schemaChecksum ?? 0, 0);
|
|
257
|
+
result.push(checksum);
|
|
258
|
+
}
|
|
252
259
|
return result;
|
|
253
260
|
}
|
|
254
261
|
//# sourceMappingURL=toBuffer.js.map
|
|
@@ -12,7 +12,8 @@ export declare enum QueryType {
|
|
|
12
12
|
ids = 1,
|
|
13
13
|
default = 2,
|
|
14
14
|
alias = 3,
|
|
15
|
-
aggregates = 4
|
|
15
|
+
aggregates = 4,
|
|
16
|
+
aggregatesCountType = 5
|
|
16
17
|
}
|
|
17
18
|
declare enum QueryDefType {
|
|
18
19
|
Edge = 1,
|
|
@@ -85,6 +86,7 @@ export type QueryDefAggregation = {
|
|
|
85
86
|
totalResultsPos: number;
|
|
86
87
|
};
|
|
87
88
|
export type QueryDefShared = {
|
|
89
|
+
schemaChecksum?: number;
|
|
88
90
|
errors: QueryError[];
|
|
89
91
|
lang: LangCode;
|
|
90
92
|
filter: QueryDefFilter;
|
|
@@ -5,6 +5,7 @@ export var QueryType;
|
|
|
5
5
|
QueryType[QueryType["default"] = 2] = "default";
|
|
6
6
|
QueryType[QueryType["alias"] = 3] = "alias";
|
|
7
7
|
QueryType[QueryType["aggregates"] = 4] = "aggregates";
|
|
8
|
+
QueryType[QueryType["aggregatesCountType"] = 5] = "aggregatesCountType";
|
|
8
9
|
})(QueryType || (QueryType = {}));
|
|
9
10
|
var QueryDefType;
|
|
10
11
|
(function (QueryDefType) {
|
package/dist/src/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { DbClient } from './client/index.js';
|
|
|
5
5
|
import { wait } from '@saulx/utils';
|
|
6
6
|
import { debugMode, debugServer } from './utils.js';
|
|
7
7
|
import { BasedQueryResponse } from './client/query/BasedIterable.js';
|
|
8
|
+
import { registerQuery } from './client/query/registerQuery.js';
|
|
8
9
|
export * from './client/modify/modify.js';
|
|
9
10
|
export { compress, decompress };
|
|
10
11
|
export { ModifyCtx }; // TODO move this somewhere
|
|
@@ -57,6 +58,12 @@ export class BasedDb {
|
|
|
57
58
|
let lastLen = 0;
|
|
58
59
|
let response;
|
|
59
60
|
const get = async () => {
|
|
61
|
+
const schemaVersion = q.def.schemaChecksum;
|
|
62
|
+
if (schemaVersion && schemaVersion !== server.schema.hash) {
|
|
63
|
+
q.reBuildQuery();
|
|
64
|
+
registerQuery(q);
|
|
65
|
+
response = undefined;
|
|
66
|
+
}
|
|
60
67
|
const res = await server.getQueryBuf(q.buffer);
|
|
61
68
|
if (!response) {
|
|
62
69
|
response = new BasedQueryResponse(q.id, q.def, res, 0);
|
|
@@ -71,7 +78,7 @@ export class BasedDb {
|
|
|
71
78
|
lastLen = res.byteLength;
|
|
72
79
|
prevChecksum = checksum;
|
|
73
80
|
}
|
|
74
|
-
setTimeout(get, 200);
|
|
81
|
+
timer = setTimeout(get, 200);
|
|
75
82
|
};
|
|
76
83
|
get();
|
|
77
84
|
return () => {
|
|
@@ -23,7 +23,7 @@ export declare class DbWorker {
|
|
|
23
23
|
updateCtx(address: BigInt): Promise<void>;
|
|
24
24
|
getQueryBuf(buf: Uint8Array): Promise<Uint8Array>;
|
|
25
25
|
}
|
|
26
|
-
type OnSchemaChange = (schema:
|
|
26
|
+
type OnSchemaChange = (schema: DbServer['schema']) => void;
|
|
27
27
|
export declare class DbServer {
|
|
28
28
|
#private;
|
|
29
29
|
modifyDirtyRanges: Float64Array;
|
|
@@ -99,7 +99,7 @@ export declare class DbServer {
|
|
|
99
99
|
lastId: number;
|
|
100
100
|
hash?: number;
|
|
101
101
|
}>;
|
|
102
|
-
modify(buf: Uint8Array): Record<number, number
|
|
102
|
+
modify(buf: Uint8Array): Record<number, number> | null;
|
|
103
103
|
addToQueryQueue(resolve: any, buf: any): void;
|
|
104
104
|
getQueryBuf(buf: Uint8Array, fromQueue?: boolean): Promise<Uint8Array>;
|
|
105
105
|
onQueryEnd(): void;
|
package/dist/src/server/index.js
CHANGED
|
@@ -12,6 +12,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
12
12
|
import { setTimeout } from 'node:timers/promises';
|
|
13
13
|
import { migrate } from './migrate/index.js';
|
|
14
14
|
import { debugServer, schemaLooseEqual } from '../utils.js';
|
|
15
|
+
import { readUint16, readUint32, readUint64, writeUint64 } from '@saulx/utils';
|
|
15
16
|
import { hash } from '@saulx/hash';
|
|
16
17
|
export const SCHEMA_FILE = 'schema.json';
|
|
17
18
|
export const WRITELOG_FILE = 'writelog.json';
|
|
@@ -28,12 +29,6 @@ class SortIndex {
|
|
|
28
29
|
idx;
|
|
29
30
|
cnt = 0;
|
|
30
31
|
}
|
|
31
|
-
function readUint16LE(buf, off) {
|
|
32
|
-
return buf[off] | (buf[off + 1] << 8);
|
|
33
|
-
}
|
|
34
|
-
function readUint32LE(buf, off) {
|
|
35
|
-
return (buf[off] | (buf[off + 1] << 8) | (buf[off + 2] << 16) | (buf[off + 3] << 24));
|
|
36
|
-
}
|
|
37
32
|
export class DbWorker {
|
|
38
33
|
constructor(address, db) {
|
|
39
34
|
const { port1, port2 } = new MessageChannel();
|
|
@@ -362,14 +357,16 @@ export class DbServer {
|
|
|
362
357
|
// TODO fix this add it in schema at least
|
|
363
358
|
const data = [2, 1, 0, 0, 0, 1, 9, 1, 0, 0, 0, 7, 1, 0, 1];
|
|
364
359
|
const blockKey = makeCsmtKey(1, 1);
|
|
365
|
-
const buf = new Uint8Array(data.length + 2 + 8 + 4);
|
|
360
|
+
const buf = new Uint8Array(8 + data.length + 2 + 8 + 4);
|
|
366
361
|
const view = new DataView(buf.buffer, 0, buf.byteLength);
|
|
362
|
+
// set schema hash
|
|
363
|
+
writeUint64(buf, this.schema.hash, 0);
|
|
367
364
|
// add content
|
|
368
|
-
buf.set(data);
|
|
365
|
+
buf.set(data, 8);
|
|
369
366
|
// add typesLen
|
|
370
|
-
view.setFloat64(data.length, 0, true);
|
|
367
|
+
view.setFloat64(8 + data.length, 0, true);
|
|
371
368
|
// add dirty key
|
|
372
|
-
view.setFloat64(data.length + 2, blockKey, true);
|
|
369
|
+
view.setFloat64(8 + data.length + 2, blockKey, true);
|
|
373
370
|
// add dataLen
|
|
374
371
|
view.setUint32(buf.length - 4, data.length, true);
|
|
375
372
|
this.modify(buf);
|
|
@@ -380,14 +377,19 @@ export class DbServer {
|
|
|
380
377
|
return this.schema;
|
|
381
378
|
}
|
|
382
379
|
modify(buf) {
|
|
380
|
+
const schemaHash = readUint64(buf, 0);
|
|
381
|
+
if (schemaHash !== this.schema.hash) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
buf = buf.subarray(8);
|
|
383
385
|
const offsets = {};
|
|
384
|
-
const dataLen =
|
|
385
|
-
let typesSize =
|
|
386
|
+
const dataLen = readUint32(buf, buf.length - 4);
|
|
387
|
+
let typesSize = readUint16(buf, dataLen);
|
|
386
388
|
let i = dataLen + 2;
|
|
387
389
|
while (typesSize--) {
|
|
388
|
-
const typeId =
|
|
390
|
+
const typeId = readUint16(buf, i);
|
|
389
391
|
i += 2;
|
|
390
|
-
const startId =
|
|
392
|
+
const startId = readUint32(buf, i);
|
|
391
393
|
const def = this.schemaTypesParsedById[typeId];
|
|
392
394
|
let offset = def.lastId - startId;
|
|
393
395
|
if (offset < 0) {
|
|
@@ -397,7 +399,7 @@ export class DbServer {
|
|
|
397
399
|
buf[i++] = offset >>> 8;
|
|
398
400
|
buf[i++] = offset >>> 16;
|
|
399
401
|
buf[i++] = offset >>> 24;
|
|
400
|
-
const lastId =
|
|
402
|
+
const lastId = readUint32(buf, i);
|
|
401
403
|
i += 4;
|
|
402
404
|
def.lastId = lastId + offset;
|
|
403
405
|
offsets[typeId] = offset;
|
|
@@ -416,14 +418,14 @@ export class DbServer {
|
|
|
416
418
|
return;
|
|
417
419
|
}
|
|
418
420
|
const end = buf.length - 4;
|
|
419
|
-
const dataLen =
|
|
420
|
-
let typesSize =
|
|
421
|
+
const dataLen = readUint32(buf, end);
|
|
422
|
+
let typesSize = readUint16(buf, dataLen);
|
|
421
423
|
const typesLen = typesSize * 10;
|
|
422
424
|
const types = buf.subarray(dataLen + 2, dataLen + typesLen + 2);
|
|
423
425
|
const data = buf.subarray(0, dataLen);
|
|
424
426
|
let i = dataLen + 2;
|
|
425
427
|
while (typesSize--) {
|
|
426
|
-
const typeId =
|
|
428
|
+
const typeId = readUint16(buf, i);
|
|
427
429
|
const def = this.schemaTypesParsedById[typeId];
|
|
428
430
|
const key = makeCsmtKeyFromNodeId(def.id, def.blockCapacity, def.lastId);
|
|
429
431
|
this.dirtyRanges.add(key);
|
|
@@ -475,13 +477,13 @@ export class DbServer {
|
|
|
475
477
|
else {
|
|
476
478
|
const queryType = buf[0];
|
|
477
479
|
if (queryType == 2) {
|
|
478
|
-
const s = 13 +
|
|
479
|
-
const sortLen =
|
|
480
|
+
const s = 13 + readUint16(buf, 11);
|
|
481
|
+
const sortLen = readUint16(buf, s);
|
|
480
482
|
if (sortLen) {
|
|
481
|
-
const typeId =
|
|
483
|
+
const typeId = readUint16(buf, 1);
|
|
482
484
|
const sort = buf.slice(s + 2, s + 2 + sortLen);
|
|
483
485
|
const field = sort[1];
|
|
484
|
-
const start =
|
|
486
|
+
const start = readUint16(sort, 3);
|
|
485
487
|
let sortIndex = this.getSortIndex(typeId, field, start, 0);
|
|
486
488
|
if (!sortIndex) {
|
|
487
489
|
if (this.processingQueries) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@based/db",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.44",
|
|
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.16",
|
|
42
42
|
"@saulx/hash": "^3.0.0",
|
|
43
|
-
"@saulx/utils": "^6.
|
|
43
|
+
"@saulx/utils": "^6.7.0",
|
|
44
44
|
"exit-hook": "^4.0.0",
|
|
45
45
|
"picocolors": "^1.1.0",
|
|
46
46
|
"@based/crc32c": "^1.0.0"
|