@onchaindb/sdk 0.4.4 → 1.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 +29 -43
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +199 -323
- 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 +6 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -15
- 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/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 +1 -1
- package/dist/query-sdk/index.d.ts.map +1 -1
- 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 +153 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/jest.config.js +4 -0
- package/package.json +1 -1
- package/skills.md +0 -1
- package/src/client.ts +243 -745
- package/src/database.ts +70 -493
- package/src/index.ts +40 -193
- package/src/query-sdk/ConditionBuilder.ts +37 -89
- package/src/query-sdk/NestedBuilders.ts +90 -92
- package/src/query-sdk/QueryBuilder.ts +59 -218
- package/src/query-sdk/QueryResult.ts +4 -330
- package/src/query-sdk/README.md +214 -583
- package/src/query-sdk/index.ts +1 -1
- 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.ts +229 -8
- 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/OnChainDB.d.ts +0 -19
- package/src/query-sdk/dist/OnChainDB.js +0 -123
- 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
|
@@ -1,653 +0,0 @@
|
|
|
1
|
-
import { QueryBuilder, FieldConditionBuilder, LogicalOperator } from '../index';
|
|
2
|
-
|
|
3
|
-
describe('QueryBuilder - Query Building Verification', () => {
|
|
4
|
-
const serverUrl = 'http://localhost:3000';
|
|
5
|
-
const app = 'testApp';
|
|
6
|
-
|
|
7
|
-
describe('Basic query structure', () => {
|
|
8
|
-
test('should build correct query request with collection', () => {
|
|
9
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
10
|
-
|
|
11
|
-
const request = queryBuilder
|
|
12
|
-
.collection('users')
|
|
13
|
-
.whereField('email').equals('alice@example.com')
|
|
14
|
-
.getQueryRequest();
|
|
15
|
-
|
|
16
|
-
expect(request.root).toBe('testApp::users');
|
|
17
|
-
expect(request.find).toEqual({
|
|
18
|
-
email: { is: 'alice@example.com' }
|
|
19
|
-
});
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
test('should build query with multiple whereField conditions', () => {
|
|
23
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
24
|
-
|
|
25
|
-
// Note: chaining whereField overwrites previous condition in current implementation
|
|
26
|
-
const request = queryBuilder
|
|
27
|
-
.collection('users')
|
|
28
|
-
.whereField('active').equals(true)
|
|
29
|
-
.getQueryRequest();
|
|
30
|
-
|
|
31
|
-
expect(request.find).toEqual({
|
|
32
|
-
active: { is: true }
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
test('should build query with limit and offset', () => {
|
|
37
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
38
|
-
|
|
39
|
-
const request = queryBuilder
|
|
40
|
-
.collection('orders')
|
|
41
|
-
.whereField('status').equals('completed')
|
|
42
|
-
.limit(50)
|
|
43
|
-
.offset(100)
|
|
44
|
-
.getQueryRequest();
|
|
45
|
-
|
|
46
|
-
expect(request.limit).toBe(50);
|
|
47
|
-
expect(request.offset).toBe(100);
|
|
48
|
-
expect(request.root).toBe('testApp::orders');
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test('should build query with orderBy', () => {
|
|
52
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
53
|
-
|
|
54
|
-
const request = queryBuilder
|
|
55
|
-
.collection('products')
|
|
56
|
-
.whereField('category').equals('electronics')
|
|
57
|
-
.orderBy('price')
|
|
58
|
-
.getQueryRequest();
|
|
59
|
-
|
|
60
|
-
expect(request.sortBy).toBe('price');
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
test('should build query with field selection', () => {
|
|
64
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
65
|
-
|
|
66
|
-
const request = queryBuilder
|
|
67
|
-
.collection('users')
|
|
68
|
-
.whereField('active').equals(true)
|
|
69
|
-
.selectFields(['id', 'name', 'email'])
|
|
70
|
-
.getQueryRequest();
|
|
71
|
-
|
|
72
|
-
expect(request.select).toEqual({
|
|
73
|
-
id: true,
|
|
74
|
-
name: true,
|
|
75
|
-
email: true
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test('should build query with selectAll', () => {
|
|
80
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
81
|
-
|
|
82
|
-
const request = queryBuilder
|
|
83
|
-
.collection('users')
|
|
84
|
-
.selectAll()
|
|
85
|
-
.getQueryRequest();
|
|
86
|
-
|
|
87
|
-
expect(request.select).toEqual({});
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('Complex find conditions', () => {
|
|
92
|
-
test('should build AND conditions correctly', () => {
|
|
93
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
94
|
-
|
|
95
|
-
const request = queryBuilder
|
|
96
|
-
.collection('users')
|
|
97
|
-
.find(builder =>
|
|
98
|
-
LogicalOperator.And([
|
|
99
|
-
LogicalOperator.Condition(new FieldConditionBuilder('status').equals('active')),
|
|
100
|
-
LogicalOperator.Condition(new FieldConditionBuilder('age').greaterThan(18))
|
|
101
|
-
])
|
|
102
|
-
)
|
|
103
|
-
.getQueryRequest();
|
|
104
|
-
|
|
105
|
-
expect(request.find).toEqual({
|
|
106
|
-
and: [
|
|
107
|
-
{ status: { is: 'active' } },
|
|
108
|
-
{ age: { greaterThan: 18 } }
|
|
109
|
-
]
|
|
110
|
-
});
|
|
111
|
-
});
|
|
112
|
-
|
|
113
|
-
test('should build OR conditions correctly', () => {
|
|
114
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
115
|
-
|
|
116
|
-
const request = queryBuilder
|
|
117
|
-
.collection('orders')
|
|
118
|
-
.find(builder =>
|
|
119
|
-
LogicalOperator.Or([
|
|
120
|
-
LogicalOperator.Condition(new FieldConditionBuilder('status').equals('pending')),
|
|
121
|
-
LogicalOperator.Condition(new FieldConditionBuilder('status').equals('processing'))
|
|
122
|
-
])
|
|
123
|
-
)
|
|
124
|
-
.getQueryRequest();
|
|
125
|
-
|
|
126
|
-
expect(request.find).toEqual({
|
|
127
|
-
or: [
|
|
128
|
-
{ status: { is: 'pending' } },
|
|
129
|
-
{ status: { is: 'processing' } }
|
|
130
|
-
]
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
|
|
134
|
-
test('should build NOT conditions correctly', () => {
|
|
135
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
136
|
-
|
|
137
|
-
const request = queryBuilder
|
|
138
|
-
.collection('users')
|
|
139
|
-
.find(builder =>
|
|
140
|
-
LogicalOperator.Not([
|
|
141
|
-
LogicalOperator.Condition(new FieldConditionBuilder('status').equals('banned'))
|
|
142
|
-
])
|
|
143
|
-
)
|
|
144
|
-
.getQueryRequest();
|
|
145
|
-
|
|
146
|
-
expect(request.find).toEqual({
|
|
147
|
-
not: [
|
|
148
|
-
{ status: { is: 'banned' } }
|
|
149
|
-
]
|
|
150
|
-
});
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('should build nested AND/OR conditions', () => {
|
|
154
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
155
|
-
|
|
156
|
-
const request = queryBuilder
|
|
157
|
-
.collection('products')
|
|
158
|
-
.find(builder =>
|
|
159
|
-
LogicalOperator.And([
|
|
160
|
-
LogicalOperator.Condition(new FieldConditionBuilder('active').equals(true)),
|
|
161
|
-
LogicalOperator.Or([
|
|
162
|
-
LogicalOperator.Condition(new FieldConditionBuilder('category').equals('electronics')),
|
|
163
|
-
LogicalOperator.Condition(new FieldConditionBuilder('category').equals('computers'))
|
|
164
|
-
])
|
|
165
|
-
])
|
|
166
|
-
)
|
|
167
|
-
.getQueryRequest();
|
|
168
|
-
|
|
169
|
-
expect(request.find).toEqual({
|
|
170
|
-
and: [
|
|
171
|
-
{ active: { is: true } },
|
|
172
|
-
{
|
|
173
|
-
or: [
|
|
174
|
-
{ category: { is: 'electronics' } },
|
|
175
|
-
{ category: { is: 'computers' } }
|
|
176
|
-
]
|
|
177
|
-
}
|
|
178
|
-
]
|
|
179
|
-
});
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('WhereClause operators', () => {
|
|
184
|
-
test('should build equals condition', () => {
|
|
185
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
186
|
-
|
|
187
|
-
const request = queryBuilder
|
|
188
|
-
.collection('users')
|
|
189
|
-
.whereField('name').equals('John')
|
|
190
|
-
.getQueryRequest();
|
|
191
|
-
|
|
192
|
-
expect(request.find).toEqual({
|
|
193
|
-
name: { is: 'John' }
|
|
194
|
-
});
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
test('should build notEquals condition', () => {
|
|
198
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
199
|
-
|
|
200
|
-
const request = queryBuilder
|
|
201
|
-
.collection('users')
|
|
202
|
-
.whereField('status').notEquals('deleted')
|
|
203
|
-
.getQueryRequest();
|
|
204
|
-
|
|
205
|
-
expect(request.find).toEqual({
|
|
206
|
-
status: { isNot: 'deleted' }
|
|
207
|
-
});
|
|
208
|
-
});
|
|
209
|
-
|
|
210
|
-
test('should build greaterThan condition', () => {
|
|
211
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
212
|
-
|
|
213
|
-
const request = queryBuilder
|
|
214
|
-
.collection('products')
|
|
215
|
-
.whereField('price').greaterThan(100)
|
|
216
|
-
.getQueryRequest();
|
|
217
|
-
|
|
218
|
-
expect(request.find).toEqual({
|
|
219
|
-
price: { greaterThan: 100 }
|
|
220
|
-
});
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
test('should build greaterThanOrEqual condition', () => {
|
|
224
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
225
|
-
|
|
226
|
-
const request = queryBuilder
|
|
227
|
-
.collection('products')
|
|
228
|
-
.whereField('stock').greaterThanOrEqual(10)
|
|
229
|
-
.getQueryRequest();
|
|
230
|
-
|
|
231
|
-
expect(request.find).toEqual({
|
|
232
|
-
stock: { greaterThanOrEqual: 10 }
|
|
233
|
-
});
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
test('should build lessThan condition', () => {
|
|
237
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
238
|
-
|
|
239
|
-
const request = queryBuilder
|
|
240
|
-
.collection('products')
|
|
241
|
-
.whereField('price').lessThan(50)
|
|
242
|
-
.getQueryRequest();
|
|
243
|
-
|
|
244
|
-
expect(request.find).toEqual({
|
|
245
|
-
price: { lessThan: 50 }
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
test('should build lessThanOrEqual condition', () => {
|
|
250
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
251
|
-
|
|
252
|
-
const request = queryBuilder
|
|
253
|
-
.collection('orders')
|
|
254
|
-
.whereField('quantity').lessThanOrEqual(100)
|
|
255
|
-
.getQueryRequest();
|
|
256
|
-
|
|
257
|
-
expect(request.find).toEqual({
|
|
258
|
-
quantity: { lessThanOrEqual: 100 }
|
|
259
|
-
});
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test('should build contains condition', () => {
|
|
263
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
264
|
-
|
|
265
|
-
const request = queryBuilder
|
|
266
|
-
.collection('posts')
|
|
267
|
-
.whereField('content').contains('blockchain')
|
|
268
|
-
.getQueryRequest();
|
|
269
|
-
|
|
270
|
-
expect(request.find).toEqual({
|
|
271
|
-
content: { includes: 'blockchain' }
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
|
|
275
|
-
test('should build startsWith condition', () => {
|
|
276
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
277
|
-
|
|
278
|
-
const request = queryBuilder
|
|
279
|
-
.collection('users')
|
|
280
|
-
.whereField('email').startsWith('admin')
|
|
281
|
-
.getQueryRequest();
|
|
282
|
-
|
|
283
|
-
expect(request.find).toEqual({
|
|
284
|
-
email: { startsWith: 'admin' }
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
test('should build endsWith condition', () => {
|
|
289
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
290
|
-
|
|
291
|
-
const request = queryBuilder
|
|
292
|
-
.collection('users')
|
|
293
|
-
.whereField('email').endsWith('@example.com')
|
|
294
|
-
.getQueryRequest();
|
|
295
|
-
|
|
296
|
-
expect(request.find).toEqual({
|
|
297
|
-
email: { endsWith: '@example.com' }
|
|
298
|
-
});
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
test('should build in condition', () => {
|
|
302
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
303
|
-
|
|
304
|
-
const request = queryBuilder
|
|
305
|
-
.collection('orders')
|
|
306
|
-
.whereField('status').in(['pending', 'processing', 'shipped'])
|
|
307
|
-
.getQueryRequest();
|
|
308
|
-
|
|
309
|
-
expect(request.find).toEqual({
|
|
310
|
-
status: { in: ['pending', 'processing', 'shipped'] }
|
|
311
|
-
});
|
|
312
|
-
});
|
|
313
|
-
|
|
314
|
-
test('should build notIn condition', () => {
|
|
315
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
316
|
-
|
|
317
|
-
const request = queryBuilder
|
|
318
|
-
.collection('users')
|
|
319
|
-
.whereField('role').notIn(['banned', 'suspended'])
|
|
320
|
-
.getQueryRequest();
|
|
321
|
-
|
|
322
|
-
expect(request.find).toEqual({
|
|
323
|
-
role: { notIn: ['banned', 'suspended'] }
|
|
324
|
-
});
|
|
325
|
-
});
|
|
326
|
-
|
|
327
|
-
test('should build exists condition', () => {
|
|
328
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
329
|
-
|
|
330
|
-
const request = queryBuilder
|
|
331
|
-
.collection('profiles')
|
|
332
|
-
.whereField('avatar').exists()
|
|
333
|
-
.getQueryRequest();
|
|
334
|
-
|
|
335
|
-
expect(request.find).toEqual({
|
|
336
|
-
avatar: { exists: true }
|
|
337
|
-
});
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
test('should build notExists condition', () => {
|
|
341
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
342
|
-
|
|
343
|
-
const request = queryBuilder
|
|
344
|
-
.collection('profiles')
|
|
345
|
-
.whereField('deletedAt').notExists()
|
|
346
|
-
.getQueryRequest();
|
|
347
|
-
|
|
348
|
-
expect(request.find).toEqual({
|
|
349
|
-
deletedAt: { exists: false }
|
|
350
|
-
});
|
|
351
|
-
});
|
|
352
|
-
|
|
353
|
-
test('should build isNull condition', () => {
|
|
354
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
355
|
-
|
|
356
|
-
const request = queryBuilder
|
|
357
|
-
.collection('orders')
|
|
358
|
-
.whereField('cancelledAt').isNull()
|
|
359
|
-
.getQueryRequest();
|
|
360
|
-
|
|
361
|
-
expect(request.find).toEqual({
|
|
362
|
-
cancelledAt: { isNull: true }
|
|
363
|
-
});
|
|
364
|
-
});
|
|
365
|
-
|
|
366
|
-
test('should build isNotNull condition', () => {
|
|
367
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
368
|
-
|
|
369
|
-
const request = queryBuilder
|
|
370
|
-
.collection('orders')
|
|
371
|
-
.whereField('completedAt').isNotNull()
|
|
372
|
-
.getQueryRequest();
|
|
373
|
-
|
|
374
|
-
expect(request.find).toEqual({
|
|
375
|
-
completedAt: { isNull: false }
|
|
376
|
-
});
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('should build between condition', () => {
|
|
380
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
381
|
-
|
|
382
|
-
const request = queryBuilder
|
|
383
|
-
.collection('products')
|
|
384
|
-
.whereField('price').between(10, 100)
|
|
385
|
-
.getQueryRequest();
|
|
386
|
-
|
|
387
|
-
expect(request.find).toEqual({
|
|
388
|
-
price: { betweenOp: { from: 10, to: 100 } }
|
|
389
|
-
});
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
test('should build regExpMatches condition', () => {
|
|
393
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
394
|
-
|
|
395
|
-
const request = queryBuilder
|
|
396
|
-
.collection('logs')
|
|
397
|
-
.whereField('message').regExpMatches('^ERROR:.*')
|
|
398
|
-
.getQueryRequest();
|
|
399
|
-
|
|
400
|
-
expect(request.find).toEqual({
|
|
401
|
-
message: { regExpMatches: '^ERROR:.*' }
|
|
402
|
-
});
|
|
403
|
-
});
|
|
404
|
-
});
|
|
405
|
-
|
|
406
|
-
describe('Nested field queries', () => {
|
|
407
|
-
test('should build nested field condition with dot notation', () => {
|
|
408
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
409
|
-
|
|
410
|
-
const request = queryBuilder
|
|
411
|
-
.collection('users')
|
|
412
|
-
.whereField('profile.country').equals('USA')
|
|
413
|
-
.getQueryRequest();
|
|
414
|
-
|
|
415
|
-
expect(request.find).toEqual({
|
|
416
|
-
profile: {
|
|
417
|
-
country: {
|
|
418
|
-
is: 'USA'
|
|
419
|
-
}
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
test('should build deeply nested field condition', () => {
|
|
425
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
426
|
-
|
|
427
|
-
const request = queryBuilder
|
|
428
|
-
.collection('data')
|
|
429
|
-
.whereField('a.b.c.d').greaterThan(100)
|
|
430
|
-
.getQueryRequest();
|
|
431
|
-
|
|
432
|
-
expect(request.find).toEqual({
|
|
433
|
-
a: {
|
|
434
|
-
b: {
|
|
435
|
-
c: {
|
|
436
|
-
d: {
|
|
437
|
-
greaterThan: 100
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
});
|
|
443
|
-
});
|
|
444
|
-
});
|
|
445
|
-
|
|
446
|
-
describe('Server-side JOINs', () => {
|
|
447
|
-
test('should build simple server join', () => {
|
|
448
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
449
|
-
|
|
450
|
-
const request = queryBuilder
|
|
451
|
-
.collection('posts')
|
|
452
|
-
.whereField('published').equals(true)
|
|
453
|
-
.joinOne('author', 'users')
|
|
454
|
-
.onField('authorId').equals('userId')
|
|
455
|
-
.selectFields(['name', 'email'])
|
|
456
|
-
.build()
|
|
457
|
-
.getQueryRequest();
|
|
458
|
-
|
|
459
|
-
expect(request.root).toBe('testApp::posts');
|
|
460
|
-
expect(request.find.author).toBeDefined();
|
|
461
|
-
expect(request.find.author.model).toBe('users');
|
|
462
|
-
expect(request.find.author.many).toBe(false);
|
|
463
|
-
expect(request.find.author.resolve.select).toEqual({
|
|
464
|
-
name: true,
|
|
465
|
-
email: true
|
|
466
|
-
});
|
|
467
|
-
});
|
|
468
|
-
|
|
469
|
-
test('should build one-to-many join', () => {
|
|
470
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
471
|
-
|
|
472
|
-
const request = queryBuilder
|
|
473
|
-
.collection('users')
|
|
474
|
-
.joinMany('posts', 'posts')
|
|
475
|
-
.onField('authorId').equals('userId')
|
|
476
|
-
.selectAll()
|
|
477
|
-
.build()
|
|
478
|
-
.getQueryRequest();
|
|
479
|
-
|
|
480
|
-
expect(request.find.posts).toBeDefined();
|
|
481
|
-
expect(request.find.posts.model).toBe('posts');
|
|
482
|
-
expect(request.find.posts.many).toBe(true);
|
|
483
|
-
});
|
|
484
|
-
|
|
485
|
-
test('should build multiple joins', () => {
|
|
486
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
487
|
-
|
|
488
|
-
const request = queryBuilder
|
|
489
|
-
.collection('posts')
|
|
490
|
-
.joinOne('author', 'users')
|
|
491
|
-
.selectFields(['name'])
|
|
492
|
-
.build()
|
|
493
|
-
.joinMany('comments', 'comments')
|
|
494
|
-
.selectFields(['text', 'createdAt'])
|
|
495
|
-
.build()
|
|
496
|
-
.getQueryRequest();
|
|
497
|
-
|
|
498
|
-
expect(request.find.author).toBeDefined();
|
|
499
|
-
expect(request.find.author.many).toBe(false);
|
|
500
|
-
expect(request.find.comments).toBeDefined();
|
|
501
|
-
expect(request.find.comments.many).toBe(true);
|
|
502
|
-
});
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
describe('includeHistory', () => {
|
|
506
|
-
test('should include history flag when set to true', () => {
|
|
507
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
508
|
-
|
|
509
|
-
const request = queryBuilder
|
|
510
|
-
.collection('documents')
|
|
511
|
-
.whereField('id').equals('doc123')
|
|
512
|
-
.includeHistory(true)
|
|
513
|
-
.getQueryRequest();
|
|
514
|
-
|
|
515
|
-
expect((request as any).include_history).toBe(true);
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
test('should not include history flag when not set', () => {
|
|
519
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
520
|
-
|
|
521
|
-
const request = queryBuilder
|
|
522
|
-
.collection('documents')
|
|
523
|
-
.whereField('id').equals('doc123')
|
|
524
|
-
.getQueryRequest();
|
|
525
|
-
|
|
526
|
-
expect((request as any).include_history).toBeUndefined();
|
|
527
|
-
});
|
|
528
|
-
});
|
|
529
|
-
|
|
530
|
-
describe('Query validation', () => {
|
|
531
|
-
test('should validate query with find conditions', () => {
|
|
532
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
533
|
-
|
|
534
|
-
queryBuilder.whereField('name').equals('test');
|
|
535
|
-
expect(queryBuilder.isValid()).toBe(true);
|
|
536
|
-
});
|
|
537
|
-
|
|
538
|
-
test('should validate query with selections only', () => {
|
|
539
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
540
|
-
|
|
541
|
-
queryBuilder.selectFields(['id', 'name']);
|
|
542
|
-
expect(queryBuilder.isValid()).toBe(true);
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
test('should invalidate empty query', () => {
|
|
546
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
547
|
-
|
|
548
|
-
expect(queryBuilder.isValid()).toBe(false);
|
|
549
|
-
});
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
describe('Query cloning', () => {
|
|
553
|
-
test('should create independent clone', () => {
|
|
554
|
-
const original = new QueryBuilder(undefined, serverUrl, app)
|
|
555
|
-
.collection('users')
|
|
556
|
-
.whereField('status').equals('active')
|
|
557
|
-
.limit(10);
|
|
558
|
-
|
|
559
|
-
const cloned = original.clone();
|
|
560
|
-
|
|
561
|
-
// Modify clone
|
|
562
|
-
cloned.limit(20);
|
|
563
|
-
cloned.offset(50);
|
|
564
|
-
|
|
565
|
-
const originalRequest = original.getQueryRequest();
|
|
566
|
-
const clonedRequest = cloned.getQueryRequest();
|
|
567
|
-
|
|
568
|
-
expect(originalRequest.limit).toBe(10);
|
|
569
|
-
expect(originalRequest.offset).toBeUndefined();
|
|
570
|
-
expect(clonedRequest.limit).toBe(20);
|
|
571
|
-
expect(clonedRequest.offset).toBe(50);
|
|
572
|
-
});
|
|
573
|
-
});
|
|
574
|
-
|
|
575
|
-
describe('Full query examples', () => {
|
|
576
|
-
test('should build complete e-commerce query', () => {
|
|
577
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
578
|
-
|
|
579
|
-
const request = queryBuilder
|
|
580
|
-
.collection('orders')
|
|
581
|
-
.find(builder =>
|
|
582
|
-
LogicalOperator.And([
|
|
583
|
-
LogicalOperator.Condition(new FieldConditionBuilder('status').equals('completed')),
|
|
584
|
-
LogicalOperator.Condition(new FieldConditionBuilder('total').greaterThan(100)),
|
|
585
|
-
LogicalOperator.Or([
|
|
586
|
-
LogicalOperator.Condition(new FieldConditionBuilder('paymentMethod').equals('credit_card')),
|
|
587
|
-
LogicalOperator.Condition(new FieldConditionBuilder('paymentMethod').equals('paypal'))
|
|
588
|
-
])
|
|
589
|
-
])
|
|
590
|
-
)
|
|
591
|
-
.selectFields(['id', 'customerId', 'total', 'createdAt'])
|
|
592
|
-
.orderBy('createdAt')
|
|
593
|
-
.limit(50)
|
|
594
|
-
.offset(0)
|
|
595
|
-
.getQueryRequest();
|
|
596
|
-
|
|
597
|
-
expect(request).toEqual({
|
|
598
|
-
find: {
|
|
599
|
-
and: [
|
|
600
|
-
{ status: { is: 'completed' } },
|
|
601
|
-
{ total: { greaterThan: 100 } },
|
|
602
|
-
{
|
|
603
|
-
or: [
|
|
604
|
-
{ paymentMethod: { is: 'credit_card' } },
|
|
605
|
-
{ paymentMethod: { is: 'paypal' } }
|
|
606
|
-
]
|
|
607
|
-
}
|
|
608
|
-
]
|
|
609
|
-
},
|
|
610
|
-
select: {
|
|
611
|
-
id: true,
|
|
612
|
-
customerId: true,
|
|
613
|
-
total: true,
|
|
614
|
-
createdAt: true
|
|
615
|
-
},
|
|
616
|
-
sortBy: 'createdAt',
|
|
617
|
-
limit: 50,
|
|
618
|
-
offset: 0,
|
|
619
|
-
root: 'testApp::orders'
|
|
620
|
-
});
|
|
621
|
-
});
|
|
622
|
-
|
|
623
|
-
test('should build user search query with joins', () => {
|
|
624
|
-
const queryBuilder = new QueryBuilder(undefined, serverUrl, app);
|
|
625
|
-
|
|
626
|
-
const request = queryBuilder
|
|
627
|
-
.collection('users')
|
|
628
|
-
.whereField('profile.verified').equals(true)
|
|
629
|
-
.joinMany('posts', 'posts')
|
|
630
|
-
.onField('authorId').equals('userId')
|
|
631
|
-
.selectFields(['title', 'likes'])
|
|
632
|
-
.build()
|
|
633
|
-
.joinOne('subscription', 'subscriptions')
|
|
634
|
-
.onField('userId').equals('id')
|
|
635
|
-
.selectFields(['plan', 'expiresAt'])
|
|
636
|
-
.build()
|
|
637
|
-
.selectFields(['id', 'name', 'email'])
|
|
638
|
-
.limit(20)
|
|
639
|
-
.getQueryRequest();
|
|
640
|
-
|
|
641
|
-
expect(request.root).toBe('testApp::users');
|
|
642
|
-
expect(request.find.profile.verified.is).toBe(true);
|
|
643
|
-
expect(request.find.posts.many).toBe(true);
|
|
644
|
-
expect(request.find.subscription.many).toBe(false);
|
|
645
|
-
expect(request.select).toEqual({
|
|
646
|
-
id: true,
|
|
647
|
-
name: true,
|
|
648
|
-
email: true
|
|
649
|
-
});
|
|
650
|
-
expect(request.limit).toBe(20);
|
|
651
|
-
});
|
|
652
|
-
});
|
|
653
|
-
});
|