@based/db 0.0.31 → 0.0.33
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 +565 -3
- package/dist/lib/darwin_aarch64/include/selva/db.h +1 -1
- 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/libselva.dylib +0 -0
- package/dist/lib/linux_aarch64/include/selva/db.h +1 -1
- 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/libselva.so +0 -0
- package/dist/lib/linux_x86_64/include/selva/db.h +1 -1
- 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/libselva.so +0 -0
- package/dist/src/client/flushModify.js +5 -4
- package/dist/src/client/index.d.ts +3 -2
- package/dist/src/client/modify/binary.js +1 -1
- package/dist/src/client/modify/cardinality.js +1 -1
- package/dist/src/client/modify/references/appendEdgeRefs.js +3 -0
- package/dist/src/client/modify/references/edge.js +6 -0
- package/dist/src/client/modify/references/getEdgeSize.js +1 -1
- package/dist/src/client/modify/string.js +10 -4
- package/dist/src/client/modify/text.js +1 -9
- package/dist/src/client/modify/types.d.ts +1 -0
- package/dist/src/client/modify/types.js +1 -0
- package/dist/src/client/modify/upsert.js +33 -21
- package/dist/src/client/query/BasedDbQuery.js +1 -1
- package/dist/src/client/query/BasedIterable.d.ts +2 -2
- package/dist/src/client/query/BasedIterable.js +7 -1
- package/dist/src/client/query/debug.js +3 -2
- package/dist/src/client/query/display.js +1 -1
- package/dist/src/client/query/filter/createFixedFilterBuffer.js +1 -1
- package/dist/src/client/query/filter/createVariableFilterBuffer.js +1 -1
- package/dist/src/client/query/filter/parseFilterValue.js +1 -2
- package/dist/src/client/query/queryDef.js +2 -2
- package/dist/src/client/query/read/read.js +4 -14
- package/dist/src/client/query/registerQuery.js +1 -1
- package/dist/src/client/query/search/index.js +1 -1
- package/dist/src/client/query/toBuffer.js +11 -15
- package/dist/src/client/query/types.d.ts +7 -0
- package/dist/src/client/query/types.js +8 -0
- package/dist/src/client/query/validation.d.ts +1 -1
- package/dist/src/client/query/validation.js +4 -5
- package/dist/src/client/string.js +1 -3
- package/dist/src/index.d.ts +1 -0
- package/dist/src/index.js +5 -1
- package/dist/src/native.d.ts +1 -2
- package/dist/src/native.js +2 -5
- package/dist/src/server/csmt/draw-dot.d.ts +3 -1
- package/dist/src/server/csmt/draw-dot.js +7 -2
- package/dist/src/server/csmt/memebership-proof.d.ts +1 -1
- package/dist/src/server/csmt/tree-utils.d.ts +4 -4
- package/dist/src/server/csmt/tree.d.ts +1 -1
- package/dist/src/server/csmt/tree.js +1 -1
- package/dist/src/server/csmt/types.d.ts +10 -10
- package/dist/src/server/dbHash.js +1 -1
- package/dist/src/server/index.d.ts +6 -2
- package/dist/src/server/index.js +28 -13
- package/dist/src/server/migrate/worker.js +11 -0
- package/dist/src/server/save.js +3 -2
- package/dist/src/server/start.js +9 -5
- package/dist/src/utils.d.ts +0 -10
- package/dist/src/utils.js +0 -152
- package/package.json +3 -3
- package/dist/lib/darwin_aarch64/include/selva/xxhash64.h +0 -23
- package/dist/lib/linux_aarch64/include/selva/xxhash64.h +0 -23
- package/dist/lib/linux_x86_64/include/selva/xxhash64.h +0 -23
- package/dist/src/client/query/subscription/markers.d.ts +0 -10
- package/dist/src/client/query/subscription/markers.js +0 -213
- package/dist/src/client/query/subscription/run.d.ts +0 -5
- package/dist/src/client/query/subscription/run.js +0 -76
- package/dist/src/client/tmpBuffer.d.ts +0 -3
- package/dist/src/client/tmpBuffer.js +0 -20
package/README.md
CHANGED
|
@@ -23,11 +23,11 @@ BasedDb is a powerful database solution that supports various data types, refere
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
npm i
|
|
26
|
-
npm run get-napi
|
|
26
|
+
npm run get-napi # only need this the first time
|
|
27
27
|
npm run build
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
##
|
|
30
|
+
## Testing
|
|
31
31
|
|
|
32
32
|
Run all tests + ldb + build c, zig and js
|
|
33
33
|
|
|
@@ -47,7 +47,7 @@ Run specific test file & run specific test
|
|
|
47
47
|
npm run test -- range.js:range
|
|
48
48
|
```
|
|
49
49
|
|
|
50
|
-
Different flavours of test
|
|
50
|
+
Different flavours of test:
|
|
51
51
|
|
|
52
52
|
Only builds zig
|
|
53
53
|
|
|
@@ -60,3 +60,565 @@ Builds nothing only runs tests
|
|
|
60
60
|
```bash
|
|
61
61
|
npm run test-fast
|
|
62
62
|
```
|
|
63
|
+
|
|
64
|
+
## API Documentation
|
|
65
|
+
|
|
66
|
+
This documentation is generated based on the features demonstrated in the \`./test\` directory.
|
|
67
|
+
|
|
68
|
+
### Setup
|
|
69
|
+
|
|
70
|
+
```typescript
|
|
71
|
+
import { BasedDb } from './src/index.js' // Adjust import path as needed
|
|
72
|
+
|
|
73
|
+
// Instantiate the database
|
|
74
|
+
const db = new BasedDb({
|
|
75
|
+
path: './db-directory', // Path to store database files
|
|
76
|
+
saveIntervalInSeconds: 1, // Optional: Auto-save interval (e.g., every second)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Start the database (connects/loads existing or creates new)
|
|
80
|
+
// clean: true wipes data on start if path exists
|
|
81
|
+
await db.start({ clean: true })
|
|
82
|
+
|
|
83
|
+
// Define the database schema (required before most operations)
|
|
84
|
+
await db.setSchema({
|
|
85
|
+
// Optional: Define locales for 'text' fields
|
|
86
|
+
locales: {
|
|
87
|
+
en: { required: true }, // 'en' is required
|
|
88
|
+
nl: { fallback: ['en'] }, // 'nl' falls back to 'en' if missing
|
|
89
|
+
fi: { fallback: ['en'] },
|
|
90
|
+
},
|
|
91
|
+
// Optional: Define properties directly on the root node
|
|
92
|
+
props: {
|
|
93
|
+
siteName: 'string',
|
|
94
|
+
featuredItems: { items: { ref: 'product' } },
|
|
95
|
+
},
|
|
96
|
+
// Define data types
|
|
97
|
+
types: {
|
|
98
|
+
user: {
|
|
99
|
+
props: {
|
|
100
|
+
name: 'string', // Simple string
|
|
101
|
+
email: { type: 'alias', required: true }, // Unique alias (indexed string)
|
|
102
|
+
age: 'uint32', // Unsigned 32-bit integer
|
|
103
|
+
score: { type: 'number', min: 0, max: 100, step: 0.5 }, // Float with validation
|
|
104
|
+
isActive: 'boolean', // Boolean true/false
|
|
105
|
+
createdAt: { type: 'timestamp', on: 'create' }, // Auto-set on creation
|
|
106
|
+
updatedAt: { type: 'timestamp', on: 'update' }, // Auto-set on update/create
|
|
107
|
+
bio: 'text', // Multi-language text
|
|
108
|
+
friends: {
|
|
109
|
+
// List of references
|
|
110
|
+
items: {
|
|
111
|
+
ref: 'user', // References 'user' type
|
|
112
|
+
prop: 'friends', // Bidirectional link property
|
|
113
|
+
$friendshipLevel: 'uint8', // Optional: Edge property
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
bestFriend: {
|
|
117
|
+
// Single reference
|
|
118
|
+
ref: 'user',
|
|
119
|
+
prop: 'bestFriendOf', // Bidirectional link
|
|
120
|
+
},
|
|
121
|
+
profilePicture: 'binary', // Raw binary data (Uint8Array)
|
|
122
|
+
settings: 'json', // JSON object/array
|
|
123
|
+
visits: 'cardinality', // HyperLogLog counter for unique values
|
|
124
|
+
embedding: { type: 'vector', size: 5 }, // Fixed-size float vector (size from test)
|
|
125
|
+
status: ['pending', 'active', 'inactive'], // Enum type
|
|
126
|
+
countryCode: { type: 'string', maxBytes: 2 }, // String with max byte length
|
|
127
|
+
nestedData: {
|
|
128
|
+
// Nested object
|
|
129
|
+
type: 'object',
|
|
130
|
+
props: {
|
|
131
|
+
value: 'string',
|
|
132
|
+
nestedRef: { ref: 'product', prop: 'nestedUsers' },
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
customValidated: {
|
|
136
|
+
// Custom validation function
|
|
137
|
+
type: 'number',
|
|
138
|
+
validation: (v) => v % 2 === 0, // Must be an even number
|
|
139
|
+
},
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
product: {
|
|
143
|
+
props: {
|
|
144
|
+
title: 'text',
|
|
145
|
+
price: 'number',
|
|
146
|
+
nestedUsers: { items: { ref: 'user', prop: 'nestedData.nestedRef' } },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
// Other types from tests (e.g., payment, round, vote, contestant, dialog, article, etc.)
|
|
150
|
+
// ...
|
|
151
|
+
},
|
|
152
|
+
})
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Create
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
// Create a new node
|
|
159
|
+
const userId = await db.create('user', {
|
|
160
|
+
name: 'Alice',
|
|
161
|
+
email: 'alice@example.com',
|
|
162
|
+
})
|
|
163
|
+
// userId is the numeric ID of the created node
|
|
164
|
+
|
|
165
|
+
// Create with references and edge properties
|
|
166
|
+
const friendId = await db.create('user', {
|
|
167
|
+
name: 'Bob',
|
|
168
|
+
email: 'bob@example.com',
|
|
169
|
+
})
|
|
170
|
+
const userWithFriendId = await db.create('user', {
|
|
171
|
+
name: 'Charlie',
|
|
172
|
+
email: 'charlie@example.com',
|
|
173
|
+
friends: [{ id: friendId, $friendshipLevel: 5 }], // Reference Bob with edge data
|
|
174
|
+
bestFriend: friendId, // Single reference
|
|
175
|
+
})
|
|
176
|
+
|
|
177
|
+
// Create text with specific locale
|
|
178
|
+
const dialogFi = await db.create('dialog', { fun: 'hauskaa' }, { locale: 'fi' })
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
### Read (Query)
|
|
182
|
+
|
|
183
|
+
The \`query\` method starts building a request to retrieve data.
|
|
184
|
+
|
|
185
|
+
```typescript
|
|
186
|
+
// Query by type and optionally ID(s) or alias
|
|
187
|
+
db.query('user') // Query all users (limited by default)
|
|
188
|
+
db.query('user', userId) // Query a single user by ID
|
|
189
|
+
db.query('user', [userId, friendId]) // Query multiple users by IDs
|
|
190
|
+
db.query('user', { alias: 'alice@example.com' }) // Query by unique alias
|
|
191
|
+
db.query() // Query root properties
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
#### \`.include(...fields)\`
|
|
195
|
+
|
|
196
|
+
Specifies which fields to return. Supports nested fields and edge properties.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
// Include specific fields
|
|
200
|
+
await db.query('user', userId).include('name', 'age').get()
|
|
201
|
+
// -> { id: userId, name: 'Alice', age: 30 }
|
|
202
|
+
|
|
203
|
+
// Include all direct fields (non-nested, non-reference)
|
|
204
|
+
await db.query('user', userId).include('*').get()
|
|
205
|
+
// -> { id: userId, name: 'Alice', email: '...', age: 30, ... } (no friends, bestFriend data)
|
|
206
|
+
|
|
207
|
+
// Include nested fields and reference fields
|
|
208
|
+
await db
|
|
209
|
+
.query('user', userWithFriendId)
|
|
210
|
+
.include('name', 'friends.name', 'bestFriend.email')
|
|
211
|
+
.get()
|
|
212
|
+
// -> { id: ..., name: 'Charlie', friends: [{ id: ..., name: 'Bob' }], bestFriend: { id: ..., email: '...' } }
|
|
213
|
+
|
|
214
|
+
// Include specific language from a 'text' field
|
|
215
|
+
await db.query('user', userId).include('bio.en').get()
|
|
216
|
+
// -> { id: userId, bio: { en: 'Engineer' } }
|
|
217
|
+
|
|
218
|
+
// Include edge properties from a reference list
|
|
219
|
+
await db
|
|
220
|
+
.query('user', userWithFriendId)
|
|
221
|
+
.include('friends.$friendshipLevel')
|
|
222
|
+
.get()
|
|
223
|
+
// -> { id: ..., friends: [{ id: ..., $friendshipLevel: 5 }] }
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
#### \`.filter(field, operator, value, options?)\`
|
|
227
|
+
|
|
228
|
+
Filters the results based on field values.
|
|
229
|
+
|
|
230
|
+
Operators:
|
|
231
|
+
|
|
232
|
+
- \`=\`: Equal to (works for most types, including exact string match).
|
|
233
|
+
- \`!=\`: Not equal to.
|
|
234
|
+
- \`>\`: Greater than (numbers, timestamps).
|
|
235
|
+
- \`<\`: Less than (numbers, timestamps).
|
|
236
|
+
- \`>=\`: Greater than or equal to.
|
|
237
|
+
- \`<=\`: Less than or equal to.
|
|
238
|
+
- \`has\`: Contains substring (case-insensitive by default for \`string\`, \`text\`).
|
|
239
|
+
- \`like\`: Fuzzy search / similarity (for \`string\`, \`text\`, \`vector\`).
|
|
240
|
+
|
|
241
|
+
**Filter Examples:**
|
|
242
|
+
|
|
243
|
+
- **Equality (=)**
|
|
244
|
+
Finds nodes where the field exactly matches the value.
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Find users named exactly 'Alice'
|
|
248
|
+
await db.query('user').filter('name', '=', 'Alice').get()
|
|
249
|
+
|
|
250
|
+
// Find users with age 30
|
|
251
|
+
await db.query('user').filter('age', '=', 30).get()
|
|
252
|
+
|
|
253
|
+
// Find users with a specific country code
|
|
254
|
+
await db.query('user').filter('countryCode', '=', 'NL').get()
|
|
255
|
+
|
|
256
|
+
// Find users with a specific vector (exact match)
|
|
257
|
+
const queryVector = new Float32Array([
|
|
258
|
+
/* ... */
|
|
259
|
+
])
|
|
260
|
+
await db.query('user').filter('embedding', '=', queryVector).get()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
- **Inequality (!=)**
|
|
264
|
+
Finds nodes where the field does _not_ match the value.
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
// Find users not named 'Alice'
|
|
268
|
+
await db.query('user').filter('name', '!=', 'Alice').get()
|
|
269
|
+
|
|
270
|
+
// Find users whose status is not 'pending'
|
|
271
|
+
await db.query('user').filter('status', '!=', 'pending').get()
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
- **Comparison (>, \`<, >=, <=)**
|
|
275
|
+
Finds nodes based on numerical or timestamp comparisons.
|
|
276
|
+
|
|
277
|
+
```typescript
|
|
278
|
+
// Find users older than 50
|
|
279
|
+
await db.query('user').filter('age', '>', 50).get()
|
|
280
|
+
|
|
281
|
+
// Find users with a score less than or equal to 75.5
|
|
282
|
+
await db.query('user').filter('score', '<=', 75.5).get()
|
|
283
|
+
|
|
284
|
+
// Find users created within the last 24 hours
|
|
285
|
+
await db
|
|
286
|
+
.query('user')
|
|
287
|
+
.filter('createdAt', '>=', Date.now() - 86400000)
|
|
288
|
+
.get()
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
- **String/Text Contains (has)**
|
|
292
|
+
Finds nodes where a \`string\` or \`text\` field includes a substring. Case-insensitive by default.
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
// Find users whose name contains 'ali' (matches 'Alice', 'Ali', 'Salim', etc.)
|
|
296
|
+
await db.query('user').filter('name', 'has', 'ali').get()
|
|
297
|
+
|
|
298
|
+
// Find users whose name contains 'ALI' (case-sensitive)
|
|
299
|
+
await db
|
|
300
|
+
.query('user')
|
|
301
|
+
.filter('name', 'has', 'ALI', { lowerCase: false })
|
|
302
|
+
.get()
|
|
303
|
+
|
|
304
|
+
// Find users whose bio (any language) contains 'engineer'
|
|
305
|
+
await db.query('user').filter('bio', 'has', 'engineer').get()
|
|
306
|
+
|
|
307
|
+
// Find users whose English bio contains 'dev'
|
|
308
|
+
await db.query('user').filter('bio.en', 'has', 'dev').get()
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
- **Fuzzy Match / Similarity (like)**
|
|
312
|
+
Finds nodes based on approximate matching for \`string\`, \`text\`, or \`vector\` types.
|
|
313
|
+
|
|
314
|
+
```typescript
|
|
315
|
+
// Find users whose bio might contain a typo like 'engneer'
|
|
316
|
+
await db.query('user').filter('bio', 'like', 'engneer').get()
|
|
317
|
+
|
|
318
|
+
// Find users whose embedding vector is similar to queryVector (cosine similarity >= 0.8)
|
|
319
|
+
const queryVector = new Float32Array([
|
|
320
|
+
/* ... */
|
|
321
|
+
])
|
|
322
|
+
await db
|
|
323
|
+
.query('user')
|
|
324
|
+
.filter('embedding', 'like', queryVector, { score: 0.8 })
|
|
325
|
+
.get()
|
|
326
|
+
|
|
327
|
+
// Find users whose embedding vector is similar (Euclidean distance <= 1.0)
|
|
328
|
+
await db
|
|
329
|
+
.query('user')
|
|
330
|
+
.filter('embedding', 'like', queryVector, {
|
|
331
|
+
fn: 'euclideanDistance',
|
|
332
|
+
score: 1.0,
|
|
333
|
+
})
|
|
334
|
+
.get()
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
- **Boolean Filtering**
|
|
338
|
+
Finds nodes based on a boolean field's value.
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
// Find active users (explicitly true)
|
|
342
|
+
await db.query('user').filter('isActive', '=', true).get()
|
|
343
|
+
|
|
344
|
+
// Find active users (shortcut for true)
|
|
345
|
+
await db.query('user').filter('isActive').get()
|
|
346
|
+
|
|
347
|
+
// Find inactive users
|
|
348
|
+
await db.query('user').filter('isActive', false).get()
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
- **Enum Filtering**
|
|
352
|
+
Finds nodes where an \`enum\` field matches a specific value.
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
// Find users with status 'active'
|
|
356
|
+
await db.query('user').filter('status', '=', 'active').get()
|
|
357
|
+
|
|
358
|
+
// Find users whose status is not 'pending'
|
|
359
|
+
await db.query('user').filter('status', '!=', 'pending').get()
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
- **Filtering on Nested Fields**
|
|
363
|
+
Uses dot notation to access fields within nested objects.
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
// Find users where nestedData.value is 'nested info'
|
|
367
|
+
await db.query('user').filter('nestedData.value', '=', 'nested info').get()
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
- **Filtering on Reference Fields**
|
|
371
|
+
Uses dot notation to filter based on fields of referenced nodes.
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// Find users whose best friend is named 'Bob'
|
|
375
|
+
await db.query('user').filter('bestFriend.name', '=', 'Bob').get()
|
|
376
|
+
|
|
377
|
+
// Find users who have at least one friend older than 30
|
|
378
|
+
await db.query('user').filter('friends.age', '>', 30).get()
|
|
379
|
+
|
|
380
|
+
// Find users whose best friend's status is 'active'
|
|
381
|
+
await db.query('user').filter('bestFriend.status', '=', 'active').get()
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
#### \`.sort(field, direction?)\`
|
|
385
|
+
|
|
386
|
+
Sorts the results by a specific field. \`direction\` can be \`'asc'\` (default) or \`'desc'\`.
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
// Sort by age descending
|
|
390
|
+
await db.query('user').sort('age', 'desc').get()
|
|
391
|
+
|
|
392
|
+
// Sort by name ascending
|
|
393
|
+
await db.query('user').sort('name').get() // 'asc' is default
|
|
394
|
+
|
|
395
|
+
// Sort by text field (uses locale if provided)
|
|
396
|
+
await db.query('user').locale('nl').sort('bio').get()
|
|
397
|
+
|
|
398
|
+
// Sort by cardinality (HLL count)
|
|
399
|
+
await db.query('user').sort('visits', 'desc').get()
|
|
400
|
+
|
|
401
|
+
// Sort by alias
|
|
402
|
+
await db.query('article').sort('email', 'desc').get()
|
|
403
|
+
|
|
404
|
+
// Sort by timestamp
|
|
405
|
+
await db.query('event').sort('startTime').get()
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
#### \`.range(offset, limit)\`
|
|
409
|
+
|
|
410
|
+
Paginates the results.
|
|
411
|
+
|
|
412
|
+
```typescript
|
|
413
|
+
// Get users 11-20 sorted by name
|
|
414
|
+
await db.query('user').range(10, 10).sort('name').get()
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
#### \`.search(term, fieldOrWeights, options?)\`
|
|
418
|
+
|
|
419
|
+
Performs full-text or vector search, returning results sorted by relevance/similarity.
|
|
420
|
+
|
|
421
|
+
```typescript
|
|
422
|
+
// Full-text search for 'engineer' in 'name' and 'bio' fields
|
|
423
|
+
await db.query('user').search('engineer', 'name', 'bio').get()
|
|
424
|
+
|
|
425
|
+
// Full-text search with field weights
|
|
426
|
+
await db.query('user').search('engineer', { bio: 0.8, name: 0.2 }).get()
|
|
427
|
+
|
|
428
|
+
// Vector search (returns sorted by similarity)
|
|
429
|
+
const queryVector = new Float32Array([
|
|
430
|
+
/* ... */
|
|
431
|
+
])
|
|
432
|
+
await db.query('user').search(queryVector, 'embedding', { score: 0.7 }).get()
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### \`.locale(langCode)\`
|
|
436
|
+
|
|
437
|
+
Specifies the language context for \`text\` fields in \`include\`, \`filter\`, and \`sort\`.
|
|
438
|
+
|
|
439
|
+
```typescript
|
|
440
|
+
// Get 'bio' in 'nl', falling back to 'en' if 'nl' is missing
|
|
441
|
+
await db.query('user', userId).locale('nl').include('bio').get()
|
|
442
|
+
// -> { id: userId, bio: 'Ingenieur' } (assuming 'nl' exists, else 'Engineer')
|
|
443
|
+
|
|
444
|
+
// Filter based on 'nl' text, return 'nl' text
|
|
445
|
+
await db
|
|
446
|
+
.query('user')
|
|
447
|
+
.locale('nl')
|
|
448
|
+
.filter('bio', 'has', 'ingenieur')
|
|
449
|
+
.include('bio')
|
|
450
|
+
.get()
|
|
451
|
+
```
|
|
452
|
+
|
|
453
|
+
#### \`.get()\`
|
|
454
|
+
|
|
455
|
+
Executes the query and returns a \`BasedQueryResponse\` promise.
|
|
456
|
+
|
|
457
|
+
```typescript
|
|
458
|
+
const response = await db.query('user').include('name').get()
|
|
459
|
+
|
|
460
|
+
// Get results as plain objects
|
|
461
|
+
const userObjects = response.toObject() // Returns array or single object/null
|
|
462
|
+
|
|
463
|
+
// Get single node result directly (for single ID or alias queries)
|
|
464
|
+
const singleUserObject = await db
|
|
465
|
+
.query('user', userId)
|
|
466
|
+
.get()
|
|
467
|
+
.then((res) => res.node()) // Returns single object or null
|
|
468
|
+
```
|
|
469
|
+
|
|
470
|
+
#### Combining Methods
|
|
471
|
+
|
|
472
|
+
Query methods can be chained together.
|
|
473
|
+
|
|
474
|
+
```typescript
|
|
475
|
+
const specificUsers = await db
|
|
476
|
+
.query('user')
|
|
477
|
+
.filter('age', '>', 25)
|
|
478
|
+
.filter('status', '=', 'active')
|
|
479
|
+
.sort('createdAt', 'desc')
|
|
480
|
+
.range(0, 5)
|
|
481
|
+
.include('name', 'email')
|
|
482
|
+
.locale('en') // Optional: set locale context
|
|
483
|
+
.get()
|
|
484
|
+
.then((res) => res.toObject()) // Get plain objects
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
### Update
|
|
488
|
+
|
|
489
|
+
```typescript
|
|
490
|
+
// Update specific fields
|
|
491
|
+
await db.update('user', userId, { age: 31, isActive: false })
|
|
492
|
+
|
|
493
|
+
// Update using payload.id
|
|
494
|
+
await db.update('user', { id: userId, age: 32 })
|
|
495
|
+
|
|
496
|
+
// Atomic increment/decrement (number/timestamp)
|
|
497
|
+
await db.update('user', userId, {
|
|
498
|
+
age: { increment: 1 },
|
|
499
|
+
score: { decrement: 0.5 },
|
|
500
|
+
})
|
|
501
|
+
|
|
502
|
+
// Update references (single)
|
|
503
|
+
await db.update('user', userWithFriendId, { bestFriend: userId })
|
|
504
|
+
|
|
505
|
+
// Update references (list) - add, delete, update edge props
|
|
506
|
+
await db.update('user', userWithFriendId, {
|
|
507
|
+
friends: {
|
|
508
|
+
add: [userId],
|
|
509
|
+
delete: [friendId],
|
|
510
|
+
update: [{ id: userId, $friendshipLevel: 10 }],
|
|
511
|
+
},
|
|
512
|
+
})
|
|
513
|
+
|
|
514
|
+
// Replace references (list)
|
|
515
|
+
await db.update('user', userWithFriendId, { friends: [userId] }) // Replaces entire list
|
|
516
|
+
|
|
517
|
+
// Clear references
|
|
518
|
+
await db.update('user', userWithFriendId, { friends: null, bestFriend: null })
|
|
519
|
+
|
|
520
|
+
// Update cardinality field
|
|
521
|
+
await db.update('user', userId, { visits: 'newSession' }) // Adds 'newSession' if unique
|
|
522
|
+
|
|
523
|
+
// Update root properties
|
|
524
|
+
await db.update({ siteName: 'My Awesome Site V2' })
|
|
525
|
+
|
|
526
|
+
// Update text with specific locale
|
|
527
|
+
await db.update(
|
|
528
|
+
'dialog',
|
|
529
|
+
dialogFi,
|
|
530
|
+
{ fun: 'vielä hauskempaa' },
|
|
531
|
+
{ locale: 'fi' },
|
|
532
|
+
)
|
|
533
|
+
|
|
534
|
+
// Update timestamp with string parsing
|
|
535
|
+
await db.update('user', userId, { updatedAt: 'now + 1h' }) // Relative time
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Upsert
|
|
539
|
+
|
|
540
|
+
Update if alias exists, otherwise create.
|
|
541
|
+
|
|
542
|
+
```typescript
|
|
543
|
+
await db.upsert('user', {
|
|
544
|
+
alias: 'alice@example.com', // The alias to match
|
|
545
|
+
name: 'Alice Smith', // Field to update or set on create
|
|
546
|
+
age: 33, // Another field
|
|
547
|
+
})
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
### Delete
|
|
551
|
+
|
|
552
|
+
```typescript
|
|
553
|
+
// Delete a node by ID
|
|
554
|
+
await db.delete('user', userId)
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
### Persistence & Control
|
|
558
|
+
|
|
559
|
+
```typescript
|
|
560
|
+
// Manually trigger a save to disk
|
|
561
|
+
await db.save()
|
|
562
|
+
|
|
563
|
+
// Wait for pending modifications to be processed
|
|
564
|
+
await db.drain()
|
|
565
|
+
// or use the promise returned by isModified()
|
|
566
|
+
await db.isModified()
|
|
567
|
+
|
|
568
|
+
// Gracefully stop the database (saves pending changes)
|
|
569
|
+
await db.stop()
|
|
570
|
+
|
|
571
|
+
// Destroy the instance (call stop() first for graceful shutdown)
|
|
572
|
+
await db.destroy()
|
|
573
|
+
|
|
574
|
+
// Completely clear all data and schema (USE WITH CAUTION!)
|
|
575
|
+
await db.wipe()
|
|
576
|
+
```
|
|
577
|
+
|
|
578
|
+
### Validation
|
|
579
|
+
|
|
580
|
+
BasedDb automatically validates data against the schema on \`create\`, \`update\`, and \`upsert\`. It checks types, required fields, enums, number constraints (min, \`max\`, \`step\`), string constraints (maxBytes), vector size, references, aliases, locales, and custom validation functions. Invalid operations will throw an error.
|
|
581
|
+
|
|
582
|
+
```typescript
|
|
583
|
+
// Example: This would throw if 'invalid-status' is not in the enum
|
|
584
|
+
// await db.create('user', { status: 'invalid-status', email: 'fail@example.com' })
|
|
585
|
+
|
|
586
|
+
// Example: This would throw if score is > 100
|
|
587
|
+
// await db.update('user', userId, { score: 101 })
|
|
588
|
+
|
|
589
|
+
// Example: This would throw if the custom validation fails (e.g., not an even number)
|
|
590
|
+
// await db.create('user', { customValidated: 3, email: 'customfail@example.com' })
|
|
591
|
+
```
|
|
592
|
+
|
|
593
|
+
### Subscriptions (Client/Server Context)
|
|
594
|
+
|
|
595
|
+
Note: Subscriptions are primarily managed by a higher-level client/server setup.
|
|
596
|
+
|
|
597
|
+
```typescript
|
|
598
|
+
// Client-side subscription setup (simplified)
|
|
599
|
+
const closeSub = db
|
|
600
|
+
.query('user')
|
|
601
|
+
.filter('isActive')
|
|
602
|
+
.include('name')
|
|
603
|
+
.subscribe((data) => {
|
|
604
|
+
console.log('Subscription update:', data.toObject())
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
// To stop listening:
|
|
608
|
+
closeSub()
|
|
609
|
+
```
|
|
610
|
+
|
|
611
|
+
### Schema Updates (Client/Server Context)
|
|
612
|
+
|
|
613
|
+
Note: Schema updates in a client/server environment involve coordination.
|
|
614
|
+
|
|
615
|
+
```typescript
|
|
616
|
+
// Client initiates a schema update
|
|
617
|
+
await clientDb.setSchema({
|
|
618
|
+
types: {
|
|
619
|
+
/* ... new or modified schema ... */
|
|
620
|
+
},
|
|
621
|
+
})
|
|
622
|
+
|
|
623
|
+
// Server processes and notifies clients.
|
|
624
|
+
```
|
|
@@ -205,7 +205,7 @@ SELVA_EXPORT
|
|
|
205
205
|
void selva_expire_node_cancel(struct SelvaDb *db, node_type_t type, node_id_t node_id);
|
|
206
206
|
|
|
207
207
|
SELVA_EXPORT
|
|
208
|
-
void selva_db_expire_tick(struct SelvaDb *db, int64_t now);
|
|
208
|
+
void selva_db_expire_tick(struct SelvaDb *db, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx, int64_t now);
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
211
|
* Delete a node.
|
|
@@ -105,7 +105,6 @@ void selva_fields_ensure_ref_meta(
|
|
|
105
105
|
*/
|
|
106
106
|
SELVA_EXPORT
|
|
107
107
|
int selva_fields_set(
|
|
108
|
-
struct SelvaDb *db,
|
|
109
108
|
struct SelvaNode *node,
|
|
110
109
|
const struct SelvaFieldSchema *fs,
|
|
111
110
|
const void *value, size_t len);
|
|
@@ -117,7 +116,6 @@ int selva_fields_set(
|
|
|
117
116
|
*/
|
|
118
117
|
SELVA_EXPORT
|
|
119
118
|
int fields_set2(
|
|
120
|
-
struct SelvaDb *db,
|
|
121
119
|
struct SelvaNode *node,
|
|
122
120
|
const struct SelvaFieldSchema *fs,
|
|
123
121
|
struct SelvaFields *fields,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -205,7 +205,7 @@ SELVA_EXPORT
|
|
|
205
205
|
void selva_expire_node_cancel(struct SelvaDb *db, node_type_t type, node_id_t node_id);
|
|
206
206
|
|
|
207
207
|
SELVA_EXPORT
|
|
208
|
-
void selva_db_expire_tick(struct SelvaDb *db, int64_t now);
|
|
208
|
+
void selva_db_expire_tick(struct SelvaDb *db, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx, int64_t now);
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
211
|
* Delete a node.
|
|
@@ -105,7 +105,6 @@ void selva_fields_ensure_ref_meta(
|
|
|
105
105
|
*/
|
|
106
106
|
SELVA_EXPORT
|
|
107
107
|
int selva_fields_set(
|
|
108
|
-
struct SelvaDb *db,
|
|
109
108
|
struct SelvaNode *node,
|
|
110
109
|
const struct SelvaFieldSchema *fs,
|
|
111
110
|
const void *value, size_t len);
|
|
@@ -117,7 +116,6 @@ int selva_fields_set(
|
|
|
117
116
|
*/
|
|
118
117
|
SELVA_EXPORT
|
|
119
118
|
int fields_set2(
|
|
120
|
-
struct SelvaDb *db,
|
|
121
119
|
struct SelvaNode *node,
|
|
122
120
|
const struct SelvaFieldSchema *fs,
|
|
123
121
|
struct SelvaFields *fields,
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -205,7 +205,7 @@ SELVA_EXPORT
|
|
|
205
205
|
void selva_expire_node_cancel(struct SelvaDb *db, node_type_t type, node_id_t node_id);
|
|
206
206
|
|
|
207
207
|
SELVA_EXPORT
|
|
208
|
-
void selva_db_expire_tick(struct SelvaDb *db, int64_t now);
|
|
208
|
+
void selva_db_expire_tick(struct SelvaDb *db, selva_dirty_node_cb_t dirty_cb, void *dirty_ctx, int64_t now);
|
|
209
209
|
|
|
210
210
|
/**
|
|
211
211
|
* Delete a node.
|
|
@@ -105,7 +105,6 @@ void selva_fields_ensure_ref_meta(
|
|
|
105
105
|
*/
|
|
106
106
|
SELVA_EXPORT
|
|
107
107
|
int selva_fields_set(
|
|
108
|
-
struct SelvaDb *db,
|
|
109
108
|
struct SelvaNode *node,
|
|
110
109
|
const struct SelvaFieldSchema *fs,
|
|
111
110
|
const void *value, size_t len);
|
|
@@ -117,7 +116,6 @@ int selva_fields_set(
|
|
|
117
116
|
*/
|
|
118
117
|
SELVA_EXPORT
|
|
119
118
|
int fields_set2(
|
|
120
|
-
struct SelvaDb *db,
|
|
121
119
|
struct SelvaNode *node,
|
|
122
120
|
const struct SelvaFieldSchema *fs,
|
|
123
121
|
struct SelvaFields *fields,
|