@onchaindb/sdk 0.4.0 → 0.4.2

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.
Files changed (98) hide show
  1. package/.DS_Store +0 -0
  2. package/.claude/settings.local.json +8 -0
  3. package/.gitignore +5 -0
  4. package/.idea/.gitignore +5 -0
  5. package/.idea/compiler.xml +6 -0
  6. package/.idea/inspectionProfiles/Project_Default.xml +6 -0
  7. package/.idea/jsLinters/eslint.xml +6 -0
  8. package/.idea/modules.xml +8 -0
  9. package/.idea/prettier.xml +7 -0
  10. package/.idea/sdk.iml +12 -0
  11. package/.idea/vcs.xml +6 -0
  12. package/.idea/workspace.xml +257 -0
  13. package/dist/client.d.ts.map +1 -1
  14. package/dist/client.js +11 -3
  15. package/dist/client.js.map +1 -1
  16. package/dist/database.d.ts +0 -20
  17. package/dist/database.d.ts.map +1 -1
  18. package/dist/database.js +0 -40
  19. package/dist/database.js.map +1 -1
  20. package/dist/query-sdk/tests/setup.d.ts +16 -0
  21. package/dist/query-sdk/tests/setup.d.ts.map +1 -0
  22. package/dist/query-sdk/tests/setup.js +49 -0
  23. package/dist/query-sdk/tests/setup.js.map +1 -0
  24. package/examples/basic-usage.ts +136 -0
  25. package/examples/blob-upload-example.ts +140 -0
  26. package/examples/collection-schema-example.ts +304 -0
  27. package/examples/server-side-joins.ts +201 -0
  28. package/examples/tweet-self-joins-example.ts +352 -0
  29. package/package-lock.json +3823 -0
  30. package/package.json +1 -1
  31. package/skills.md +1096 -0
  32. package/src/.env +1 -0
  33. package/src/batch.d.ts +121 -0
  34. package/src/batch.js +205 -0
  35. package/src/batch.ts +257 -0
  36. package/src/client.ts +1856 -0
  37. package/src/database.d.ts +268 -0
  38. package/src/database.js +294 -0
  39. package/src/database.ts +695 -0
  40. package/src/index.d.ts +160 -0
  41. package/src/index.js +186 -0
  42. package/src/index.ts +253 -0
  43. package/src/query-sdk/ConditionBuilder.ts +103 -0
  44. package/src/query-sdk/FieldConditionBuilder.ts +2 -0
  45. package/src/query-sdk/NestedBuilders.ts +186 -0
  46. package/src/query-sdk/OnChainDB.ts +294 -0
  47. package/src/query-sdk/QueryBuilder.ts +1191 -0
  48. package/src/query-sdk/QueryResult.ts +375 -0
  49. package/src/query-sdk/README.md +866 -0
  50. package/src/query-sdk/SelectionBuilder.ts +94 -0
  51. package/src/query-sdk/adapters/HttpClientAdapter.ts +249 -0
  52. package/src/query-sdk/dist/ConditionBuilder.d.ts +22 -0
  53. package/src/query-sdk/dist/ConditionBuilder.js +90 -0
  54. package/src/query-sdk/dist/FieldConditionBuilder.d.ts +1 -0
  55. package/src/query-sdk/dist/FieldConditionBuilder.js +6 -0
  56. package/src/query-sdk/dist/NestedBuilders.d.ts +43 -0
  57. package/src/query-sdk/dist/NestedBuilders.js +144 -0
  58. package/src/query-sdk/dist/OnChainDB.d.ts +19 -0
  59. package/src/query-sdk/dist/OnChainDB.js +123 -0
  60. package/src/query-sdk/dist/QueryBuilder.d.ts +70 -0
  61. package/src/query-sdk/dist/QueryBuilder.js +295 -0
  62. package/src/query-sdk/dist/QueryResult.d.ts +52 -0
  63. package/src/query-sdk/dist/QueryResult.js +293 -0
  64. package/src/query-sdk/dist/SelectionBuilder.d.ts +20 -0
  65. package/src/query-sdk/dist/SelectionBuilder.js +80 -0
  66. package/src/query-sdk/dist/adapters/HttpClientAdapter.d.ts +27 -0
  67. package/src/query-sdk/dist/adapters/HttpClientAdapter.js +170 -0
  68. package/src/query-sdk/dist/index.d.ts +36 -0
  69. package/src/query-sdk/dist/index.js +27 -0
  70. package/src/query-sdk/dist/operators.d.ts +56 -0
  71. package/src/query-sdk/dist/operators.js +289 -0
  72. package/src/query-sdk/dist/tests/setup.d.ts +15 -0
  73. package/src/query-sdk/dist/tests/setup.js +46 -0
  74. package/src/query-sdk/index.ts +59 -0
  75. package/src/query-sdk/jest.config.js +25 -0
  76. package/src/query-sdk/operators.ts +335 -0
  77. package/src/query-sdk/package.json +46 -0
  78. package/src/query-sdk/tests/FieldConditionBuilder.test.ts +84 -0
  79. package/src/query-sdk/tests/LogicalOperator.test.ts +85 -0
  80. package/src/query-sdk/tests/NestedBuilders.test.ts +321 -0
  81. package/src/query-sdk/tests/QueryBuilder.test.ts +348 -0
  82. package/src/query-sdk/tests/QueryResult.test.ts +464 -0
  83. package/src/query-sdk/tests/aggregations.test.ts +653 -0
  84. package/src/query-sdk/tests/comprehensive.test.ts +279 -0
  85. package/src/query-sdk/tests/integration.test.ts +608 -0
  86. package/src/query-sdk/tests/operators.test.ts +327 -0
  87. package/src/query-sdk/tests/setup.ts +59 -0
  88. package/src/query-sdk/tests/unit.test.ts +794 -0
  89. package/src/query-sdk/tsconfig.json +26 -0
  90. package/src/query-sdk/yarn.lock +3092 -0
  91. package/src/types.d.ts +131 -0
  92. package/src/types.js +46 -0
  93. package/src/types.ts +534 -0
  94. package/src/x402/index.ts +12 -0
  95. package/src/x402/types.ts +250 -0
  96. package/src/x402/utils.ts +332 -0
  97. package/tsconfig.json +20 -0
  98. package/yarn.lock +2309 -0
