@onchaindb/sdk 0.4.5 → 2.0.0
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/.claude/settings.local.json +10 -2
- package/README.md +422 -355
- package/dist/batch.d.ts +1 -10
- package/dist/batch.d.ts.map +1 -1
- package/dist/batch.js +4 -26
- package/dist/batch.js.map +1 -1
- package/dist/client.d.ts +31 -46
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +222 -357
- package/dist/client.js.map +1 -1
- package/dist/database.d.ts +14 -131
- package/dist/database.d.ts.map +1 -1
- package/dist/database.js +35 -131
- package/dist/database.js.map +1 -1
- package/dist/index.d.ts +10 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -18
- package/dist/index.js.map +1 -1
- package/dist/query-sdk/ConditionBuilder.d.ts +3 -11
- package/dist/query-sdk/ConditionBuilder.d.ts.map +1 -1
- package/dist/query-sdk/ConditionBuilder.js +10 -48
- package/dist/query-sdk/ConditionBuilder.js.map +1 -1
- package/dist/query-sdk/NestedBuilders.d.ts +33 -30
- package/dist/query-sdk/NestedBuilders.d.ts.map +1 -1
- package/dist/query-sdk/NestedBuilders.js +46 -43
- package/dist/query-sdk/NestedBuilders.js.map +1 -1
- package/{src/query-sdk/dist/OnChainDB.d.ts → dist/query-sdk/OnDB.d.ts} +10 -2
- package/dist/query-sdk/OnDB.d.ts.map +1 -0
- package/{src/query-sdk/dist/OnChainDB.js → dist/query-sdk/OnDB.js} +86 -18
- package/dist/query-sdk/OnDB.js.map +1 -0
- package/dist/query-sdk/QueryBuilder.d.ts +4 -2
- package/dist/query-sdk/QueryBuilder.d.ts.map +1 -1
- package/dist/query-sdk/QueryBuilder.js +47 -169
- package/dist/query-sdk/QueryBuilder.js.map +1 -1
- package/dist/query-sdk/QueryResult.d.ts +0 -38
- package/dist/query-sdk/QueryResult.d.ts.map +1 -1
- package/dist/query-sdk/QueryResult.js +1 -227
- package/dist/query-sdk/QueryResult.js.map +1 -1
- package/dist/query-sdk/index.d.ts +2 -2
- package/dist/query-sdk/index.d.ts.map +1 -1
- package/dist/query-sdk/index.js +3 -3
- package/dist/query-sdk/index.js.map +1 -1
- package/dist/query-sdk/operators.d.ts +32 -28
- package/dist/query-sdk/operators.d.ts.map +1 -1
- package/dist/query-sdk/operators.js +45 -155
- package/dist/query-sdk/operators.js.map +1 -1
- package/dist/types.d.ts +159 -36
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +8 -8
- package/dist/types.js.map +1 -1
- package/dist/x402/types.d.ts +1 -1
- package/dist/x402/types.d.ts.map +1 -1
- package/dist/x402/utils.js +2 -2
- package/dist/x402/utils.js.map +1 -1
- package/jest.config.js +4 -0
- package/package.json +1 -1
- package/skills.md +0 -1
- package/src/batch.d.ts +3 -3
- package/src/batch.js +1 -1
- package/src/client.ts +287 -823
- package/src/database.d.ts +1 -1
- package/src/database.js +4 -4
- package/src/database.ts +71 -494
- package/src/index.d.ts +18 -18
- package/src/index.js +16 -16
- package/src/index.ts +44 -198
- package/src/query-sdk/ConditionBuilder.ts +37 -89
- package/src/query-sdk/NestedBuilders.ts +90 -92
- package/src/query-sdk/{OnChainDB.ts → OnDB.ts} +1 -1
- package/src/query-sdk/QueryBuilder.ts +59 -218
- package/src/query-sdk/QueryResult.ts +4 -330
- package/src/query-sdk/README.md +218 -587
- package/src/query-sdk/index.ts +2 -2
- package/src/query-sdk/operators.ts +91 -200
- package/src/query-sdk/tests/FieldConditionBuilder.test.ts +70 -71
- package/src/query-sdk/tests/LogicalOperator.test.ts +43 -82
- package/src/query-sdk/tests/NestedBuilders.test.ts +229 -309
- package/src/query-sdk/tests/QueryBuilder.test.ts +5 -5
- package/src/query-sdk/tests/QueryResult.test.ts +41 -435
- package/src/query-sdk/tests/comprehensive.test.ts +4 -185
- package/src/tests/client-requests.test.ts +280 -0
- package/src/tests/client-validation.test.ts +80 -0
- package/src/types.d.ts +6 -6
- package/src/types.js +8 -8
- package/src/types.ts +239 -54
- package/src/x402/types.ts +3 -3
- package/src/x402/utils.ts +3 -3
- package/examples/blob-upload-example.ts +0 -140
- package/src/batch.ts +0 -257
- package/src/query-sdk/dist/ConditionBuilder.d.ts +0 -22
- package/src/query-sdk/dist/ConditionBuilder.js +0 -90
- package/src/query-sdk/dist/FieldConditionBuilder.d.ts +0 -1
- package/src/query-sdk/dist/FieldConditionBuilder.js +0 -6
- package/src/query-sdk/dist/NestedBuilders.d.ts +0 -43
- package/src/query-sdk/dist/NestedBuilders.js +0 -144
- package/src/query-sdk/dist/QueryBuilder.d.ts +0 -70
- package/src/query-sdk/dist/QueryBuilder.js +0 -295
- package/src/query-sdk/dist/QueryResult.d.ts +0 -52
- package/src/query-sdk/dist/QueryResult.js +0 -293
- package/src/query-sdk/dist/SelectionBuilder.d.ts +0 -20
- package/src/query-sdk/dist/SelectionBuilder.js +0 -80
- package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +0 -27
- package/src/query-sdk/dist/adapters/HttpClientAdapter.js +0 -170
- package/src/query-sdk/dist/index.d.ts +0 -36
- package/src/query-sdk/dist/index.js +0 -27
- package/src/query-sdk/dist/operators.d.ts +0 -56
- package/src/query-sdk/dist/operators.js +0 -289
- package/src/query-sdk/dist/tests/setup.d.ts +0 -15
- package/src/query-sdk/dist/tests/setup.js +0 -46
- package/src/query-sdk/jest.config.js +0 -25
- package/src/query-sdk/package.json +0 -46
- package/src/query-sdk/tests/aggregations.test.ts +0 -653
- package/src/query-sdk/tests/integration.test.ts +0 -608
- package/src/query-sdk/tests/operators.test.ts +0 -327
- package/src/query-sdk/tests/unit.test.ts +0 -794
- package/src/query-sdk/tsconfig.json +0 -26
- package/src/query-sdk/yarn.lock +0 -3092
package/src/query-sdk/README.md
CHANGED
|
@@ -1,248 +1,156 @@
|
|
|
1
|
-
#
|
|
1
|
+
# OnDB Query SDK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Fluent API for building queries with server-side JOINs, logical operators, field conditions, and aggregations.
|
|
4
4
|
|
|
5
5
|
## Table of Contents
|
|
6
6
|
|
|
7
|
-
- [Quick Start](#quick-start)
|
|
8
7
|
- [QueryBuilder](#querybuilder)
|
|
9
|
-
- [Aggregations](#aggregations)
|
|
10
|
-
- [Server-Side JOINs](#server-side-joins)
|
|
11
|
-
- [Logical Operators](#logical-operators)
|
|
12
8
|
- [Field Operators](#field-operators)
|
|
9
|
+
- [Logical Operators](#logical-operators)
|
|
10
|
+
- [Server-Side JOINs](#server-side-joins)
|
|
11
|
+
- [Aggregations](#aggregations)
|
|
13
12
|
- [Selection](#selection)
|
|
14
|
-
- [QueryResult Utilities](#queryresult-utilities)
|
|
15
13
|
- [Nested Field Queries](#nested-field-queries)
|
|
16
|
-
- [
|
|
14
|
+
- [QueryResult](#queryresult)
|
|
17
15
|
- [HTTP Adapters](#http-adapters)
|
|
18
16
|
- [API Reference](#api-reference)
|
|
19
17
|
|
|
20
|
-
##
|
|
18
|
+
## QueryBuilder
|
|
21
19
|
|
|
22
20
|
```typescript
|
|
23
|
-
import { createClient } from '@onchaindb/sdk';
|
|
24
|
-
|
|
25
|
-
const db = createClient({
|
|
26
|
-
endpoint: 'http://localhost:9092',
|
|
27
|
-
appId: 'my_app'
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
// Simple query
|
|
31
21
|
const result = await db.queryBuilder()
|
|
32
|
-
.collection('users')
|
|
22
|
+
.collection('users') // Target collection
|
|
33
23
|
.whereField('status').equals('active')
|
|
34
|
-
.selectFields(['id', 'name'
|
|
35
|
-
.
|
|
24
|
+
.selectFields(['id', 'name'])
|
|
25
|
+
.orderBy('created_at', 'DESC')
|
|
26
|
+
.limit(50)
|
|
27
|
+
.offset(100)
|
|
36
28
|
.execute();
|
|
37
|
-
|
|
38
|
-
console.log(result.records);
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
## QueryBuilder
|
|
42
|
-
|
|
43
|
-
The QueryBuilder provides a fluent interface for constructing queries.
|
|
44
|
-
|
|
45
|
-
### Basic Methods
|
|
46
|
-
|
|
47
|
-
```typescript
|
|
48
|
-
const query = db.queryBuilder()
|
|
49
|
-
.collection('users') // Set target collection
|
|
50
|
-
.whereField('status').equals('active') // Add filter condition
|
|
51
|
-
.selectFields(['id', 'name']) // Select specific fields
|
|
52
|
-
.selectAll() // Or select all fields
|
|
53
|
-
.limit(50) // Limit results
|
|
54
|
-
.offset(100) // Skip results (pagination)
|
|
55
|
-
.orderBy('created_at') // Sort results
|
|
56
|
-
.includeHistory(true) // Include historical versions
|
|
57
|
-
.execute(); // Execute the query
|
|
58
29
|
```
|
|
59
30
|
|
|
60
31
|
### executeUnique()
|
|
61
32
|
|
|
62
|
-
Returns the latest record by
|
|
33
|
+
Returns the latest record by `updatedAt` / `createdAt` timestamp. Returns `null` if nothing matches.
|
|
63
34
|
|
|
64
35
|
```typescript
|
|
65
|
-
// Find the latest user record by email
|
|
66
36
|
const user = await db.queryBuilder()
|
|
67
37
|
.collection('users')
|
|
68
38
|
.whereField('email').equals('alice@example.com')
|
|
69
39
|
.executeUnique<User>();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### buildRawQuery / clone / isValid
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
// Inspect the raw query object
|
|
46
|
+
const raw = db.queryBuilder()
|
|
47
|
+
.collection('tweets')
|
|
48
|
+
.whereField('author').equals('alice')
|
|
49
|
+
.buildRawQuery();
|
|
50
|
+
|
|
51
|
+
// Clone a base query and diverge
|
|
52
|
+
const base = db.queryBuilder().collection('users').selectAll();
|
|
53
|
+
const active = base.clone().whereField('active').isTrue();
|
|
54
|
+
const inactive = base.clone().whereField('active').isFalse();
|
|
70
55
|
|
|
71
|
-
//
|
|
56
|
+
// Check if a query has conditions set
|
|
57
|
+
queryBuilder.isValid(); // false until whereField / find is called
|
|
72
58
|
```
|
|
73
59
|
|
|
74
|
-
|
|
60
|
+
## Field Operators
|
|
75
61
|
|
|
76
|
-
All operators available on `whereField()`:
|
|
62
|
+
All operators available on `whereField()` and `FieldConditionBuilder`:
|
|
77
63
|
|
|
78
64
|
```typescript
|
|
79
65
|
// Comparison
|
|
80
|
-
.equals(value)
|
|
81
|
-
.
|
|
82
|
-
.
|
|
83
|
-
.
|
|
84
|
-
.lessThan(value)
|
|
85
|
-
.lessThanOrEqual(value)
|
|
86
|
-
.between(min, max)
|
|
66
|
+
.equals(value) .notEquals(value)
|
|
67
|
+
.greaterThan(value) .greaterThanOrEqual(value)
|
|
68
|
+
.lessThan(value) .lessThanOrEqual(value)
|
|
69
|
+
.between(min, max) // betweenOp: { from, to }
|
|
87
70
|
|
|
88
71
|
// String
|
|
89
|
-
.contains(value)
|
|
90
|
-
.startsWith(value)
|
|
91
|
-
.endsWith(value)
|
|
72
|
+
.contains(value) // includes
|
|
73
|
+
.startsWith(value) .endsWith(value)
|
|
92
74
|
.regExpMatches(pattern)
|
|
93
75
|
.includesCaseInsensitive(value)
|
|
94
76
|
.startsWithCaseInsensitive(value)
|
|
95
77
|
.endsWithCaseInsensitive(value)
|
|
96
78
|
|
|
97
79
|
// Array
|
|
98
|
-
.in(values)
|
|
99
|
-
.notIn(values)
|
|
80
|
+
.in(values) .notIn(values)
|
|
100
81
|
|
|
101
82
|
// Existence
|
|
102
|
-
.exists()
|
|
103
|
-
.
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
.
|
|
107
|
-
.
|
|
108
|
-
|
|
109
|
-
// IP Address
|
|
110
|
-
.isLocalIp()
|
|
111
|
-
.isExternalIp()
|
|
112
|
-
.inCountry(countryCode)
|
|
113
|
-
.cidr(cidrRange)
|
|
114
|
-
|
|
115
|
-
// Special
|
|
116
|
-
.b64(value)
|
|
117
|
-
.inDataset(dataset)
|
|
83
|
+
.exists() .notExists() .isNull() .isNotNull()
|
|
84
|
+
.isTrue() .isFalse()
|
|
85
|
+
|
|
86
|
+
// Network / security
|
|
87
|
+
.isLocalIp() .isExternalIp()
|
|
88
|
+
.inCountry(countryCode) .cidr(cidrRange)
|
|
89
|
+
.b64(value) .inDataset(dataset)
|
|
118
90
|
```
|
|
119
91
|
|
|
120
|
-
###
|
|
92
|
+
### FieldConditionBuilder directly
|
|
121
93
|
|
|
122
94
|
```typescript
|
|
123
|
-
|
|
124
|
-
const rawQuery = db.queryBuilder()
|
|
125
|
-
.collection('tweets')
|
|
126
|
-
.whereField('author').equals('alice')
|
|
127
|
-
.selectAll()
|
|
128
|
-
.buildRawQuery();
|
|
95
|
+
import { FieldConditionBuilder } from '@ondb/sdk';
|
|
129
96
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
// Clone a query builder
|
|
133
|
-
const baseQuery = db.queryBuilder().collection('users').selectAll();
|
|
134
|
-
const activeUsers = baseQuery.clone().whereField('active').isTrue();
|
|
135
|
-
const inactiveUsers = baseQuery.clone().whereField('active').isFalse();
|
|
97
|
+
const condition = new FieldConditionBuilder('user.profile.bio').contains('Developer');
|
|
98
|
+
condition.toComposable(); // { user: { profile: { bio: { includes: 'Developer' } } } }
|
|
136
99
|
```
|
|
137
100
|
|
|
138
|
-
##
|
|
139
|
-
|
|
140
|
-
QueryBuilder provides built-in aggregation methods for counting, summing, averaging, and grouping data.
|
|
141
|
-
|
|
142
|
-
### Basic Aggregations
|
|
101
|
+
## Logical Operators
|
|
143
102
|
|
|
144
103
|
```typescript
|
|
145
|
-
|
|
146
|
-
const activeUsers = await db.queryBuilder()
|
|
147
|
-
.collection('users')
|
|
148
|
-
.whereField('active').equals(true)
|
|
149
|
-
.count();
|
|
150
|
-
// Returns: number
|
|
104
|
+
import { LogicalOperator } from '@ondb/sdk';
|
|
151
105
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
.collection('products')
|
|
169
|
-
.maxBy('price');
|
|
170
|
-
// Returns: T | null
|
|
171
|
-
|
|
172
|
-
// Find minimum value
|
|
173
|
-
const lowestPrice = await db.queryBuilder()
|
|
174
|
-
.collection('products')
|
|
175
|
-
.minBy('price');
|
|
176
|
-
// Returns: T | null
|
|
177
|
-
|
|
178
|
-
// Get distinct values
|
|
179
|
-
const categories = await db.queryBuilder()
|
|
180
|
-
.collection('products')
|
|
181
|
-
.distinctBy('category');
|
|
182
|
-
// Returns: string[]
|
|
183
|
-
|
|
184
|
-
// Count distinct values
|
|
185
|
-
const uniqueCategories = await db.queryBuilder()
|
|
186
|
-
.collection('products')
|
|
187
|
-
.countDistinct('category');
|
|
188
|
-
// Returns: number
|
|
106
|
+
const result = await db.queryBuilder()
|
|
107
|
+
.collection('posts')
|
|
108
|
+
.find(builder =>
|
|
109
|
+
LogicalOperator.And([
|
|
110
|
+
builder.field('published').isTrue(),
|
|
111
|
+
LogicalOperator.Or([
|
|
112
|
+
builder.field('category').equals('tech'),
|
|
113
|
+
builder.field('views').greaterThan(1000)
|
|
114
|
+
]),
|
|
115
|
+
LogicalOperator.Not([
|
|
116
|
+
builder.field('archived').isTrue()
|
|
117
|
+
])
|
|
118
|
+
])
|
|
119
|
+
)
|
|
120
|
+
.selectAll()
|
|
121
|
+
.execute();
|
|
189
122
|
```
|
|
190
123
|
|
|
191
|
-
|
|
124
|
+
`builder.field(name)` returns a `FieldConditionBuilder`. Calling any operator on it (`.equals()`, `.greaterThan()`, etc.) returns a `LogicalOperator` directly — no extra wrapping needed.
|
|
192
125
|
|
|
193
|
-
|
|
126
|
+
### Instance chain methods
|
|
194
127
|
|
|
195
128
|
```typescript
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
.groupBy('country')
|
|
200
|
-
.count();
|
|
201
|
-
// Returns: { "USA": 150, "UK": 75, "Germany": 50 }
|
|
202
|
-
|
|
203
|
-
// Sum order amounts by category
|
|
204
|
-
const salesByCategory = await db.queryBuilder()
|
|
205
|
-
.collection('orders')
|
|
206
|
-
.whereField('status').equals('completed')
|
|
207
|
-
.groupBy('category')
|
|
208
|
-
.sumBy('amount');
|
|
209
|
-
// Returns: { "electronics": 50000, "clothing": 25000 }
|
|
210
|
-
|
|
211
|
-
// Average rating by product
|
|
212
|
-
const avgRatingByProduct = await db.queryBuilder()
|
|
213
|
-
.collection('reviews')
|
|
214
|
-
.groupBy('productId')
|
|
215
|
-
.avgBy('rating');
|
|
216
|
-
// Returns: { "prod_1": 4.5, "prod_2": 3.8 }
|
|
217
|
-
|
|
218
|
-
// Max/Min by group
|
|
219
|
-
const maxPriceByCategory = await db.queryBuilder()
|
|
220
|
-
.collection('products')
|
|
221
|
-
.groupBy('category')
|
|
222
|
-
.maxBy('price');
|
|
223
|
-
// Returns: { "electronics": 999, "books": 49 }
|
|
129
|
+
const condition = new FieldConditionBuilder('age').greaterThan(18)
|
|
130
|
+
.and(new FieldConditionBuilder('active').isTrue());
|
|
131
|
+
// type: 'and', conditions: [age > 18, active = true]
|
|
224
132
|
```
|
|
225
133
|
|
|
226
|
-
###
|
|
227
|
-
|
|
228
|
-
GroupBy supports nested field paths:
|
|
134
|
+
### ConditionBuilder (inside find())
|
|
229
135
|
|
|
230
136
|
```typescript
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
.
|
|
234
|
-
|
|
235
|
-
.
|
|
236
|
-
|
|
137
|
+
const result = await db.queryBuilder()
|
|
138
|
+
.collection('users')
|
|
139
|
+
.find(builder => {
|
|
140
|
+
const isAdult = builder.field('age').greaterThan(18);
|
|
141
|
+
const isActive = builder.field('status').equals('active');
|
|
142
|
+
return builder.and(isAdult, isActive);
|
|
143
|
+
})
|
|
144
|
+
.execute();
|
|
237
145
|
```
|
|
238
146
|
|
|
239
147
|
## Server-Side JOINs
|
|
240
148
|
|
|
241
|
-
|
|
149
|
+
JOINs run on the backend in a single request. Use `$data.fieldname` to reference fields from the parent record.
|
|
242
150
|
|
|
243
|
-
### joinOne (
|
|
151
|
+
### joinOne (one-to-one)
|
|
244
152
|
|
|
245
|
-
Returns a single object or null
|
|
153
|
+
Returns a single object or `null`.
|
|
246
154
|
|
|
247
155
|
```typescript
|
|
248
156
|
const result = await db.queryBuilder()
|
|
@@ -254,12 +162,12 @@ const result = await db.queryBuilder()
|
|
|
254
162
|
.selectAll()
|
|
255
163
|
.execute();
|
|
256
164
|
|
|
257
|
-
//
|
|
165
|
+
// record.author_info => { display_name, avatar_url, verified } | null
|
|
258
166
|
```
|
|
259
167
|
|
|
260
|
-
### joinMany (
|
|
168
|
+
### joinMany (one-to-many)
|
|
261
169
|
|
|
262
|
-
Returns an array
|
|
170
|
+
Returns an array.
|
|
263
171
|
|
|
264
172
|
```typescript
|
|
265
173
|
const result = await db.queryBuilder()
|
|
@@ -271,22 +179,7 @@ const result = await db.queryBuilder()
|
|
|
271
179
|
.selectAll()
|
|
272
180
|
.execute();
|
|
273
181
|
|
|
274
|
-
//
|
|
275
|
-
```
|
|
276
|
-
|
|
277
|
-
### joinWith (Default Behavior)
|
|
278
|
-
|
|
279
|
-
Returns an array (same as joinMany but without explicit many flag).
|
|
280
|
-
|
|
281
|
-
```typescript
|
|
282
|
-
const result = await db.queryBuilder()
|
|
283
|
-
.collection('posts')
|
|
284
|
-
.joinWith('comments', 'comments')
|
|
285
|
-
.onField('post_id').equals('$data.id')
|
|
286
|
-
.selectAll()
|
|
287
|
-
.build()
|
|
288
|
-
.selectAll()
|
|
289
|
-
.execute();
|
|
182
|
+
// record.tweets => [{ id, content, created_at }, ...]
|
|
290
183
|
```
|
|
291
184
|
|
|
292
185
|
### Multiple JOINs
|
|
@@ -295,17 +188,14 @@ const result = await db.queryBuilder()
|
|
|
295
188
|
const result = await db.queryBuilder()
|
|
296
189
|
.collection('tweets')
|
|
297
190
|
.whereField('reply_to_id').isNull()
|
|
298
|
-
// Author profile (one-to-one)
|
|
299
191
|
.joinOne('author_info', 'users')
|
|
300
192
|
.onField('address').equals('$data.author')
|
|
301
193
|
.selectFields(['display_name', 'avatar_url', 'verified'])
|
|
302
194
|
.build()
|
|
303
|
-
// All likes (one-to-many)
|
|
304
195
|
.joinMany('likes', 'likes')
|
|
305
196
|
.onField('tweet_id').equals('$data.id')
|
|
306
197
|
.selectFields(['user', 'created_at'])
|
|
307
198
|
.build()
|
|
308
|
-
// All replies (one-to-many)
|
|
309
199
|
.joinMany('replies', 'tweets')
|
|
310
200
|
.onField('reply_to_id').equals('$data.id')
|
|
311
201
|
.selectFields(['id', 'author', 'content'])
|
|
@@ -317,18 +207,15 @@ const result = await db.queryBuilder()
|
|
|
317
207
|
|
|
318
208
|
### Nested JOINs
|
|
319
209
|
|
|
320
|
-
Chain JOINs before
|
|
210
|
+
Chain JOINs before `.build()` to create nested relationships:
|
|
321
211
|
|
|
322
212
|
```typescript
|
|
323
213
|
const result = await db.queryBuilder()
|
|
324
214
|
.collection('tweets')
|
|
325
|
-
.whereField('id').equals(tweetId)
|
|
326
|
-
// Get replies with their authors
|
|
327
215
|
.joinMany('replies', 'tweets')
|
|
328
216
|
.onField('reply_to_id').equals('$data.id')
|
|
329
217
|
.selectAll()
|
|
330
|
-
// Nested:
|
|
331
|
-
.joinOne('author_info', 'users')
|
|
218
|
+
.joinOne('author_info', 'users') // Nested: author for each reply
|
|
332
219
|
.onField('address').equals('$data.author')
|
|
333
220
|
.selectFields(['display_name', 'avatar_url'])
|
|
334
221
|
.build()
|
|
@@ -337,272 +224,114 @@ const result = await db.queryBuilder()
|
|
|
337
224
|
.execute();
|
|
338
225
|
```
|
|
339
226
|
|
|
340
|
-
###
|
|
227
|
+
### JoinBuilder Operators (on `onField()`)
|
|
341
228
|
|
|
342
229
|
```typescript
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
.whereField('quote_tweet_id').isNotNull()
|
|
347
|
-
.joinOne('quote_tweet', 'tweets')
|
|
348
|
-
.onField('id').equals('$data.quote_tweet_id')
|
|
349
|
-
.selectFields(['id', 'content', 'author', 'created_at'])
|
|
350
|
-
.joinOne('author_info', 'users')
|
|
351
|
-
.onField('address').equals('$data.author')
|
|
352
|
-
.selectFields(['display_name', 'avatar_url'])
|
|
353
|
-
.build()
|
|
354
|
-
.build()
|
|
355
|
-
.selectAll()
|
|
356
|
-
.execute();
|
|
230
|
+
.equals(value) .in(values)
|
|
231
|
+
.greaterThan(v) .lessThan(v)
|
|
232
|
+
.isNull() .isNotNull()
|
|
357
233
|
```
|
|
358
234
|
|
|
359
|
-
|
|
235
|
+
## Aggregations
|
|
360
236
|
|
|
361
|
-
|
|
237
|
+
All aggregations execute server-side.
|
|
362
238
|
|
|
363
|
-
|
|
364
|
-
.equals(value)
|
|
365
|
-
.in(values)
|
|
366
|
-
.greaterThan(value)
|
|
367
|
-
.lessThan(value)
|
|
368
|
-
.isNull()
|
|
369
|
-
.isNotNull()
|
|
370
|
-
```
|
|
239
|
+
### Basic Aggregations
|
|
371
240
|
|
|
372
|
-
|
|
241
|
+
```typescript
|
|
242
|
+
// Count
|
|
243
|
+
const n = await db.queryBuilder()
|
|
244
|
+
.collection('users')
|
|
245
|
+
.whereField('active').isTrue()
|
|
246
|
+
.count();
|
|
373
247
|
|
|
374
|
-
|
|
248
|
+
// Sum / average / max / min
|
|
249
|
+
const total = await db.queryBuilder().collection('orders').sumBy('amount');
|
|
250
|
+
const avg = await db.queryBuilder().collection('orders').avgBy('amount');
|
|
251
|
+
const max = await db.queryBuilder().collection('products').maxBy('price');
|
|
252
|
+
const min = await db.queryBuilder().collection('products').minBy('price');
|
|
375
253
|
|
|
376
|
-
|
|
377
|
-
|
|
254
|
+
// Distinct values
|
|
255
|
+
const categories = await db.queryBuilder().collection('products').distinctBy('category');
|
|
378
256
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
.find(builder =>
|
|
382
|
-
LogicalOperator.And([
|
|
383
|
-
LogicalOperator.Condition(builder.field('published').equals(true)),
|
|
384
|
-
LogicalOperator.Or([
|
|
385
|
-
LogicalOperator.Condition(builder.field('category').equals('tech')),
|
|
386
|
-
LogicalOperator.Condition(builder.field('views').greaterThan(1000))
|
|
387
|
-
]),
|
|
388
|
-
LogicalOperator.Not([
|
|
389
|
-
LogicalOperator.Condition(builder.field('archived').equals(true))
|
|
390
|
-
])
|
|
391
|
-
])
|
|
392
|
-
)
|
|
393
|
-
.selectAll()
|
|
394
|
-
.execute();
|
|
257
|
+
// Count distinct
|
|
258
|
+
const n = await db.queryBuilder().collection('products').countDistinct('category');
|
|
395
259
|
```
|
|
396
260
|
|
|
397
|
-
###
|
|
261
|
+
### Grouped Aggregations
|
|
262
|
+
|
|
263
|
+
`groupBy(field)` returns a `GroupByQueryBuilder` with the same aggregate methods:
|
|
398
264
|
|
|
399
265
|
```typescript
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
builder.field('age').greaterThan(18)
|
|
407
|
-
|
|
408
|
-
// Group conditions
|
|
409
|
-
builder.andGroup(() => [...conditions])
|
|
410
|
-
builder.orGroup(() => [...conditions])
|
|
411
|
-
builder.notGroup(() => [...conditions])
|
|
412
|
-
|
|
413
|
-
// Nested field conditions (ORM-like)
|
|
414
|
-
builder.nested('user', nested =>
|
|
415
|
-
nested.andGroup(() => [
|
|
416
|
-
LogicalOperator.Condition(nested.field('profile').field('name').equals('John')),
|
|
417
|
-
LogicalOperator.Condition(nested.field('settings').field('theme').equals('dark'))
|
|
418
|
-
])
|
|
419
|
-
)
|
|
420
|
-
```
|
|
266
|
+
// Count per group
|
|
267
|
+
const byCountry = await db.queryBuilder()
|
|
268
|
+
.collection('users')
|
|
269
|
+
.groupBy('country')
|
|
270
|
+
.count();
|
|
271
|
+
// { "USA": 150, "UK": 75, "Germany": 50 }
|
|
421
272
|
|
|
422
|
-
|
|
273
|
+
// Sum per group
|
|
274
|
+
const salesByCategory = await db.queryBuilder()
|
|
275
|
+
.collection('orders')
|
|
276
|
+
.whereField('status').equals('completed')
|
|
277
|
+
.groupBy('category')
|
|
278
|
+
.sumBy('amount');
|
|
279
|
+
// { "electronics": 50000, "clothing": 25000 }
|
|
423
280
|
|
|
424
|
-
|
|
281
|
+
// Supports nested field paths
|
|
282
|
+
const byRegion = await db.queryBuilder()
|
|
283
|
+
.collection('orders')
|
|
284
|
+
.groupBy('customer.address.region')
|
|
285
|
+
.sumBy('total');
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## Selection
|
|
425
289
|
|
|
426
|
-
|
|
290
|
+
### selectFields / selectAll
|
|
427
291
|
|
|
428
292
|
```typescript
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
const field = new FieldConditionBuilder('fieldName');
|
|
432
|
-
|
|
433
|
-
// Base operators
|
|
434
|
-
field.equals(value) // is
|
|
435
|
-
field.notEquals(value) // isNot
|
|
436
|
-
field.in(values) // in
|
|
437
|
-
field.notIn(values) // notIn
|
|
438
|
-
field.isNull() // isNull: true
|
|
439
|
-
field.isNotNull() // isNull: false
|
|
440
|
-
field.exists() // exists: true
|
|
441
|
-
field.notExists() // exists: false
|
|
442
|
-
|
|
443
|
-
// String operators
|
|
444
|
-
field.startsWith(value)
|
|
445
|
-
field.endsWith(value)
|
|
446
|
-
field.contains(value) // includes
|
|
447
|
-
field.regExpMatches(pattern)
|
|
448
|
-
field.includesCaseInsensitive(value)
|
|
449
|
-
field.startsWithCaseInsensitive(value)
|
|
450
|
-
field.endsWithCaseInsensitive(value)
|
|
451
|
-
|
|
452
|
-
// Number operators
|
|
453
|
-
field.greaterThan(value)
|
|
454
|
-
field.lessThan(value)
|
|
455
|
-
field.greaterThanOrEqual(value)
|
|
456
|
-
field.lessThanOrEqual(value)
|
|
457
|
-
field.between(min, max) // betweenOp: { from, to }
|
|
458
|
-
|
|
459
|
-
// IP operators
|
|
460
|
-
field.isLocalIp()
|
|
461
|
-
field.isExternalIp()
|
|
462
|
-
field.inCountry(countryCode)
|
|
463
|
-
field.cidr(cidrRange)
|
|
464
|
-
|
|
465
|
-
// Misc operators
|
|
466
|
-
field.b64(value) // Base64 comparison
|
|
467
|
-
field.inDataset(dataset) // Dataset membership
|
|
468
|
-
|
|
469
|
-
// Convenience
|
|
470
|
-
field.isTrue() // equals(true)
|
|
471
|
-
field.isFalse() // equals(false)
|
|
293
|
+
db.queryBuilder().collection('users').selectFields(['id', 'name', 'email']).execute();
|
|
294
|
+
db.queryBuilder().collection('users').selectAll().execute();
|
|
472
295
|
```
|
|
473
296
|
|
|
474
|
-
## Selection
|
|
475
|
-
|
|
476
297
|
### SelectionBuilder
|
|
477
298
|
|
|
478
299
|
```typescript
|
|
479
|
-
import { SelectionBuilder } from '@
|
|
300
|
+
import { SelectionBuilder } from '@ondb/sdk';
|
|
480
301
|
|
|
481
|
-
//
|
|
482
|
-
const
|
|
483
|
-
.field('id')
|
|
484
|
-
.field('name')
|
|
485
|
-
.field('email')
|
|
486
|
-
.build();
|
|
302
|
+
// Field-by-field
|
|
303
|
+
const sel = new SelectionBuilder().field('id').field('name').build();
|
|
487
304
|
|
|
488
|
-
//
|
|
489
|
-
new SelectionBuilder()
|
|
490
|
-
.fields(['id', 'name', 'email', 'created_at'])
|
|
491
|
-
.build();
|
|
305
|
+
// Multiple at once
|
|
306
|
+
new SelectionBuilder().fields(['id', 'name', 'email']).build();
|
|
492
307
|
|
|
493
|
-
// Nested
|
|
308
|
+
// Nested selection
|
|
494
309
|
new SelectionBuilder()
|
|
495
310
|
.field('id')
|
|
496
|
-
.nested('profile',
|
|
311
|
+
.nested('profile', n => n.field('bio').field('avatar'))
|
|
497
312
|
.build();
|
|
498
313
|
|
|
499
314
|
// Exclude fields
|
|
500
|
-
new SelectionBuilder()
|
|
501
|
-
.excludeFields(['password', 'secret_key'])
|
|
502
|
-
.build();
|
|
315
|
+
new SelectionBuilder().excludeFields(['password', 'secret']).build();
|
|
503
316
|
|
|
504
|
-
// Select all
|
|
505
|
-
SelectionBuilder.all();
|
|
317
|
+
// Select all
|
|
318
|
+
SelectionBuilder.all(); // {}
|
|
506
319
|
```
|
|
507
320
|
|
|
508
|
-
###
|
|
321
|
+
### select() with builder
|
|
509
322
|
|
|
510
323
|
```typescript
|
|
511
|
-
// Method 1: selectFields()
|
|
512
|
-
db.queryBuilder()
|
|
513
|
-
.collection('users')
|
|
514
|
-
.selectFields(['id', 'name', 'email'])
|
|
515
|
-
.execute();
|
|
516
|
-
|
|
517
|
-
// Method 2: selectAll()
|
|
518
|
-
db.queryBuilder()
|
|
519
|
-
.collection('users')
|
|
520
|
-
.selectAll()
|
|
521
|
-
.execute();
|
|
522
|
-
|
|
523
|
-
// Method 3: select() with builder
|
|
524
324
|
db.queryBuilder()
|
|
525
325
|
.collection('users')
|
|
526
326
|
.select(s => s.field('id').field('name').nested('profile', n => n.field('bio')))
|
|
527
327
|
.execute();
|
|
528
328
|
```
|
|
529
329
|
|
|
530
|
-
## QueryResult Utilities
|
|
531
|
-
|
|
532
|
-
Process query results with built-in utilities:
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
import { createQueryResult, QueryResult } from '@onchaindb/sdk';
|
|
536
|
-
|
|
537
|
-
const result = createQueryResult(response.records);
|
|
538
|
-
|
|
539
|
-
// Basic utilities
|
|
540
|
-
result.len() // Count of records
|
|
541
|
-
result.isEmpty() // Check if empty
|
|
542
|
-
result.first() // First record or undefined
|
|
543
|
-
result.last() // Last record or undefined
|
|
544
|
-
result.get(5) // Get record at index
|
|
545
|
-
|
|
546
|
-
// Iteration
|
|
547
|
-
result.any(r => r.active) // Any match predicate?
|
|
548
|
-
result.all(r => r.verified) // All match predicate?
|
|
549
|
-
result.find(r => r.id === '123') // Find first matching
|
|
550
|
-
result.filter(r => r.status === 'active')
|
|
551
|
-
result.map(r => r.name)
|
|
552
|
-
result.forEach((r, i) => console.log(i, r))
|
|
553
|
-
result.reduce((acc, r) => acc + r.amount, 0)
|
|
554
|
-
|
|
555
|
-
// Aggregation
|
|
556
|
-
result.groupBy('department') // { 'sales': [...], 'engineering': [...] }
|
|
557
|
-
result.pluck('name') // ['Alice', 'Bob', ...]
|
|
558
|
-
result.pluckStrings('name') // Type-safe string pluck
|
|
559
|
-
result.pluckNumbers('age') // Type-safe number pluck
|
|
560
|
-
result.countBy('status') // { 'active': 10, 'inactive': 3 }
|
|
561
|
-
|
|
562
|
-
// Numeric summary
|
|
563
|
-
const stats = result.summarizeNumeric('salary');
|
|
564
|
-
// { count, sum, mean, median, min, max, standardDeviation, variance }
|
|
565
|
-
|
|
566
|
-
// Sorting
|
|
567
|
-
result.sortBy('name') // Ascending
|
|
568
|
-
result.sortBy('created_at', false) // Descending
|
|
569
|
-
result.sortByMultiple([
|
|
570
|
-
{ field: 'department', ascending: true },
|
|
571
|
-
{ field: 'salary', ascending: false }
|
|
572
|
-
])
|
|
573
|
-
|
|
574
|
-
// Pagination
|
|
575
|
-
result.paginate(2, 20) // Page 2, 20 items per page
|
|
576
|
-
result.chunk(10) // Split into chunks of 10
|
|
577
|
-
|
|
578
|
-
// Distinct
|
|
579
|
-
result.distinctBy('email') // Unique by field
|
|
580
|
-
result.unique() // Unique records
|
|
581
|
-
|
|
582
|
-
// Joining (client-side)
|
|
583
|
-
const departments = createQueryResult(deptData);
|
|
584
|
-
result.innerJoin(departments, 'dept_id', 'id')
|
|
585
|
-
result.leftJoin(departments, 'dept_id', 'id')
|
|
586
|
-
|
|
587
|
-
// Export
|
|
588
|
-
result.toCsv() // CSV string
|
|
589
|
-
result.toCsv(';') // Custom delimiter
|
|
590
|
-
result.toJSON() // JSON string
|
|
591
|
-
result.toArray() // Plain array
|
|
592
|
-
|
|
593
|
-
// Debugging
|
|
594
|
-
result.inspect() // Log to console
|
|
595
|
-
result.inspect(5) // Log first 5 records
|
|
596
|
-
```
|
|
597
|
-
|
|
598
330
|
## Nested Field Queries
|
|
599
331
|
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
### Dot Notation
|
|
332
|
+
### Dot notation
|
|
603
333
|
|
|
604
334
|
```typescript
|
|
605
|
-
// Query nested fields with dot notation
|
|
606
335
|
const result = await db.queryBuilder()
|
|
607
336
|
.collection('users')
|
|
608
337
|
.whereField('profile.settings.theme').equals('dark')
|
|
@@ -610,99 +339,68 @@ const result = await db.queryBuilder()
|
|
|
610
339
|
.execute();
|
|
611
340
|
```
|
|
612
341
|
|
|
613
|
-
###
|
|
342
|
+
### NestedConditionBuilder
|
|
614
343
|
|
|
615
344
|
```typescript
|
|
616
|
-
import { NestedConditionBuilder
|
|
345
|
+
import { NestedConditionBuilder } from '@ondb/sdk';
|
|
617
346
|
|
|
618
347
|
const result = await db.queryBuilder()
|
|
619
348
|
.collection('users')
|
|
620
349
|
.find(builder =>
|
|
621
350
|
builder.nested('user', nested =>
|
|
622
|
-
nested.andGroup(
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
),
|
|
626
|
-
LogicalOperator.Condition(
|
|
627
|
-
nested.field('settings').field('notifications').isTrue()
|
|
628
|
-
)
|
|
351
|
+
nested.andGroup(b => [
|
|
352
|
+
b.field('name').equals('John'),
|
|
353
|
+
b.field('status').equals('active')
|
|
629
354
|
])
|
|
630
355
|
)
|
|
631
356
|
)
|
|
632
357
|
.execute();
|
|
633
358
|
```
|
|
634
359
|
|
|
635
|
-
##
|
|
360
|
+
## QueryResult
|
|
636
361
|
|
|
637
|
-
|
|
362
|
+
A lightweight wrapper around query response data for convenient access. Aggregation, sorting, filtering, and joining are handled server-side via `QueryBuilder`.
|
|
638
363
|
|
|
639
364
|
```typescript
|
|
640
|
-
import {
|
|
641
|
-
|
|
642
|
-
// Configure globally
|
|
643
|
-
OnChainDB.configure(new FetchHttpClient(), 'http://localhost:9092', 'api_key');
|
|
644
|
-
|
|
645
|
-
// Build queries
|
|
646
|
-
const query = OnChainDB.query()
|
|
647
|
-
.collection('users')
|
|
648
|
-
.selectAll();
|
|
649
|
-
|
|
650
|
-
// Execute raw queries
|
|
651
|
-
const result = await OnChainDB.rawQuery('{"find": {}, "select": {}}');
|
|
365
|
+
import { createQueryResult } from '@ondb/sdk';
|
|
652
366
|
|
|
653
|
-
|
|
654
|
-
const result = await OnChainDB.queryFromJson({ find: {}, select: {} });
|
|
655
|
-
|
|
656
|
-
// Health check
|
|
657
|
-
const isHealthy = await OnChainDB.healthCheck();
|
|
658
|
-
|
|
659
|
-
// Create indexes
|
|
660
|
-
await OnChainDB.createIndex('my_app', 'users', 'email');
|
|
661
|
-
await OnChainDB.createUniqueIndex('my_app', 'users', 'id');
|
|
367
|
+
const result = createQueryResult(response.records);
|
|
662
368
|
|
|
663
|
-
//
|
|
664
|
-
|
|
369
|
+
result.len() // Total record count
|
|
370
|
+
result.length() // Alias for len()
|
|
371
|
+
result.isEmpty() // true if no records
|
|
372
|
+
result.first() // First record or undefined
|
|
373
|
+
result.last() // Last record or undefined
|
|
374
|
+
result.get(2) // Record at index or undefined
|
|
375
|
+
result.toArray() // Plain array copy
|
|
376
|
+
result.toJSON() // JSON string
|
|
377
|
+
result.cast<User>() // Re-type without copying
|
|
665
378
|
```
|
|
666
379
|
|
|
667
380
|
## HTTP Adapters
|
|
668
381
|
|
|
669
|
-
### Built-in Adapters
|
|
670
|
-
|
|
671
382
|
```typescript
|
|
672
383
|
import {
|
|
673
|
-
FetchHttpClient,
|
|
674
|
-
AxiosHttpClient,
|
|
675
|
-
NodeHttpClient,
|
|
676
|
-
createHttpClient
|
|
677
|
-
} from '@
|
|
678
|
-
|
|
679
|
-
// Browser (Fetch API)
|
|
680
|
-
const fetchClient = new FetchHttpClient();
|
|
681
|
-
|
|
682
|
-
// Axios (browser or Node.js)
|
|
683
|
-
const axiosClient = new AxiosHttpClient();
|
|
684
|
-
|
|
685
|
-
// Node.js native HTTP
|
|
686
|
-
const nodeClient = new NodeHttpClient();
|
|
687
|
-
|
|
688
|
-
// Auto-detect best client for environment
|
|
689
|
-
const client = createHttpClient();
|
|
384
|
+
FetchHttpClient, // Browser Fetch API
|
|
385
|
+
AxiosHttpClient, // Axios (browser or Node.js)
|
|
386
|
+
NodeHttpClient, // Node.js native HTTP
|
|
387
|
+
createHttpClient // Auto-detect best for environment
|
|
388
|
+
} from '@ondb/sdk';
|
|
690
389
|
```
|
|
691
390
|
|
|
692
391
|
### Custom HTTP Client
|
|
693
392
|
|
|
694
393
|
```typescript
|
|
695
|
-
import { HttpClient } from '@
|
|
394
|
+
import { HttpClient } from '@ondb/sdk';
|
|
696
395
|
|
|
697
|
-
class
|
|
396
|
+
class MyHttpClient implements HttpClient {
|
|
698
397
|
async post(url: string, data: any, headers?: Record<string, string>): Promise<any> {
|
|
699
|
-
|
|
700
|
-
const response = await fetch(url, {
|
|
398
|
+
const res = await fetch(url, {
|
|
701
399
|
method: 'POST',
|
|
702
400
|
headers: { 'Content-Type': 'application/json', ...headers },
|
|
703
401
|
body: JSON.stringify(data)
|
|
704
402
|
});
|
|
705
|
-
return
|
|
403
|
+
return res.json();
|
|
706
404
|
}
|
|
707
405
|
}
|
|
708
406
|
```
|
|
@@ -714,68 +412,61 @@ class CustomHttpClient implements HttpClient {
|
|
|
714
412
|
| Method | Description |
|
|
715
413
|
|--------|-------------|
|
|
716
414
|
| `collection(name)` | Set target collection |
|
|
717
|
-
| `whereField(name)` | Start a field condition |
|
|
415
|
+
| `whereField(name)` | Start a field condition (returns `WhereClause`) |
|
|
718
416
|
| `find(builderFn)` | Set complex logical conditions |
|
|
719
|
-
| `select(builderFn)` | Configure selection with
|
|
417
|
+
| `select(builderFn)` | Configure selection with `SelectionBuilder` |
|
|
720
418
|
| `selectFields(fields)` | Select specific fields |
|
|
721
419
|
| `selectAll()` | Select all fields |
|
|
722
|
-
| `joinOne(alias, model)` | One-to-one JOIN
|
|
723
|
-
| `joinMany(alias, model)` | One-to-many JOIN
|
|
724
|
-
| `joinWith(alias, model)` | JOIN with default behavior |
|
|
725
|
-
| `
|
|
420
|
+
| `joinOne(alias, model)` | One-to-one server JOIN |
|
|
421
|
+
| `joinMany(alias, model)` | One-to-many server JOIN |
|
|
422
|
+
| `joinWith(alias, model)` | Server JOIN with default array behavior |
|
|
423
|
+
| `orderBy(field, dir?)` | Sort (`'ASC'` or `'DESC'`) |
|
|
726
424
|
| `limit(n)` | Limit result count |
|
|
727
|
-
| `offset(n)` | Skip results
|
|
728
|
-
| `
|
|
729
|
-
| `
|
|
730
|
-
| `
|
|
731
|
-
| `execute()` | Execute query |
|
|
732
|
-
| `executeUnique()` | Execute and return latest record by metadata |
|
|
733
|
-
| `executeWithPayment(quoteId, txHash, network?)` | Execute with x402 payment |
|
|
425
|
+
| `offset(n)` | Skip results |
|
|
426
|
+
| `includeHistory(bool)` | Include all historical versions |
|
|
427
|
+
| `execute()` | Execute and return records |
|
|
428
|
+
| `executeUnique()` | Return latest single record or `null` |
|
|
734
429
|
| `count()` | Count matching records |
|
|
735
430
|
| `sumBy(field)` | Sum numeric field values |
|
|
736
|
-
| `avgBy(field)` |
|
|
737
|
-
| `maxBy(field)` |
|
|
738
|
-
| `minBy(field)` |
|
|
739
|
-
| `distinctBy(field)` |
|
|
431
|
+
| `avgBy(field)` | Average numeric field values |
|
|
432
|
+
| `maxBy(field)` | Maximum field value |
|
|
433
|
+
| `minBy(field)` | Minimum field value |
|
|
434
|
+
| `distinctBy(field)` | Distinct field values |
|
|
740
435
|
| `countDistinct(field)` | Count distinct values |
|
|
741
436
|
| `groupBy(field)` | Start grouped aggregation |
|
|
742
|
-
| `buildRawQuery()` |
|
|
437
|
+
| `buildRawQuery()` | Inspect raw query object |
|
|
743
438
|
| `clone()` | Clone the builder |
|
|
744
|
-
| `isValid()` |
|
|
439
|
+
| `isValid()` | `true` if query has conditions |
|
|
745
440
|
|
|
746
441
|
### JoinBuilder
|
|
747
442
|
|
|
748
443
|
| Method | Description |
|
|
749
444
|
|--------|-------------|
|
|
750
445
|
| `onField(name)` | Start JOIN condition |
|
|
751
|
-
| `
|
|
752
|
-
| `
|
|
753
|
-
| `
|
|
754
|
-
| `selecting(builderFn)` | Configure selection with builder |
|
|
446
|
+
| `selectFields(fields)` | Select from joined collection |
|
|
447
|
+
| `selectAll()` | Select all from joined collection |
|
|
448
|
+
| `selecting(builderFn)` | Selection via `SelectionBuilder` |
|
|
755
449
|
| `joinOne(alias, model)` | Nested one-to-one JOIN |
|
|
756
450
|
| `joinMany(alias, model)` | Nested one-to-many JOIN |
|
|
757
|
-
| `build()` | Complete JOIN
|
|
451
|
+
| `build()` | Complete JOIN, return to parent |
|
|
758
452
|
|
|
759
453
|
### GroupByQueryBuilder
|
|
760
454
|
|
|
761
|
-
Returned by `queryBuilder.groupBy(field)`:
|
|
762
|
-
|
|
763
455
|
| Method | Description |
|
|
764
456
|
|--------|-------------|
|
|
765
|
-
| `count()` | Count
|
|
766
|
-
| `sumBy(field)` | Sum
|
|
767
|
-
| `avgBy(field)` | Average
|
|
768
|
-
| `maxBy(field)` | Max
|
|
769
|
-
| `minBy(field)` | Min
|
|
457
|
+
| `count()` | Count per group |
|
|
458
|
+
| `sumBy(field)` | Sum per group |
|
|
459
|
+
| `avgBy(field)` | Average per group |
|
|
460
|
+
| `maxBy(field)` | Max per group |
|
|
461
|
+
| `minBy(field)` | Min per group |
|
|
770
462
|
|
|
771
|
-
### LogicalOperator
|
|
463
|
+
### LogicalOperator (static)
|
|
772
464
|
|
|
773
|
-
|
|
|
774
|
-
|
|
775
|
-
| `And(conditions)` |
|
|
776
|
-
| `Or(conditions)` |
|
|
777
|
-
| `Not(conditions)` |
|
|
778
|
-
| `Condition(cond)` | Wrap a field condition |
|
|
465
|
+
| Method | Description |
|
|
466
|
+
|--------|-------------|
|
|
467
|
+
| `And(conditions)` | AND of conditions |
|
|
468
|
+
| `Or(conditions)` | OR of conditions |
|
|
469
|
+
| `Not(conditions)` | NOT of conditions |
|
|
779
470
|
|
|
780
471
|
### SelectionBuilder
|
|
781
472
|
|
|
@@ -783,83 +474,23 @@ Returned by `queryBuilder.groupBy(field)`:
|
|
|
783
474
|
|--------|-------------|
|
|
784
475
|
| `field(name)` | Include a field |
|
|
785
476
|
| `fields(names)` | Include multiple fields |
|
|
786
|
-
| `nested(name,
|
|
477
|
+
| `nested(name, fn)` | Nested field selection |
|
|
787
478
|
| `exclude(name)` | Exclude a field |
|
|
788
479
|
| `excludeFields(names)` | Exclude multiple fields |
|
|
789
480
|
| `build()` | Get selection map |
|
|
790
|
-
| `
|
|
791
|
-
| `static all()` | Select all fields |
|
|
481
|
+
| `static all()` | Select all fields (`{}`) |
|
|
792
482
|
|
|
793
483
|
### QueryResult
|
|
794
484
|
|
|
795
485
|
| Method | Description |
|
|
796
486
|
|--------|-------------|
|
|
797
487
|
| `len()` / `length()` | Record count |
|
|
798
|
-
| `isEmpty()` |
|
|
799
|
-
| `first()` / `last()`
|
|
800
|
-
| `
|
|
801
|
-
| `
|
|
802
|
-
| `
|
|
803
|
-
| `
|
|
804
|
-
| `map(transform)` | Transform records |
|
|
805
|
-
| `forEach(callback)` | Iterate records |
|
|
806
|
-
| `reduce(callback, initial)` | Reduce records |
|
|
807
|
-
| `groupBy(field)` | Group by field value |
|
|
808
|
-
| `pluck(field)` | Extract field values |
|
|
809
|
-
| `summarizeNumeric(field)` | Statistical summary |
|
|
810
|
-
| `sortBy(field, asc?)` | Sort by field |
|
|
811
|
-
| `sortByMultiple(specs)` | Multi-field sort |
|
|
812
|
-
| `paginate(page, size)` | Paginate results |
|
|
813
|
-
| `chunk(size)` | Split into chunks |
|
|
814
|
-
| `distinctBy(field)` | Unique by field |
|
|
815
|
-
| `count(predicate?)` | Count records |
|
|
816
|
-
| `countBy(field)` | Count by field value |
|
|
817
|
-
| `innerJoin(other, thisKey, otherKey)` | Inner join |
|
|
818
|
-
| `leftJoin(other, thisKey, otherKey)` | Left join |
|
|
819
|
-
| `toCsv(delimiter?)` | Export to CSV |
|
|
820
|
-
| `toJSON()` | Export to JSON |
|
|
821
|
-
| `toArray()` | Get plain array |
|
|
822
|
-
| `inspect(limit?)` | Debug output |
|
|
823
|
-
|
|
824
|
-
## Types
|
|
825
|
-
|
|
826
|
-
```typescript
|
|
827
|
-
// Query types
|
|
828
|
-
interface QueryRequest {
|
|
829
|
-
find: any;
|
|
830
|
-
select: SelectionMap;
|
|
831
|
-
include_history?: boolean;
|
|
832
|
-
limit?: number;
|
|
833
|
-
offset?: number;
|
|
834
|
-
sort?: string[];
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
interface QueryResponse<T = any> {
|
|
838
|
-
records: T[];
|
|
839
|
-
total?: number;
|
|
840
|
-
error?: string;
|
|
841
|
-
}
|
|
842
|
-
|
|
843
|
-
// Value types
|
|
844
|
-
type Val = string | number | boolean | null | Val[] | Date | RegExp;
|
|
845
|
-
|
|
846
|
-
interface BetweenValue {
|
|
847
|
-
min: Val;
|
|
848
|
-
max: Val;
|
|
849
|
-
}
|
|
850
|
-
|
|
851
|
-
// Numeric summary
|
|
852
|
-
interface NumericSummary {
|
|
853
|
-
count: number;
|
|
854
|
-
sum: number;
|
|
855
|
-
mean: number;
|
|
856
|
-
median: number;
|
|
857
|
-
min: number;
|
|
858
|
-
max: number;
|
|
859
|
-
standardDeviation: number;
|
|
860
|
-
variance: number;
|
|
861
|
-
}
|
|
862
|
-
```
|
|
488
|
+
| `isEmpty()` | `true` if no records |
|
|
489
|
+
| `first()` / `last()` | First or last record |
|
|
490
|
+
| `get(index)` | Record at index |
|
|
491
|
+
| `toArray()` | Plain array copy |
|
|
492
|
+
| `toJSON()` | JSON string |
|
|
493
|
+
| `cast<T>()` | Re-type the result |
|
|
863
494
|
|
|
864
495
|
---
|
|
865
496
|
|