@based/db 0.0.43 → 0.0.45
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/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/modify/create.js +1 -1
- package/dist/src/client/modify/update.js +1 -1
- package/dist/src/client/query/BasedDbQuery.d.ts +9 -2
- package/dist/src/client/query/BasedDbQuery.js +93 -8
- package/dist/src/client/query/display.js +3 -2
- package/dist/src/client/query/toBuffer.js +11 -1
- package/dist/src/client/query/types.d.ts +1 -0
- package/dist/src/index.js +18 -1
- package/dist/src/server/index.d.ts +2 -2
- package/dist/src/server/index.js +28 -22
- package/dist/src/server/worker.js +1 -2
- 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
|
|
@@ -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
|
}
|
|
@@ -229,7 +285,24 @@ export class BasedDbQuery extends QueryBranch {
|
|
|
229
285
|
throw new Error('Query: No schema yet - use await db.schemaIsSet()');
|
|
230
286
|
}
|
|
231
287
|
const def = createQueryDef(db, QueryDefType.Root, target, skipValidation);
|
|
288
|
+
def.schemaChecksum = db.schema?.hash || 0;
|
|
232
289
|
super(db, def);
|
|
290
|
+
this.db = db;
|
|
291
|
+
this.skipValidation = skipValidation;
|
|
292
|
+
this.queryCommands = [];
|
|
293
|
+
this.target = target;
|
|
294
|
+
}
|
|
295
|
+
reBuildQuery() {
|
|
296
|
+
this.id = undefined;
|
|
297
|
+
this.buffer = undefined;
|
|
298
|
+
const def = createQueryDef(this.db, QueryDefType.Root, this.target, this.skipValidation);
|
|
299
|
+
def.schemaChecksum = this.db.schema?.hash || 0;
|
|
300
|
+
this.def = def;
|
|
301
|
+
const q = this.queryCommands;
|
|
302
|
+
this.queryCommands = [];
|
|
303
|
+
for (const command of q) {
|
|
304
|
+
this[command.method](...command.args);
|
|
305
|
+
}
|
|
233
306
|
}
|
|
234
307
|
#getInternal = async (resolve, reject) => {
|
|
235
308
|
if (!this.def.include.stringFields.size && !this.def.references.size) {
|
|
@@ -245,8 +318,20 @@ export class BasedDbQuery extends QueryBranch {
|
|
|
245
318
|
}
|
|
246
319
|
const d = performance.now();
|
|
247
320
|
await this.db.isModified();
|
|
321
|
+
if (this.db.schema?.hash !== this.def.schemaChecksum) {
|
|
322
|
+
this.reBuildQuery();
|
|
323
|
+
return this.#getInternal(resolve, reject);
|
|
324
|
+
}
|
|
248
325
|
const res = await this.db.hooks.getQueryBuf(buf);
|
|
249
|
-
if (res
|
|
326
|
+
if (res.byteLength === 1) {
|
|
327
|
+
if (res[0] === 0) {
|
|
328
|
+
reject(new Error('schema mismatch'));
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
reject(new Error('unexpected error'));
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
else if (res instanceof Error) {
|
|
250
335
|
reject(res);
|
|
251
336
|
}
|
|
252
337
|
else {
|
|
@@ -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) + '';
|
|
@@ -3,7 +3,7 @@ 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';
|
|
6
|
+
import { ENCODER, writeUint64 } from '@saulx/utils';
|
|
7
7
|
import { aggregateToBuffer, isRootCountOnly } from './aggregates/aggregation.js';
|
|
8
8
|
const byteSize = (arr) => {
|
|
9
9
|
return arr.reduce((a, b) => {
|
|
@@ -65,6 +65,11 @@ export function defToBuffer(db, def) {
|
|
|
65
65
|
result.push(buf);
|
|
66
66
|
// ignore this for now...
|
|
67
67
|
// result.push(...include)
|
|
68
|
+
if (def.type === QueryDefType.Root) {
|
|
69
|
+
const checksum = new Uint8Array(8);
|
|
70
|
+
writeUint64(checksum, def.schemaChecksum ?? 0, 0);
|
|
71
|
+
result.push(checksum);
|
|
72
|
+
}
|
|
68
73
|
return result;
|
|
69
74
|
}
|
|
70
75
|
if (def.type === QueryDefType.Root) {
|
|
@@ -246,6 +251,11 @@ export function defToBuffer(db, def) {
|
|
|
246
251
|
metaEdgeBuffer[2] = edgesSize >>> 8;
|
|
247
252
|
result.push(metaEdgeBuffer, ...edges);
|
|
248
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
|
+
}
|
|
249
259
|
return result;
|
|
250
260
|
}
|
|
251
261
|
//# sourceMappingURL=toBuffer.js.map
|
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
|
|
@@ -52,12 +53,27 @@ export class BasedDb {
|
|
|
52
53
|
maxModifySize,
|
|
53
54
|
hooks: {
|
|
54
55
|
subscribe(q, onData, onError) {
|
|
56
|
+
let killed = false;
|
|
55
57
|
let timer;
|
|
56
58
|
let prevChecksum;
|
|
57
59
|
let lastLen = 0;
|
|
58
60
|
let response;
|
|
59
61
|
const get = async () => {
|
|
62
|
+
const schemaVersion = q.def.schemaChecksum;
|
|
63
|
+
if (schemaVersion && schemaVersion !== server.schema.hash) {
|
|
64
|
+
q.reBuildQuery();
|
|
65
|
+
registerQuery(q);
|
|
66
|
+
response = undefined;
|
|
67
|
+
timer = setTimeout(get, 0);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (killed) {
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
60
73
|
const res = await server.getQueryBuf(q.buffer);
|
|
74
|
+
if (killed) {
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
61
77
|
if (!response) {
|
|
62
78
|
response = new BasedQueryResponse(q.id, q.def, res, 0);
|
|
63
79
|
}
|
|
@@ -71,10 +87,11 @@ export class BasedDb {
|
|
|
71
87
|
lastLen = res.byteLength;
|
|
72
88
|
prevChecksum = checksum;
|
|
73
89
|
}
|
|
74
|
-
setTimeout(get, 200);
|
|
90
|
+
timer = setTimeout(get, 200);
|
|
75
91
|
};
|
|
76
92
|
get();
|
|
77
93
|
return () => {
|
|
94
|
+
killed = true;
|
|
78
95
|
clearTimeout(timer);
|
|
79
96
|
};
|
|
80
97
|
},
|
|
@@ -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);
|
|
@@ -467,6 +469,10 @@ export class DbServer {
|
|
|
467
469
|
console.error('Db is stopped - trying to query', buf.byteLength);
|
|
468
470
|
return Promise.resolve(new Uint8Array(8));
|
|
469
471
|
}
|
|
472
|
+
const schemaChecksum = readUint64(buf, buf.byteLength - 8);
|
|
473
|
+
if (schemaChecksum !== this.schema?.hash) {
|
|
474
|
+
return Promise.resolve(new Uint8Array(1));
|
|
475
|
+
}
|
|
470
476
|
if (this.modifyQueue.length) {
|
|
471
477
|
return new Promise((resolve) => {
|
|
472
478
|
this.addToQueryQueue(resolve, buf);
|
|
@@ -475,13 +481,13 @@ export class DbServer {
|
|
|
475
481
|
else {
|
|
476
482
|
const queryType = buf[0];
|
|
477
483
|
if (queryType == 2) {
|
|
478
|
-
const s = 13 +
|
|
479
|
-
const sortLen =
|
|
484
|
+
const s = 13 + readUint16(buf, 11);
|
|
485
|
+
const sortLen = readUint16(buf, s);
|
|
480
486
|
if (sortLen) {
|
|
481
|
-
const typeId =
|
|
487
|
+
const typeId = readUint16(buf, 1);
|
|
482
488
|
const sort = buf.slice(s + 2, s + 2 + sortLen);
|
|
483
489
|
const field = sort[1];
|
|
484
|
-
const start =
|
|
490
|
+
const start = readUint16(sort, 3);
|
|
485
491
|
let sortIndex = this.getSortIndex(typeId, field, start, 0);
|
|
486
492
|
if (!sortIndex) {
|
|
487
493
|
if (this.processingQueries) {
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { isMainThread, receiveMessageOnPort, workerData, } from 'node:worker_threads';
|
|
2
2
|
import native from '../native.js';
|
|
3
|
-
let workerCtx;
|
|
4
3
|
if (isMainThread) {
|
|
5
4
|
console.warn('running query worker.ts in mainthread');
|
|
6
5
|
}
|
|
7
6
|
else {
|
|
8
7
|
let { address, channel } = workerData;
|
|
9
8
|
let dbCtx = native.externalFromInt(address);
|
|
10
|
-
|
|
9
|
+
native.workerCtxInit();
|
|
11
10
|
// const transferList = new Array(1)
|
|
12
11
|
const handleMsg = (msg) => {
|
|
13
12
|
try {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@based/db",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.45",
|
|
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"
|