@@ -0,0 +1,866 @@
1
+ # OnChainDB Query SDK
2
+
3
+ The Query SDK provides a powerful fluent API for building queries with server-side JOINs, complex logical operators, field conditions, and result processing utilities.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Quick Start](#quick-start)
8
+ - [QueryBuilder](#querybuilder)
9
+ - [Aggregations](#aggregations)
10
+ - [Server-Side JOINs](#server-side-joins)
11
+ - [Logical Operators](#logical-operators)
12
+ - [Field Operators](#field-operators)
13
+ - [Selection](#selection)
14
+ - [QueryResult Utilities](#queryresult-utilities)
15
+ - [Nested Field Queries](#nested-field-queries)
16
+ - [OnChainDB Static Class](#onchaindb-static-class)
17
+ - [HTTP Adapters](#http-adapters)
18
+ - [API Reference](#api-reference)
19
+
20
+ ## Quick Start
21
+
22
+ ```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
+ const result = await db.queryBuilder()
32
+ .collection('users')
33
+ .whereField('status').equals('active')
34
+ .selectFields(['id', 'name', 'email'])
35
+ .limit(10)
36
+ .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
+ ```
59
+
60
+ ### executeUnique()
61
+
62
+ Returns the latest record by metadata timestamp (`updatedAt` or `createdAt`). Useful for finding a single record when multiple versions may exist.
63
+
64
+ ```typescript
65
+ // Find the latest user record by email
66
+ const user = await db.queryBuilder()
67
+ .collection('users')
68
+ .whereField('email').equals('alice@example.com')
69
+ .executeUnique<User>();
70
+
71
+ // Returns: User | null (latest version by timestamp)
72
+ ```
73
+
74
+ ### WhereField Operators
75
+
76
+ All operators available on `whereField()`:
77
+
78
+ ```typescript
79
+ // Comparison
80
+ .equals(value)
81
+ .notEquals(value)
82
+ .greaterThan(value)
83
+ .greaterThanOrEqual(value)
84
+ .lessThan(value)
85
+ .lessThanOrEqual(value)
86
+ .between(min, max)
87
+
88
+ // String
89
+ .contains(value)
90
+ .startsWith(value)
91
+ .endsWith(value)
92
+ .regExpMatches(pattern)
93
+ .includesCaseInsensitive(value)
94
+ .startsWithCaseInsensitive(value)
95
+ .endsWithCaseInsensitive(value)
96
+
97
+ // Array
98
+ .in(values)
99
+ .notIn(values)
100
+
101
+ // Existence
102
+ .exists()
103
+ .notExists()
104
+ .isNull()
105
+ .isNotNull()
106
+ .isTrue()
107
+ .isFalse()
108
+
109
+ // IP Address
110
+ .isLocalIp()
111
+ .isExternalIp()
112
+ .inCountry(countryCode)
113
+ .cidr(cidrRange)
114
+
115
+ // Special
116
+ .b64(value)
117
+ .inDataset(dataset)
118
+ ```
119
+
120
+ ### Debugging
121
+
122
+ ```typescript
123
+ // Get raw query object for debugging
124
+ const rawQuery = db.queryBuilder()
125
+ .collection('tweets')
126
+ .whereField('author').equals('alice')
127
+ .selectAll()
128
+ .buildRawQuery();
129
+
130
+ console.log(JSON.stringify(rawQuery, null, 2));
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();
136
+ ```
137
+
138
+ ## Aggregations
139
+
140
+ QueryBuilder provides built-in aggregation methods for counting, summing, averaging, and grouping data.
141
+
142
+ ### Basic Aggregations
143
+
144
+ ```typescript
145
+ // Count records
146
+ const activeUsers = await db.queryBuilder()
147
+ .collection('users')
148
+ .whereField('active').equals(true)
149
+ .count();
150
+ // Returns: number
151
+
152
+ // Sum a numeric field
153
+ const totalRevenue = await db.queryBuilder()
154
+ .collection('orders')
155
+ .whereField('status').equals('completed')
156
+ .sumBy('amount');
157
+ // Returns: number
158
+
159
+ // Calculate average
160
+ const avgPrice = await db.queryBuilder()
161
+ .collection('products')
162
+ .whereField('category').equals('electronics')
163
+ .avgBy('price');
164
+ // Returns: number
165
+
166
+ // Find maximum value
167
+ const highestPrice = await db.queryBuilder()
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
189
+ ```
190
+
191
+ ### Grouped Aggregations
192
+
193
+ Use `groupBy()` to perform aggregations on groups of records:
194
+
195
+ ```typescript
196
+ // Count users by country
197
+ const usersByCountry = await db.queryBuilder()
198
+ .collection('users')
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 }
224
+ ```
225
+
226
+ ### Nested Field Grouping
227
+
228
+ GroupBy supports nested field paths:
229
+
230
+ ```typescript
231
+ // Group by nested field
232
+ const ordersByRegion = await db.queryBuilder()
233
+ .collection('orders')
234
+ .groupBy('customer.address.region')
235
+ .sumBy('total');
236
+ // Returns: { "West": 10000, "East": 8500 }
237
+ ```
238
+
239
+ ## Server-Side JOINs
240
+
241
+ Server-side JOINs execute on the backend in a single request. Use `$data.fieldname` to reference parent record fields.
242
+
243
+ ### joinOne (One-to-One)
244
+
245
+ Returns a single object or null. Use when you expect at most one related record.
246
+
247
+ ```typescript
248
+ const result = await db.queryBuilder()
249
+ .collection('tweets')
250
+ .joinOne('author_info', 'users')
251
+ .onField('address').equals('$data.author')
252
+ .selectFields(['display_name', 'avatar_url', 'verified'])
253
+ .build()
254
+ .selectAll()
255
+ .execute();
256
+
257
+ // Result: { id, content, author, author_info: { display_name, ... } | null }
258
+ ```
259
+
260
+ ### joinMany (One-to-Many)
261
+
262
+ Returns an array of related records. Use when you expect multiple related records.
263
+
264
+ ```typescript
265
+ const result = await db.queryBuilder()
266
+ .collection('users')
267
+ .joinMany('tweets', 'tweets')
268
+ .onField('author').equals('$data.address')
269
+ .selectFields(['id', 'content', 'created_at'])
270
+ .build()
271
+ .selectAll()
272
+ .execute();
273
+
274
+ // Result: { address, name, tweets: [{ id, content, ... }, ...] }
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();
290
+ ```
291
+
292
+ ### Multiple JOINs
293
+
294
+ ```typescript
295
+ const result = await db.queryBuilder()
296
+ .collection('tweets')
297
+ .whereField('reply_to_id').isNull()
298
+ // Author profile (one-to-one)
299
+ .joinOne('author_info', 'users')
300
+ .onField('address').equals('$data.author')
301
+ .selectFields(['display_name', 'avatar_url', 'verified'])
302
+ .build()
303
+ // All likes (one-to-many)
304
+ .joinMany('likes', 'likes')
305
+ .onField('tweet_id').equals('$data.id')
306
+ .selectFields(['user', 'created_at'])
307
+ .build()
308
+ // All replies (one-to-many)
309
+ .joinMany('replies', 'tweets')
310
+ .onField('reply_to_id').equals('$data.id')
311
+ .selectFields(['id', 'author', 'content'])
312
+ .build()
313
+ .selectAll()
314
+ .limit(20)
315
+ .execute();
316
+ ```
317
+
318
+ ### Nested JOINs
319
+
320
+ Chain JOINs before calling `.build()` to create nested relationships:
321
+
322
+ ```typescript
323
+ const result = await db.queryBuilder()
324
+ .collection('tweets')
325
+ .whereField('id').equals(tweetId)
326
+ // Get replies with their authors
327
+ .joinMany('replies', 'tweets')
328
+ .onField('reply_to_id').equals('$data.id')
329
+ .selectAll()
330
+ // Nested: get author for each reply
331
+ .joinOne('author_info', 'users')
332
+ .onField('address').equals('$data.author')
333
+ .selectFields(['display_name', 'avatar_url'])
334
+ .build()
335
+ .build()
336
+ .selectAll()
337
+ .execute();
338
+ ```
339
+
340
+ ### Self-Referential JOINs
341
+
342
+ ```typescript
343
+ // Get tweets with quoted tweet info
344
+ const result = await db.queryBuilder()
345
+ .collection('tweets')
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();
357
+ ```
358
+
359
+ ### JoinBuilder Operators
360
+
361
+ Available on `onField()`:
362
+
363
+ ```typescript
364
+ .equals(value)
365
+ .in(values)
366
+ .greaterThan(value)
367
+ .lessThan(value)
368
+ .isNull()
369
+ .isNotNull()
370
+ ```
371
+
372
+ ## Logical Operators
373
+
374
+ Build complex conditions with AND, OR, and NOT:
375
+
376
+ ```typescript
377
+ import { LogicalOperator } from '@onchaindb/sdk';
378
+
379
+ const result = await db.queryBuilder()
380
+ .collection('posts')
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();
395
+ ```
396
+
397
+ ### ConditionBuilder Methods
398
+
399
+ ```typescript
400
+ import { ConditionBuilder, LogicalOperator } from '@onchaindb/sdk';
401
+
402
+ const builder = new ConditionBuilder();
403
+
404
+ // Create field conditions
405
+ builder.field('name').equals('John')
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
+ ```
421
+
422
+ ## Field Operators
423
+
424
+ ### FieldConditionBuilder
425
+
426
+ All available operators:
427
+
428
+ ```typescript
429
+ import { FieldConditionBuilder } from '@onchaindb/sdk';
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)
472
+ ```
473
+
474
+ ## Selection
475
+
476
+ ### SelectionBuilder
477
+
478
+ ```typescript
479
+ import { SelectionBuilder } from '@onchaindb/sdk';
480
+
481
+ // Select specific fields
482
+ const selection = new SelectionBuilder()
483
+ .field('id')
484
+ .field('name')
485
+ .field('email')
486
+ .build();
487
+
488
+ // Select multiple fields at once
489
+ new SelectionBuilder()
490
+ .fields(['id', 'name', 'email', 'created_at'])
491
+ .build();
492
+
493
+ // Nested field selection
494
+ new SelectionBuilder()
495
+ .field('id')
496
+ .nested('profile', nested => nested.field('bio').field('avatar'))
497
+ .build();
498
+
499
+ // Exclude fields
500
+ new SelectionBuilder()
501
+ .excludeFields(['password', 'secret_key'])
502
+ .build();
503
+
504
+ // Select all fields
505
+ SelectionBuilder.all(); // Returns {}
506
+ ```
507
+
508
+ ### Using with QueryBuilder
509
+
510
+ ```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
+ db.queryBuilder()
525
+ .collection('users')
526
+ .select(s => s.field('id').field('name').nested('profile', n => n.field('bio')))
527
+ .execute();
528
+ ```
529
+
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
+ ## Nested Field Queries
599
+
600
+ Query nested objects with dot notation or ORM-like builders:
601
+
602
+ ### Dot Notation
603
+
604
+ ```typescript
605
+ // Query nested fields with dot notation
606
+ const result = await db.queryBuilder()
607
+ .collection('users')
608
+ .whereField('profile.settings.theme').equals('dark')
609
+ .selectAll()
610
+ .execute();
611
+ ```
612
+
613
+ ### ORM-like Nested Builder
614
+
615
+ ```typescript
616
+ import { NestedConditionBuilder, LogicalOperator } from '@onchaindb/sdk';
617
+
618
+ const result = await db.queryBuilder()
619
+ .collection('users')
620
+ .find(builder =>
621
+ builder.nested('user', nested =>
622
+ nested.andGroup(() => [
623
+ LogicalOperator.Condition(
624
+ nested.field('profile').field('name').equals('John')
625
+ ),
626
+ LogicalOperator.Condition(
627
+ nested.field('settings').field('notifications').isTrue()
628
+ )
629
+ ])
630
+ )
631
+ )
632
+ .execute();
633
+ ```
634
+
635
+ ## OnChainDB Static Class
636
+
637
+ For standalone query building without client initialization:
638
+
639
+ ```typescript
640
+ import { OnChainDB, FetchHttpClient } from '@onchaindb/sdk';
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": {}}');
652
+
653
+ // Query from JSON object
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');
662
+
663
+ // Reset configuration
664
+ OnChainDB.reset();
665
+ ```
666
+
667
+ ## HTTP Adapters
668
+
669
+ ### Built-in Adapters
670
+
671
+ ```typescript
672
+ import {
673
+ FetchHttpClient,
674
+ AxiosHttpClient,
675
+ NodeHttpClient,
676
+ createHttpClient
677
+ } from '@onchaindb/sdk';
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();
690
+ ```
691
+
692
+ ### Custom HTTP Client
693
+
694
+ ```typescript
695
+ import { HttpClient } from '@onchaindb/sdk';
696
+
697
+ class CustomHttpClient implements HttpClient {
698
+ async post(url: string, data: any, headers?: Record<string, string>): Promise<any> {
699
+ // Your implementation
700
+ const response = await fetch(url, {
701
+ method: 'POST',
702
+ headers: { 'Content-Type': 'application/json', ...headers },
703
+ body: JSON.stringify(data)
704
+ });
705
+ return response.json();
706
+ }
707
+ }
708
+ ```
709
+
710
+ ## API Reference
711
+
712
+ ### QueryBuilder
713
+
714
+ | Method | Description |
715
+ |--------|-------------|
716
+ | `collection(name)` | Set target collection |
717
+ | `whereField(name)` | Start a field condition |
718
+ | `find(builderFn)` | Set complex logical conditions |
719
+ | `select(builderFn)` | Configure selection with builder |
720
+ | `selectFields(fields)` | Select specific fields |
721
+ | `selectAll()` | Select all fields |
722
+ | `joinOne(alias, model)` | One-to-one JOIN (returns object/null) |
723
+ | `joinMany(alias, model)` | One-to-many JOIN (returns array) |
724
+ | `joinWith(alias, model)` | JOIN with default behavior |
725
+ | `serverJoin(alias, model, resolve)` | Low-level server JOIN |
726
+ | `limit(n)` | Limit result count |
727
+ | `offset(n)` | Skip results (pagination) |
728
+ | `orderBy(field)` | Sort results |
729
+ | `includeHistory(bool)` | Include historical versions |
730
+ | `withFieldMap(map)` | Set field mapping |
731
+ | `execute()` | Execute query |
732
+ | `executeUnique()` | Execute and return latest record by metadata |
733
+ | `executeWithPayment(quoteId, txHash, network?)` | Execute with x402 payment |
734
+ | `count()` | Count matching records |
735
+ | `sumBy(field)` | Sum numeric field values |
736
+ | `avgBy(field)` | Calculate average of numeric field |
737
+ | `maxBy(field)` | Find maximum value |
738
+ | `minBy(field)` | Find minimum value |
739
+ | `distinctBy(field)` | Get distinct field values |
740
+ | `countDistinct(field)` | Count distinct values |
741
+ | `groupBy(field)` | Start grouped aggregation |
742
+ | `buildRawQuery()` | Get raw query object |
743
+ | `clone()` | Clone the builder |
744
+ | `isValid()` | Check if query has conditions |
745
+
746
+ ### JoinBuilder
747
+
748
+ | Method | Description |
749
+ |--------|-------------|
750
+ | `onField(name)` | Start JOIN condition |
751
+ | `on(builderFn)` | Complex JOIN condition |
752
+ | `selectFields(fields)` | Select fields from joined collection |
753
+ | `selectAll()` | Select all fields |
754
+ | `selecting(builderFn)` | Configure selection with builder |
755
+ | `joinOne(alias, model)` | Nested one-to-one JOIN |
756
+ | `joinMany(alias, model)` | Nested one-to-many JOIN |
757
+ | `build()` | Complete JOIN and return to parent |
758
+
759
+ ### GroupByQueryBuilder
760
+
761
+ Returned by `queryBuilder.groupBy(field)`:
762
+
763
+ | Method | Description |
764
+ |--------|-------------|
765
+ | `count()` | Count records per group |
766
+ | `sumBy(field)` | Sum field values per group |
767
+ | `avgBy(field)` | Average field values per group |
768
+ | `maxBy(field)` | Max field value per group |
769
+ | `minBy(field)` | Min field value per group |
770
+
771
+ ### LogicalOperator
772
+
773
+ | Static Method | Description |
774
+ |---------------|-------------|
775
+ | `And(conditions)` | Combine with AND logic |
776
+ | `Or(conditions)` | Combine with OR logic |
777
+ | `Not(conditions)` | Negate conditions |
778
+ | `Condition(cond)` | Wrap a field condition |
779
+
780
+ ### SelectionBuilder
781
+
782
+ | Method | Description |
783
+ |--------|-------------|
784
+ | `field(name)` | Include a field |
785
+ | `fields(names)` | Include multiple fields |
786
+ | `nested(name, builderFn)` | Nested selection |
787
+ | `exclude(name)` | Exclude a field |
788
+ | `excludeFields(names)` | Exclude multiple fields |
789
+ | `build()` | Get selection map |
790
+ | `clear()` | Reset builder |
791
+ | `static all()` | Select all fields |
792
+
793
+ ### QueryResult
794
+
795
+ | Method | Description |
796
+ |--------|-------------|
797
+ | `len()` / `length()` | Record count |
798
+ | `isEmpty()` | Check if empty |
799
+ | `first()` / `last()` / `get(i)` | Access records |
800
+ | `any(predicate)` | Any match? |
801
+ | `all(predicate)` | All match? |
802
+ | `find(predicate)` | Find first matching |
803
+ | `filter(predicate)` | Filter records |
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
+ ```
863
+
864
+ ---
865
+
866
+ For complete SDK documentation, see the [main README](../../README.md).