@based/db 0.0.7 → 0.0.9
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/README.md +425 -8
- package/dist/lib/darwin_aarch64/include/selva/db.h +12 -3
- package/dist/lib/darwin_aarch64/include/selva/fields.h +22 -11
- package/dist/lib/darwin_aarch64/include/selva/history.h +49 -0
- package/dist/lib/darwin_aarch64/include/selva/hll.h +21 -0
- package/dist/lib/darwin_aarch64/include/selva/sort.h +14 -0
- package/dist/lib/darwin_aarch64/include/selva/types.h +9 -2
- package/dist/lib/darwin_aarch64/include/selva/vector.h +22 -1
- package/dist/lib/darwin_aarch64/include/selva/xxhash64.h +23 -0
- package/dist/lib/darwin_aarch64/libnode-v20.11.1.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v20.18.1.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v22.13.0.node +0 -0
- package/dist/lib/darwin_aarch64/libnode-v22.8.0.node +0 -0
- package/dist/lib/darwin_aarch64/libselva.dylib +0 -0
- package/dist/lib/linux_aarch64/include/selva/db.h +12 -3
- package/dist/lib/linux_aarch64/include/selva/fields.h +22 -11
- package/dist/lib/linux_aarch64/include/selva/history.h +49 -0
- package/dist/lib/linux_aarch64/include/selva/hll.h +21 -0
- package/dist/lib/linux_aarch64/include/selva/sort.h +14 -0
- package/dist/lib/linux_aarch64/include/selva/types.h +9 -2
- package/dist/lib/linux_aarch64/include/selva/vector.h +22 -1
- package/dist/lib/linux_aarch64/include/selva/xxhash64.h +23 -0
- package/dist/lib/linux_aarch64/libnode-v20.11.1.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v20.18.1.node +0 -0
- package/dist/lib/linux_aarch64/libnode-v22.13.0.node +0 -0
- package/dist/lib/linux_aarch64/libselva.so +0 -0
- package/dist/lib/linux_x86_64/include/selva/db.h +12 -3
- package/dist/lib/linux_x86_64/include/selva/fields.h +22 -11
- package/dist/lib/linux_x86_64/include/selva/history.h +49 -0
- package/dist/lib/linux_x86_64/include/selva/hll.h +21 -0
- package/dist/lib/linux_x86_64/include/selva/sort.h +14 -0
- package/dist/lib/linux_x86_64/include/selva/types.h +9 -2
- package/dist/lib/linux_x86_64/include/selva/vector.h +22 -1
- package/dist/lib/linux_x86_64/include/selva/xxhash64.h +23 -0
- package/dist/lib/linux_x86_64/libnode-v20.11.1.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v20.18.1.node +0 -0
- package/dist/lib/linux_x86_64/libnode-v22.13.0.node +0 -0
- package/dist/lib/linux_x86_64/libselva.so +0 -0
- package/dist/src/client/bitWise.js +21 -1
- package/dist/src/client/index.d.ts +11 -5
- package/dist/src/client/index.js +57 -11
- package/dist/src/client/modify/ModifyRes.d.ts +4 -1
- package/dist/src/client/modify/ModifyRes.js +8 -1
- package/dist/src/client/modify/alias.js +3 -3
- package/dist/src/client/modify/binary.js +5 -2
- package/dist/src/client/modify/cardinality.d.ts +4 -0
- package/dist/src/client/modify/cardinality.js +50 -0
- package/dist/src/client/modify/create copy.d.ts +5 -0
- package/dist/src/client/modify/create copy.js +112 -0
- package/dist/src/client/modify/create.d.ts +2 -1
- package/dist/src/client/modify/create.js +11 -7
- package/dist/src/client/modify/delete.d.ts +2 -0
- package/dist/src/client/modify/delete.js +37 -0
- package/dist/src/client/modify/expire.d.ts +3 -0
- package/dist/src/client/modify/expire.js +25 -0
- package/dist/src/client/modify/fixed.js +11 -1
- package/dist/src/client/modify/index.d.ts +1 -1
- package/dist/src/client/modify/index.js +1 -1
- package/dist/src/client/modify/json.d.ts +4 -0
- package/dist/src/client/modify/json.js +5 -0
- package/dist/src/client/modify/modify.js +11 -7
- package/dist/src/client/modify/references/edge.js +21 -6
- package/dist/src/client/modify/references/reference.js +2 -2
- package/dist/src/client/modify/references/references.d.ts +0 -1
- package/dist/src/client/modify/references/references.js +4 -4
- package/dist/src/client/modify/remove.d.ts +1 -2
- package/dist/src/client/modify/remove.js +9 -6
- package/dist/src/client/modify/setCursor.d.ts +1 -1
- package/dist/src/client/modify/setCursor.js +4 -1
- package/dist/src/client/modify/string.js +2 -2
- package/dist/src/client/modify/text.d.ts +2 -1
- package/dist/src/client/modify/text.js +13 -7
- package/dist/src/client/modify/types.d.ts +8 -1
- package/dist/src/client/modify/types.js +1 -0
- package/dist/src/client/modify/update.d.ts +2 -1
- package/dist/src/client/modify/update.js +9 -5
- package/dist/src/client/modify/upsert.d.ts +2 -1
- package/dist/src/client/modify/upsert.js +3 -3
- package/dist/src/client/modify/vector copy.d.ts +4 -0
- package/dist/src/client/modify/vector copy.js +46 -0
- package/dist/src/client/modify/vector.js +6 -4
- package/dist/src/client/query/BasedDbQuery.d.ts +4 -3
- package/dist/src/client/query/BasedDbQuery.js +39 -16
- package/dist/src/client/query/BasedIterable.js +3 -3
- package/dist/src/client/query/filter/FilterBranch.d.ts +2 -2
- package/dist/src/client/query/filter/FilterBranch.js +2 -2
- package/dist/src/client/query/filter/createFixedFilterBuffer.d.ts +3 -2
- package/dist/src/client/query/filter/createFixedFilterBuffer.js +14 -11
- package/dist/src/client/query/filter/createReferenceFilter.d.ts +2 -1
- package/dist/src/client/query/filter/createReferenceFilter.js +6 -5
- package/dist/src/client/query/filter/createVariableFilterBuffer.d.ts +2 -1
- package/dist/src/client/query/filter/createVariableFilterBuffer.js +61 -30
- package/dist/src/client/query/filter/filter.d.ts +2 -2
- package/dist/src/client/query/filter/filter.js +27 -22
- package/dist/src/client/query/filter/parseFilterValue.js +9 -64
- package/dist/src/client/query/filter/primitiveFilter.js +7 -11
- package/dist/src/client/query/filter/toBuffer.js +5 -7
- package/dist/src/client/query/filter/types.d.ts +51 -2
- package/dist/src/client/query/filter/types.js +114 -0
- package/dist/src/client/query/include/props.d.ts +2 -0
- package/dist/src/client/query/include/props.js +25 -6
- package/dist/src/client/query/include/toBuffer.js +21 -1
- package/dist/src/client/query/include/walk.js +17 -2
- package/dist/src/client/query/queryDef.js +1 -0
- package/dist/src/client/query/read/read.js +85 -21
- package/dist/src/client/query/search/index.d.ts +2 -0
- package/dist/src/client/query/search/index.js +79 -23
- package/dist/src/client/query/subscription/index.js +2 -2
- package/dist/src/client/query/subscription/markers.d.ts +1 -1
- package/dist/src/client/query/subscription/markers.js +2 -2
- package/dist/src/client/query/toBuffer.js +0 -4
- package/dist/src/client/query/types.d.ts +10 -0
- package/dist/src/client/query/validation.d.ts +3 -2
- package/dist/src/client/query/validation.js +17 -2
- package/dist/src/client/timestamp.d.ts +1 -0
- package/dist/src/client/timestamp.js +68 -0
- package/dist/src/client/xxHash64.d.ts +1 -0
- package/dist/src/client/xxHash64.js +5 -0
- package/dist/src/index.d.ts +4 -1
- package/dist/src/index.js +13 -3
- package/dist/src/native.d.ts +1 -0
- package/dist/src/native.js +4 -1
- package/dist/src/server/csmt/tree.js +12 -2
- package/dist/src/server/index.d.ts +12 -4
- package/dist/src/server/index.js +63 -17
- package/dist/src/server/migrate/index.js +6 -2
- package/dist/src/server/migrate/worker.js +3 -3
- package/dist/src/server/schema/selvaBuffer.js +20 -11
- package/dist/src/server/schema/typeDef.d.ts +2 -2
- package/dist/src/server/schema/typeDef.js +14 -5
- package/dist/src/server/schema/types.d.ts +7 -2
- package/dist/src/server/schema/types.js +6 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
#
|
|
1
|
+
# BasedDb
|
|
2
|
+
|
|
3
|
+
BasedDb is a powerful database solution that supports various data types, references, edges, and operations. It also offers concurrency handling, client-server architecture support, and more.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Schema definition and management
|
|
8
|
+
- Data creation, querying, updating, and deletion
|
|
9
|
+
- Support for strings, numbers, booleans, binaries, aliases, enums, and cardinality
|
|
10
|
+
- Edges and references for advanced data modeling
|
|
11
|
+
- Concurrency support for high-load scenarios
|
|
12
|
+
- Client-server design for distributed systems
|
|
13
|
+
- Checksum, analytics, and expiration features
|
|
2
14
|
|
|
3
15
|
## Install
|
|
4
16
|
|
|
@@ -9,7 +21,7 @@
|
|
|
9
21
|
- zig 0.13.0
|
|
10
22
|
- npm & node.js, v20.11.1 or newer
|
|
11
23
|
|
|
12
|
-
```
|
|
24
|
+
```bash
|
|
13
25
|
npm i
|
|
14
26
|
npm run get-napi // only need this the first time
|
|
15
27
|
npm run build
|
|
@@ -18,26 +30,431 @@ npm run build
|
|
|
18
30
|
## Running tests
|
|
19
31
|
|
|
20
32
|
Run all tests + ldb + build c, zig and js
|
|
21
|
-
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
npm run test
|
|
36
|
+
```
|
|
22
37
|
|
|
23
38
|
Run specific test file - does substring matching
|
|
24
|
-
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
npm run test -- range.js
|
|
42
|
+
```
|
|
25
43
|
|
|
26
44
|
Run specific test file & run specific test
|
|
27
|
-
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm run test -- range.js:range
|
|
48
|
+
```
|
|
28
49
|
|
|
29
50
|
Different flavours of test
|
|
30
51
|
|
|
31
52
|
Only builds zig
|
|
32
|
-
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
npm run test-zig
|
|
56
|
+
```
|
|
33
57
|
|
|
34
58
|
Builds nothing only runs tests
|
|
35
|
-
`npm run test-fast`
|
|
36
59
|
|
|
37
|
-
|
|
60
|
+
```bash
|
|
61
|
+
npm run test-fast
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Getting Started
|
|
65
|
+
|
|
66
|
+
To get started with BasedDb, follow these steps:
|
|
67
|
+
|
|
68
|
+
1. Install the package
|
|
69
|
+
2. Define your schema
|
|
70
|
+
3. Start the database
|
|
71
|
+
4. Perform operations like create, query, update, and delete
|
|
38
72
|
|
|
39
73
|
```ts
|
|
40
74
|
const db = new BasedDb({
|
|
41
75
|
path: '/persistent-file-path',
|
|
42
76
|
})
|
|
43
77
|
```
|
|
78
|
+
|
|
79
|
+
## Schema Definition
|
|
80
|
+
|
|
81
|
+
Define your schema using the `putSchema` method. Here is an example:
|
|
82
|
+
|
|
83
|
+
```typescript
|
|
84
|
+
await db.putSchema({
|
|
85
|
+
types: {
|
|
86
|
+
user: {
|
|
87
|
+
name: 'string',
|
|
88
|
+
age: 'number',
|
|
89
|
+
email: 'alias',
|
|
90
|
+
isNice: 'boolean',
|
|
91
|
+
roles: ['admin', 'editor', 'viewer'],
|
|
92
|
+
file: 'binary',
|
|
93
|
+
bestFriend: {
|
|
94
|
+
ref: 'user',
|
|
95
|
+
prop: 'bestFriend',
|
|
96
|
+
},
|
|
97
|
+
friends: {
|
|
98
|
+
items: {
|
|
99
|
+
ref: 'user',
|
|
100
|
+
prop: 'friends',
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
article: {
|
|
105
|
+
body: 'string',
|
|
106
|
+
myUniqueValuesCount: 'cardinality',
|
|
107
|
+
contributors: {
|
|
108
|
+
type: 'references',
|
|
109
|
+
items: {
|
|
110
|
+
ref: 'user',
|
|
111
|
+
prop: 'articles',
|
|
112
|
+
$role: ['writer', 'editor'],
|
|
113
|
+
},
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
page: {
|
|
117
|
+
name: 'string',
|
|
118
|
+
clients: {
|
|
119
|
+
items: {
|
|
120
|
+
ref: '_client',
|
|
121
|
+
prop: 'pages',
|
|
122
|
+
$viewers: 'uint8',
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
activeViewers: {
|
|
126
|
+
type: 'uint32',
|
|
127
|
+
path: 'clients.$viewers.#sum',
|
|
128
|
+
history: {
|
|
129
|
+
interval: 'second',
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
_client: {
|
|
134
|
+
name: 'string',
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
})
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
## Data Operations
|
|
141
|
+
|
|
142
|
+
### Create
|
|
143
|
+
|
|
144
|
+
Create new records using the `create` method:
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
const userId = await db.create('user', {
|
|
148
|
+
name: 'youzi',
|
|
149
|
+
email: 'youzi@example.com',
|
|
150
|
+
isNice: true,
|
|
151
|
+
roles: 'admin',
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Query & Filters
|
|
156
|
+
|
|
157
|
+
Query records using the `query` method:
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
const results = await db
|
|
161
|
+
.query('user')
|
|
162
|
+
.filter('isNice', '=', true)
|
|
163
|
+
.filter('name', '=', 'youzi')
|
|
164
|
+
.get()
|
|
165
|
+
.toObject()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Update
|
|
169
|
+
|
|
170
|
+
Update records using the `update` method:
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
await db.update('user', userId, {
|
|
174
|
+
roles: 'editor',
|
|
175
|
+
})
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### Delete
|
|
179
|
+
|
|
180
|
+
Delete records using the `delete` method:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
await db.delete('user', userId)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Advanced Features
|
|
187
|
+
|
|
188
|
+
### Copy
|
|
189
|
+
|
|
190
|
+
Copy records with transformations:
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
await db.copy('edition', editionId, {
|
|
194
|
+
copiedByYouzi: true,
|
|
195
|
+
versionOf({ id }) {
|
|
196
|
+
return id
|
|
197
|
+
},
|
|
198
|
+
name({ name }) {
|
|
199
|
+
return name + ' (edition copy)'
|
|
200
|
+
},
|
|
201
|
+
sequences({ sequences }) {
|
|
202
|
+
return sequences.map(({ id }) => {
|
|
203
|
+
return db.copy('sequence', id, {
|
|
204
|
+
copiedByYouzi: true,
|
|
205
|
+
name({ name }) {
|
|
206
|
+
return name + ' (sequence copy)'
|
|
207
|
+
},
|
|
208
|
+
pages({ pages }) {
|
|
209
|
+
return pages.map(({ id }) =>
|
|
210
|
+
db.copy('page', id, {
|
|
211
|
+
copiedByYouzi: true,
|
|
212
|
+
name({ name }) {
|
|
213
|
+
return name + ' (page copy)'
|
|
214
|
+
},
|
|
215
|
+
}),
|
|
216
|
+
)
|
|
217
|
+
},
|
|
218
|
+
})
|
|
219
|
+
})
|
|
220
|
+
},
|
|
221
|
+
})
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
### Concurrency
|
|
225
|
+
|
|
226
|
+
Concurrent write and read operations are supported. The example below shows multiple concurrent queries and creates. Handle concurrency carefully to avoid conflicts:
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
let id = 0
|
|
230
|
+
let queries = 0
|
|
231
|
+
let refs = []
|
|
232
|
+
let timer = setTimeout(() => {
|
|
233
|
+
timer = null
|
|
234
|
+
}, 5e3)
|
|
235
|
+
|
|
236
|
+
const query = async () => {
|
|
237
|
+
queries++
|
|
238
|
+
try {
|
|
239
|
+
await db.query('user').include('friends').range(0, 1000_000).get()
|
|
240
|
+
} catch (e) {
|
|
241
|
+
console.error('err:', e)
|
|
242
|
+
}
|
|
243
|
+
queries--
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
while (timer) {
|
|
247
|
+
let i = 100
|
|
248
|
+
while (i--) {
|
|
249
|
+
query()
|
|
250
|
+
}
|
|
251
|
+
while (timer && queries) {
|
|
252
|
+
db.create('user', {
|
|
253
|
+
friends: refs,
|
|
254
|
+
})
|
|
255
|
+
refs.push(++id)
|
|
256
|
+
await db.drain()
|
|
257
|
+
await setTimeoutAsync()
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
clearTimeout(timer)
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Client-Server
|
|
265
|
+
|
|
266
|
+
Set up a client-server architecture:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
const server = new DbServer({
|
|
270
|
+
path: t.tmp,
|
|
271
|
+
onSchemaChange(schema) {
|
|
272
|
+
client1.putLocalSchema(schema)
|
|
273
|
+
client2.putLocalSchema(schema)
|
|
274
|
+
},
|
|
275
|
+
})
|
|
276
|
+
|
|
277
|
+
await server.start({ clean: true })
|
|
278
|
+
|
|
279
|
+
const hooks: DbClientHooks = {
|
|
280
|
+
async putSchema(schema, fromStart, transformFns) {
|
|
281
|
+
return server.putSchema(schema, fromStart, transformFns)
|
|
282
|
+
},
|
|
283
|
+
async flushModify(buf) {
|
|
284
|
+
const offsets = server.modify(buf)
|
|
285
|
+
return { offsets }
|
|
286
|
+
},
|
|
287
|
+
async getQueryBuf(buf) {
|
|
288
|
+
return server.getQueryBuf(buf)
|
|
289
|
+
},
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const client1 = new DbClient({ hooks })
|
|
293
|
+
const client2 = new DbClient({ hooks })
|
|
294
|
+
|
|
295
|
+
await client1.putSchema({
|
|
296
|
+
types: {
|
|
297
|
+
user: {
|
|
298
|
+
name: 'string',
|
|
299
|
+
},
|
|
300
|
+
},
|
|
301
|
+
})
|
|
302
|
+
|
|
303
|
+
const youzi = await client1.create('user', { name: 'youzi' })
|
|
304
|
+
const jamez = await client1.create('user', { name: 'jamez' })
|
|
305
|
+
|
|
306
|
+
deepEqual(await client1.query('user').get().toObject(), [
|
|
307
|
+
{ id: 1, name: 'youzi' },
|
|
308
|
+
{ id: 2, name: 'jamez' },
|
|
309
|
+
])
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
### Checksum
|
|
313
|
+
|
|
314
|
+
Include checksum in queries:
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
await db.query('article').include('*', '_checksum')
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Cardinality
|
|
321
|
+
|
|
322
|
+
Use cardinality for unique value counts:
|
|
323
|
+
|
|
324
|
+
```typescript
|
|
325
|
+
await db.putSchema({
|
|
326
|
+
types: {
|
|
327
|
+
article: {
|
|
328
|
+
myUniqueValuesCount: 'cardinality',
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
})
|
|
332
|
+
|
|
333
|
+
const myArticle = await db.create('article', {
|
|
334
|
+
myUniqueValuesCount: 'myCoolValue',
|
|
335
|
+
})
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Boolean
|
|
339
|
+
|
|
340
|
+
Handle boolean properties:
|
|
341
|
+
|
|
342
|
+
```typescript
|
|
343
|
+
await db.putSchema({
|
|
344
|
+
types: {
|
|
345
|
+
user: {
|
|
346
|
+
props: {
|
|
347
|
+
isNice: 'boolean',
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
})
|
|
352
|
+
|
|
353
|
+
db.create('user', {})
|
|
354
|
+
db.create('user', { isNice: true })
|
|
355
|
+
db.create('user', { isNice: false })
|
|
356
|
+
|
|
357
|
+
await db.drain()
|
|
358
|
+
|
|
359
|
+
deepEqual((await db.query('user').get()).toObject(), [
|
|
360
|
+
{ id: 1, isNice: false },
|
|
361
|
+
{ id: 2, isNice: true },
|
|
362
|
+
{ id: 3, isNice: false },
|
|
363
|
+
])
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
### Binary
|
|
367
|
+
|
|
368
|
+
Handle binary data:
|
|
369
|
+
|
|
370
|
+
```typescript
|
|
371
|
+
await db.putSchema({
|
|
372
|
+
types: {
|
|
373
|
+
user: {
|
|
374
|
+
props: {
|
|
375
|
+
file: { type: 'binary' },
|
|
376
|
+
},
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
})
|
|
380
|
+
|
|
381
|
+
db.create('user', {
|
|
382
|
+
file: new Uint32Array([1, 2, 3, 4]),
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
await db.drain()
|
|
386
|
+
|
|
387
|
+
deepEqual((await db.query('user').get()).toObject(), [
|
|
388
|
+
{
|
|
389
|
+
id: 1,
|
|
390
|
+
file: new Uint8Array(new Uint32Array([1, 2, 3, 4]).buffer),
|
|
391
|
+
},
|
|
392
|
+
])
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
### Analytics
|
|
396
|
+
|
|
397
|
+
Perform analytics on data:
|
|
398
|
+
|
|
399
|
+
```typescript
|
|
400
|
+
await db.putSchema({
|
|
401
|
+
types: {
|
|
402
|
+
page: {
|
|
403
|
+
name: 'string',
|
|
404
|
+
clients: {
|
|
405
|
+
items: {
|
|
406
|
+
ref: '_client',
|
|
407
|
+
prop: 'pages',
|
|
408
|
+
$viewers: 'uint8',
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
activeViewers: {
|
|
412
|
+
type: 'uint32',
|
|
413
|
+
path: 'clients.$viewers.#sum',
|
|
414
|
+
history: {
|
|
415
|
+
interval: 'second',
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
},
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
const client = await db.create('_client', {})
|
|
423
|
+
const page = await db.create('page', {
|
|
424
|
+
clients: [
|
|
425
|
+
{
|
|
426
|
+
id: client,
|
|
427
|
+
$viewers: { increment: 1 },
|
|
428
|
+
},
|
|
429
|
+
],
|
|
430
|
+
})
|
|
431
|
+
```
|
|
432
|
+
|
|
433
|
+
### Alias
|
|
434
|
+
|
|
435
|
+
Use aliases for properties:
|
|
436
|
+
|
|
437
|
+
```typescript
|
|
438
|
+
await db.putSchema({
|
|
439
|
+
types: {
|
|
440
|
+
user: {
|
|
441
|
+
props: {
|
|
442
|
+
externalId: 'alias',
|
|
443
|
+
potato: 'string',
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
})
|
|
448
|
+
|
|
449
|
+
const user1 = db.create('user', {
|
|
450
|
+
externalId: 'cool',
|
|
451
|
+
})
|
|
452
|
+
|
|
453
|
+
await db.drain()
|
|
454
|
+
|
|
455
|
+
deepEqual((await db.query('user', user1).get()).toObject(), {
|
|
456
|
+
id: 1,
|
|
457
|
+
externalId: 'cool',
|
|
458
|
+
potato: '',
|
|
459
|
+
})
|
|
460
|
+
```
|
|
@@ -107,6 +107,15 @@ enum SelvaFieldType selva_get_fs_type(const struct SelvaFieldSchema *fs);
|
|
|
107
107
|
SELVA_EXPORT
|
|
108
108
|
const struct EdgeFieldConstraint *selva_get_edge_field_constraint(const struct SelvaFieldSchema *fs) __attribute__((nonnull));
|
|
109
109
|
|
|
110
|
+
SELVA_EXPORT
|
|
111
|
+
const struct SelvaFieldsSchema *selva_get_edge_field_fields_schema(struct SelvaDb *db, const struct EdgeFieldConstraint *efc);
|
|
112
|
+
|
|
113
|
+
SELVA_EXPORT
|
|
114
|
+
void selva_expire_node(struct SelvaDb *db, node_type_t type, node_id_t node_id, int64_t ts);
|
|
115
|
+
|
|
116
|
+
SELVA_EXPORT
|
|
117
|
+
void selva_db_expire_tick(struct SelvaDb *db, int64_t now);
|
|
118
|
+
|
|
110
119
|
/**
|
|
111
120
|
* Delete a node.
|
|
112
121
|
*/
|
|
@@ -216,13 +225,13 @@ node_id_t selva_get_node_id(const struct SelvaNode *node) __attribute__((nonnull
|
|
|
216
225
|
* @param tmp_hash_state is only used for computation and it's reset before use.
|
|
217
226
|
*/
|
|
218
227
|
SELVA_EXPORT
|
|
219
|
-
selva_hash128_t selva_node_hash_update(struct SelvaTypeEntry *type, struct SelvaNode *node, struct XXH3_state_s *tmp_hash_state);
|
|
228
|
+
selva_hash128_t selva_node_hash_update(struct SelvaDb *db, struct SelvaTypeEntry *type, struct SelvaNode *node, struct XXH3_state_s *tmp_hash_state);
|
|
220
229
|
|
|
221
230
|
SELVA_EXPORT
|
|
222
|
-
selva_hash128_t selva_node_hash_update2(struct SelvaTypeEntry *type, struct SelvaNode *node);
|
|
231
|
+
selva_hash128_t selva_node_hash_update2(struct SelvaDb *db, struct SelvaTypeEntry *type, struct SelvaNode *node);
|
|
223
232
|
|
|
224
233
|
SELVA_EXPORT
|
|
225
|
-
selva_hash128_t selva_node_hash_range(struct SelvaTypeEntry *type, node_id_t start, node_id_t end) __attribute__((nonnull));
|
|
234
|
+
selva_hash128_t selva_node_hash_range(struct SelvaDb *db, struct SelvaTypeEntry *type, node_id_t start, node_id_t end) __attribute__((nonnull));
|
|
226
235
|
|
|
227
236
|
/**
|
|
228
237
|
* @}
|
|
@@ -87,6 +87,7 @@ struct SelvaFieldsAny {
|
|
|
87
87
|
struct SelvaNodeWeakReferences weak_references; /*!< SELVA_FIELD_TYPE_WEAK_REFERENCES */
|
|
88
88
|
struct SelvaMicroBuffer *smb; /*!< SELVA_FIELD_TYPE_MICRO_BUFFER */
|
|
89
89
|
#endif
|
|
90
|
+
// HyperLogLogPlusPlus cardinality; /*!< SELVA_FIELD_TYPE_HLL */
|
|
90
91
|
};
|
|
91
92
|
};
|
|
92
93
|
|
|
@@ -137,6 +138,17 @@ int selva_fields_get_mutable_string(
|
|
|
137
138
|
struct selva_string **s)
|
|
138
139
|
__attribute__((access(write_only, 4)));
|
|
139
140
|
|
|
141
|
+
/*
|
|
142
|
+
* TODO prefix with selva_
|
|
143
|
+
* TODO Document diff to get_mutable_string
|
|
144
|
+
*/
|
|
145
|
+
SELVA_EXPORT
|
|
146
|
+
struct selva_string *fields_ensure_string(
|
|
147
|
+
struct SelvaDb *db,
|
|
148
|
+
struct SelvaNode *node,
|
|
149
|
+
const struct SelvaFieldSchema *fs,
|
|
150
|
+
size_t initial_len);
|
|
151
|
+
|
|
140
152
|
SELVA_EXPORT
|
|
141
153
|
int selva_fields_reference_set(
|
|
142
154
|
struct SelvaDb *db,
|
|
@@ -204,6 +216,7 @@ int selva_fields_references_swap(
|
|
|
204
216
|
|
|
205
217
|
SELVA_EXPORT
|
|
206
218
|
int selva_fields_set_reference_meta(
|
|
219
|
+
struct SelvaDb *db,
|
|
207
220
|
struct SelvaNode *node,
|
|
208
221
|
struct SelvaNodeReference *ref,
|
|
209
222
|
const struct EdgeFieldConstraint *efc,
|
|
@@ -212,6 +225,7 @@ int selva_fields_set_reference_meta(
|
|
|
212
225
|
|
|
213
226
|
SELVA_EXPORT
|
|
214
227
|
int selva_fields_get_reference_meta_mutable_string(
|
|
228
|
+
struct SelvaDb *db,
|
|
215
229
|
struct SelvaNode *node,
|
|
216
230
|
struct SelvaNodeReference *ref,
|
|
217
231
|
const struct EdgeFieldConstraint *efc,
|
|
@@ -283,6 +297,12 @@ struct SelvaNode *selva_fields_resolve_weak_reference(
|
|
|
283
297
|
const struct SelvaFieldSchema *fs,
|
|
284
298
|
const struct SelvaNodeWeakReference *weak_ref);
|
|
285
299
|
|
|
300
|
+
SELVA_EXPORT
|
|
301
|
+
struct selva_string *selva_fields_get_selva_string2(struct SelvaFields *fields, const struct SelvaFieldSchema *fs);
|
|
302
|
+
|
|
303
|
+
SELVA_EXPORT
|
|
304
|
+
struct selva_string *selva_fields_get_selva_string(struct SelvaNode *node, const struct SelvaFieldSchema *fs);
|
|
305
|
+
|
|
286
306
|
SELVA_EXPORT
|
|
287
307
|
struct SelvaFieldsPointer selva_fields_get_raw2(struct SelvaFields *fields, const struct SelvaFieldSchema *fs)
|
|
288
308
|
__attribute__((nonnull));
|
|
@@ -319,20 +339,11 @@ void selva_fields_init(const struct SelvaFieldsSchema *schema, struct SelvaField
|
|
|
319
339
|
SELVA_EXPORT
|
|
320
340
|
void selva_fields_destroy(struct SelvaDb *db, struct SelvaNode *node);
|
|
321
341
|
|
|
322
|
-
SELVA_EXPORT
|
|
323
|
-
int selva_fields_get_string_crc(const struct SelvaNode *node, const struct SelvaFieldSchema *fs, uint32_t *crc);
|
|
324
|
-
|
|
325
|
-
SELVA_EXPORT
|
|
326
|
-
int selva_fields_get_string_crc2(const struct SelvaFields *fields, const struct SelvaFieldSchema *fs, uint32_t *crc);
|
|
327
|
-
|
|
328
342
|
SELVA_EXPORT
|
|
329
343
|
int selva_fields_get_text_crc(const struct SelvaNode *node, const struct SelvaFieldSchema *fs, enum selva_lang_code lang, uint32_t *crc);
|
|
330
344
|
|
|
331
345
|
SELVA_EXPORT
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
SELVA_EXPORT
|
|
335
|
-
void selva_fields_hash_update(struct XXH3_state_s *hash_state, const struct SelvaFieldsSchema *schema, const struct SelvaFields *fields);
|
|
346
|
+
void selva_fields_hash_update(struct XXH3_state_s *hash_state, struct SelvaDb *db, const struct SelvaFieldsSchema *schema, const struct SelvaFields *fields);
|
|
336
347
|
|
|
337
348
|
SELVA_EXPORT
|
|
338
|
-
selva_hash128_t selva_fields_hash(const struct SelvaFieldsSchema *schema, const struct SelvaFields *fields);
|
|
349
|
+
selva_hash128_t selva_fields_hash(struct SelvaDb *db, const struct SelvaFieldsSchema *schema, const struct SelvaFields *fields);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2025 SAULX
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
#pragma once
|
|
6
|
+
|
|
7
|
+
#include <stddef.h>
|
|
8
|
+
#include <stdint.h>
|
|
9
|
+
#include "selva/types.h"
|
|
10
|
+
#include "selva/_export.h"
|
|
11
|
+
|
|
12
|
+
struct selva_history;
|
|
13
|
+
|
|
14
|
+
struct selva_history_event {
|
|
15
|
+
int64_t ts;
|
|
16
|
+
node_id_t node_id;
|
|
17
|
+
uint32_t crc;
|
|
18
|
+
} __packed __attribute__((aligned(4)));
|
|
19
|
+
|
|
20
|
+
static_assert(alignof(struct selva_history_event) == alignof(uint32_t));
|
|
21
|
+
|
|
22
|
+
SELVA_EXPORT
|
|
23
|
+
int selva_history_init(const char *pathname, size_t bsize, struct selva_history **hist_out);
|
|
24
|
+
|
|
25
|
+
SELVA_EXPORT
|
|
26
|
+
void selva_history_destroy(struct selva_history *hist);
|
|
27
|
+
|
|
28
|
+
SELVA_EXPORT
|
|
29
|
+
void selva_history_append(struct selva_history *hist, int64_t ts, node_id_t node_id, void *buf);
|
|
30
|
+
|
|
31
|
+
SELVA_EXPORT
|
|
32
|
+
void selva_history_fsync(struct selva_history *hist);
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Find a range.
|
|
36
|
+
* The returned buffer must be freed with selva_history_free_range().
|
|
37
|
+
*/
|
|
38
|
+
SELVA_EXPORT
|
|
39
|
+
uint32_t *selva_history_find_range(struct selva_history *hist, int64_t from, int64_t to, size_t *size_out);
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Find a range.
|
|
43
|
+
* The returned buffer must be freed with selva_history_free_range().
|
|
44
|
+
*/
|
|
45
|
+
SELVA_EXPORT
|
|
46
|
+
uint32_t *selva_history_find_range_node(struct selva_history *hist, int64_t from, int64_t to, node_id_t node_id, size_t *size_out);
|
|
47
|
+
|
|
48
|
+
SELVA_EXPORT
|
|
49
|
+
void selva_history_free_range(uint32_t *range);
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#ifndef UTIL_HLL_H
|
|
2
|
+
#define UTIL_HLL_H
|
|
3
|
+
|
|
4
|
+
#include <stdlib.h>
|
|
5
|
+
#include <stdint.h>
|
|
6
|
+
#include "selva/_export.h"
|
|
7
|
+
#include "selva_string.h"
|
|
8
|
+
#include <stdbool.h>
|
|
9
|
+
#include "cdefs.h"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
SELVA_EXPORT
|
|
13
|
+
void hll_init(struct selva_string *hllss, uint8_t precision, bool is_sparse);
|
|
14
|
+
SELVA_EXPORT
|
|
15
|
+
void hll_add(struct selva_string *hllss, uint64_t element);
|
|
16
|
+
SELVA_EXPORT
|
|
17
|
+
double hll_count(struct selva_string *hllss);
|
|
18
|
+
SELVA_EXPORT
|
|
19
|
+
struct selva_string hll_array_union(struct selva_string *hll_array, size_t count);
|
|
20
|
+
|
|
21
|
+
#endif
|
|
@@ -30,6 +30,8 @@ enum SelvaSortOrder {
|
|
|
30
30
|
SELVA_SORT_ORDER_NONE = 0,
|
|
31
31
|
SELVA_SORT_ORDER_I64_ASC,
|
|
32
32
|
SELVA_SORT_ORDER_I64_DESC,
|
|
33
|
+
SELVA_SORT_ORDER_FLOAT_ASC,
|
|
34
|
+
SELVA_SORT_ORDER_FLOAT_DESC,
|
|
33
35
|
SELVA_SORT_ORDER_DOUBLE_ASC,
|
|
34
36
|
SELVA_SORT_ORDER_DOUBLE_DESC,
|
|
35
37
|
SELVA_SORT_ORDER_BUFFER_ASC,
|
|
@@ -58,6 +60,9 @@ void selva_sort_insert(struct SelvaSortCtx *ctx, const void *p);
|
|
|
58
60
|
SELVA_EXPORT
|
|
59
61
|
void selva_sort_insert_i64(struct SelvaSortCtx *ctx, int64_t v, const void *p);
|
|
60
62
|
|
|
63
|
+
SELVA_EXPORT
|
|
64
|
+
void selva_sort_insert_float(struct SelvaSortCtx *ctx, float f, const void *p);
|
|
65
|
+
|
|
61
66
|
SELVA_EXPORT
|
|
62
67
|
void selva_sort_insert_double(struct SelvaSortCtx *ctx, double d, const void *p);
|
|
63
68
|
|
|
@@ -73,6 +78,9 @@ void selva_sort_remove(struct SelvaSortCtx *ctx, const void *p);
|
|
|
73
78
|
SELVA_EXPORT
|
|
74
79
|
void selva_sort_remove_i64(struct SelvaSortCtx *ctx, int64_t v, const void *p);
|
|
75
80
|
|
|
81
|
+
SELVA_EXPORT
|
|
82
|
+
void selva_sort_remove_float(struct SelvaSortCtx *ctx, float f, const void *p);
|
|
83
|
+
|
|
76
84
|
SELVA_EXPORT
|
|
77
85
|
void selva_sort_remove_double(struct SelvaSortCtx *ctx, double d, const void *p);
|
|
78
86
|
|
|
@@ -100,6 +108,12 @@ void *selva_sort_foreach_i64(struct SelvaSortCtx *ctx, int64_t *v);
|
|
|
100
108
|
SELVA_EXPORT
|
|
101
109
|
void *selva_sort_foreach_i64_reverse(struct SelvaSortCtx *ctx, int64_t *v);
|
|
102
110
|
|
|
111
|
+
SELVA_EXPORT
|
|
112
|
+
void *selva_sort_foreach_float(struct SelvaSortCtx *ctx, float *f);
|
|
113
|
+
|
|
114
|
+
SELVA_EXPORT
|
|
115
|
+
void *selva_sort_foreach_float_reverse(struct SelvaSortCtx *ctx, float *f);
|
|
116
|
+
|
|
103
117
|
SELVA_EXPORT
|
|
104
118
|
void *selva_sort_foreach_double(struct SelvaSortCtx *ctx, double *d);
|
|
105
119
|
|
|
@@ -55,14 +55,19 @@ enum SelvaFieldType {
|
|
|
55
55
|
|
|
56
56
|
struct EdgeFieldConstraint {
|
|
57
57
|
enum EdgeFieldConstraintFlag {
|
|
58
|
+
EDGE_FIELD_CONSTRAINT_FLAG_DEPENDENT = 0x01,
|
|
58
59
|
/**
|
|
59
60
|
* Skip saving this field while dumping.
|
|
60
61
|
*/
|
|
61
|
-
EDGE_FIELD_CONSTRAINT_FLAG_SKIP_DUMP
|
|
62
|
+
EDGE_FIELD_CONSTRAINT_FLAG_SKIP_DUMP = 0x80,
|
|
62
63
|
} __packed flags;
|
|
63
64
|
field_t inverse_field;
|
|
64
65
|
node_type_t dst_node_type;
|
|
65
|
-
|
|
66
|
+
/**
|
|
67
|
+
* Don't use directly!
|
|
68
|
+
* Use: `selva_get_edge_field_fields_schema()`
|
|
69
|
+
*/
|
|
70
|
+
struct SelvaFieldsSchema *_fields_schema;
|
|
66
71
|
};
|
|
67
72
|
|
|
68
73
|
struct SelvaFieldSchema {
|
|
@@ -92,7 +97,9 @@ struct SelvaFieldsSchema {
|
|
|
92
97
|
};
|
|
93
98
|
|
|
94
99
|
struct SelvaNodeSchema {
|
|
100
|
+
size_t nr_aliases; /*!< Number of alias fields in this type. */
|
|
95
101
|
struct SelvaFieldsSchema fields_schema;
|
|
102
|
+
/* Nothing must be put after this line. */
|
|
96
103
|
};
|
|
97
104
|
|
|
98
105
|
struct SelvaAlias;
|