@travetto/model-query 8.0.0-alpha.3 → 8.0.0-alpha.31

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.
@@ -1,34 +1,34 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import { Suite, Test } from '@travetto/test';
3
+ import { type ModelCrudSupport, NotFoundError } from '@travetto/model';
4
4
  import { TimeUtil } from '@travetto/runtime';
5
- import { NotFoundError, type ModelCrudSupport } from '@travetto/model';
5
+ import { Suite, Test } from '@travetto/test';
6
6
 
7
7
  import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
8
8
 
9
- import { Aged, Location, Names, Note, Person, SimpleList, WithNestedLists, WithNestedNestedLists } from './model.ts';
10
-
11
9
  import type { ModelQuerySupport } from '../../src/types/query.ts';
10
+ import { Aged, Location, Names, Note, Person, PersonFamily, Recipe, SimpleList, WithNestedLists, WithNestedNestedLists } from './model.ts';
12
11
 
13
12
  @Suite()
14
13
  export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport & ModelCrudSupport> {
15
-
16
14
  supportsGeo = true;
17
15
 
18
16
  @Test()
19
17
  async testIds() {
20
18
  const svc = await this.service;
21
19
 
22
- const people = [1, 2, 3, 8].map(x => Person.from({
23
- id: svc.idSource.create(),
24
- name: 'Bob Omber',
25
- age: 20 + x,
26
- gender: 'm',
27
- address: {
28
- street1: 'a',
29
- ...(x === 1 ? { street2: 'b' } : {})
30
- }
31
- }));
20
+ const people = [1, 2, 3, 8].map(x =>
21
+ Person.from({
22
+ id: svc.idSource.create(),
23
+ name: 'Bob Omber',
24
+ age: 20 + x,
25
+ gender: 'm',
26
+ address: {
27
+ street1: 'a',
28
+ ...(x === 1 ? { street2: 'b' } : {})
29
+ }
30
+ })
31
+ );
32
32
 
33
33
  await this.saveAll(Person, people);
34
34
 
@@ -48,20 +48,25 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
48
48
  @Test('verify word boundary')
49
49
  async testWordBoundary() {
50
50
  const service = await this.service;
51
- await this.saveAll(Person, [1, 2, 3, 8].map(x => Person.from({
52
- name: 'Bob Omber',
53
- age: 20 + x,
54
- gender: 'm',
55
- address: {
56
- street1: 'a',
57
- ...(x === 1 ? { street2: 'b' } : {})
58
- }
59
- })));
51
+ await this.saveAll(
52
+ Person,
53
+ [1, 2, 3, 8].map(x =>
54
+ Person.from({
55
+ name: 'Bob Omber',
56
+ age: 20 + x,
57
+ gender: 'm',
58
+ address: {
59
+ street1: 'a',
60
+ ...(x === 1 ? { street2: 'b' } : {})
61
+ }
62
+ })
63
+ )
64
+ );
60
65
 
61
66
  const results = await service.query(Person, { where: { name: { $regex: /\bomb.*/i } } });
62
67
  assert(results.length === 4);
63
68
 
64
- const results2 = await service.query(Person, { where: { name: { $regex: /\bmbo.*/i } } });
69
+ const results2 = await service.query(Person, { where: { name: { $regex: /\b.?mbo.*/i } } });
65
70
  assert(results2.length === 0);
66
71
 
67
72
  const results3 = await service.query(Person, { where: { name: { $regex: /\bomb.*/ } } });
@@ -71,15 +76,20 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
71
76
  @Test('verify query one behavior')
72
77
  async testQueryOne() {
73
78
  const service = await this.service;
74
- await this.saveAll(Person, [1, 2, 3, 8].map(x => Person.from({
75
- name: 'Bob Omber',
76
- age: 20 + x,
77
- gender: 'm',
78
- address: {
79
- street1: 'a',
80
- ...(x === 1 ? { street2: 'b' } : {})
81
- }
82
- })));
79
+ await this.saveAll(
80
+ Person,
81
+ [1, 2, 3, 8].map(x =>
82
+ Person.from({
83
+ name: 'Bob Omber',
84
+ age: 20 + x,
85
+ gender: 'm',
86
+ address: {
87
+ street1: 'a',
88
+ ...(x === 1 ? { street2: 'b' } : {})
89
+ }
90
+ })
91
+ )
92
+ );
83
93
 
84
94
  await assert.rejects(() => service.queryOne(Person, { where: { gender: 'm' } }), /Invalid number of results/);
85
95
  await assert.rejects(() => service.queryOne(Person, { where: { gender: 'z' } }), NotFoundError);
@@ -91,13 +101,19 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
91
101
  @Test('Verify array $in queries work properly')
92
102
  async testArrayContains() {
93
103
  const svc = await this.service;
94
- const first = await svc.create(SimpleList, SimpleList.from({
95
- names: ['a', 'b', 'c']
96
- }));
97
-
98
- const second = await svc.create(SimpleList, SimpleList.from({
99
- names: ['b', 'c', 'd']
100
- }));
104
+ const first = await svc.create(
105
+ SimpleList,
106
+ SimpleList.from({
107
+ names: ['a', 'b', 'c']
108
+ })
109
+ );
110
+
111
+ const second = await svc.create(
112
+ SimpleList,
113
+ SimpleList.from({
114
+ names: ['b', 'c', 'd']
115
+ })
116
+ );
101
117
 
102
118
  const single = await svc.query(SimpleList, {
103
119
  where: {
@@ -154,6 +170,62 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
154
170
  assert(idNin.length === 2);
155
171
  }
156
172
 
173
+ @Test('Verify list intersection queries')
174
+ async testListIntersection() {
175
+ const service = await this.service;
176
+
177
+ await this.saveAll(SimpleList, [
178
+ SimpleList.from({
179
+ names: ['apple', 'banana']
180
+ }),
181
+ SimpleList.from({
182
+ names: ['banana', 'cherry']
183
+ }),
184
+ SimpleList.from({
185
+ names: ['cherry', 'date']
186
+ }),
187
+ SimpleList.from({
188
+ names: ['elderberry']
189
+ })
190
+ ]);
191
+
192
+ const singleMatch = await service.query(SimpleList, {
193
+ where: {
194
+ names: {
195
+ $in: ['apple']
196
+ }
197
+ }
198
+ });
199
+ assert(singleMatch.length === 1);
200
+
201
+ const intersectionMatch = await service.query(SimpleList, {
202
+ where: {
203
+ names: {
204
+ $in: ['banana', 'cherry']
205
+ }
206
+ }
207
+ });
208
+ assert(intersectionMatch.length === 3);
209
+
210
+ const disjointIntersectionMatch = await service.query(SimpleList, {
211
+ where: {
212
+ names: {
213
+ $in: ['apple', 'date']
214
+ }
215
+ }
216
+ });
217
+ assert(disjointIntersectionMatch.length === 2);
218
+
219
+ const noIntersectionMatch = await service.query(SimpleList, {
220
+ where: {
221
+ names: {
222
+ $in: ['fig', 'grape']
223
+ }
224
+ }
225
+ });
226
+ assert(noIntersectionMatch.length === 0);
227
+ }
228
+
157
229
  @Test('verify all operators')
158
230
  async testArrayAll() {
159
231
  const service = await this.service;
@@ -161,7 +233,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
161
233
  await this.saveAll(Names, [
162
234
  Names.from({ values: ['a', 'b', 'c'] }),
163
235
  Names.from({ values: ['a', 'b'] }),
164
- Names.from({ values: ['b', 'c'] }),
236
+ Names.from({ values: ['b', 'c'] })
165
237
  ]);
166
238
 
167
239
  const results = await service.query(Names, {});
@@ -212,23 +284,27 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
212
284
  async testSorting() {
213
285
  const service = await this.service;
214
286
 
215
- const people = [1, 2, 3, 8].map(x => Person.from({
216
- id: service.idSource.create(),
217
- name: 'Bob',
218
- age: 20 + x,
219
- gender: 'm',
220
- address: {
221
- street1: 'a',
222
- ...(x === 1 ? { street2: 'b' } : {})
223
- }
224
- }));
287
+ const people = [1, 2, 3, 8].map(x =>
288
+ Person.from({
289
+ id: service.idSource.create(),
290
+ name: 'Bob',
291
+ age: 20 + x,
292
+ gender: 'm',
293
+ address: {
294
+ street1: 'a',
295
+ ...(x === 1 ? { street2: 'b' } : {})
296
+ }
297
+ })
298
+ );
225
299
 
226
300
  await this.saveAll(Person, people);
227
301
 
228
302
  const all = await service.query(Person, {
229
- sort: [{
230
- age: -1
231
- }]
303
+ sort: [
304
+ {
305
+ age: -1
306
+ }
307
+ ]
232
308
  });
233
309
  assert(all[0].age >= all[1].age);
234
310
  }
@@ -243,23 +319,30 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
243
319
 
244
320
  const toAdd = [];
245
321
 
246
- for (let i = 0; i < 5; i++) {
247
- for (let j = 0; j < 5; j++) {
248
- toAdd.push(Location.from({
249
- point: [i, j],
250
- }));
322
+ for (let i = 0; i < 5; i += 1) {
323
+ for (let j = 0; j < 5; j += 1) {
324
+ toAdd.push(
325
+ Location.from({
326
+ point: [i, j]
327
+ })
328
+ );
251
329
  }
252
330
  }
253
331
 
254
332
  await this.saveAll(Location, toAdd);
255
333
 
256
- assert(await svc.queryCount(Location, {}) === 25);
334
+ assert((await svc.queryCount(Location, {})) === 25);
257
335
 
258
336
  const result = await svc.query(Location, {
259
337
  limit: 100,
260
338
  where: {
261
339
  point: {
262
- $geoWithin: [[-1, -1], [-1, 6], [6, 6], [6, -1]]
340
+ $geoWithin: [
341
+ [-1, -1],
342
+ [-1, 6],
343
+ [6, 6],
344
+ [6, -1]
345
+ ]
263
346
  }
264
347
  }
265
348
  });
@@ -286,15 +369,18 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
286
369
 
287
370
  const id = service.idSource.create();
288
371
 
289
- await service.create(Note, Note.from({
290
- id,
291
- entities: [
292
- {
293
- label: 'hi',
294
- id
295
- }
296
- ]
297
- }));
372
+ await service.create(
373
+ Note,
374
+ Note.from({
375
+ id,
376
+ entities: [
377
+ {
378
+ label: 'hi',
379
+ id
380
+ }
381
+ ]
382
+ })
383
+ );
298
384
 
299
385
  const out = await service.queryCount(Note, {
300
386
  where: {
@@ -320,9 +406,12 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
320
406
  @Test()
321
407
  async verifyDateRange() {
322
408
  const service = await this.service;
323
- await this.saveAll(Aged, (['-5d', '-4d', '-3d', '-2d', '-1d', '0d', '1d', '2d', '3d', '4d', '5d'] as const).map(delta =>
324
- Aged.from({ createdAt: TimeUtil.fromNow(delta) })
325
- ));
409
+ await this.saveAll(
410
+ Aged,
411
+ (['-5d', '-4d', '-3d', '-2d', '-1d', '0d', '1d', '2d', '3d', '4d', '5d'] as const).map(delta =>
412
+ Aged.from({ createdAt: TimeUtil.fromNow(delta) })
413
+ )
414
+ );
326
415
 
327
416
  const simple = await service.queryCount(Aged, {
328
417
  where: {
@@ -366,28 +455,13 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
366
455
  @Test()
367
456
  async verifyArrayEmptyVsNot() {
368
457
  const service = await this.service;
369
- await service.create(WithNestedLists, {
370
- tags: ['a', 'b']
371
- });
372
-
373
- await service.create(WithNestedLists, {
374
- names: ['c', 'd'],
375
- });
376
-
377
- await service.create(WithNestedLists, {
378
- names: ['c', 'd'],
379
- tags: ['e', 'f']
380
- });
381
-
382
- await service.create(WithNestedLists, {
383
- names: ['g', 'h'],
384
- tags: []
385
- });
386
-
387
- await service.create(WithNestedLists, {
388
- names: [],
389
- tags: []
390
- });
458
+ await this.saveAll(WithNestedLists, [
459
+ WithNestedLists.from({ tags: ['a', 'b'] }),
460
+ WithNestedLists.from({ names: ['c', 'd'] }),
461
+ WithNestedLists.from({ names: ['c', 'd'], tags: ['e', 'f'] }),
462
+ WithNestedLists.from({ names: ['g', 'h'], tags: [] }),
463
+ WithNestedLists.from({ names: [], tags: [] })
464
+ ]);
391
465
 
392
466
  let total = await service.queryCount(WithNestedLists, {
393
467
  where: {
@@ -395,6 +469,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
395
469
  }
396
470
  });
397
471
  assert(total === 3);
472
+
398
473
  total = await service.queryCount(WithNestedLists, {
399
474
  where: {
400
475
  names: { $exists: false }
@@ -430,35 +505,20 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
430
505
  names: { $exists: true }
431
506
  }
432
507
  });
433
-
434
508
  assert(total === 1);
435
509
  }
436
510
 
437
511
  @Test()
438
512
  async verifyNestedArrayEmptyVsNot() {
439
513
  const service = await this.service;
440
- await service.create(WithNestedNestedLists, {
441
- tags: ['a', 'b']
442
- });
443
-
444
- await service.create(WithNestedNestedLists, {
445
- sub: { names: ['c', 'd'] },
446
- });
447
514
 
448
- await service.create(WithNestedNestedLists, {
449
- sub: { names: ['c', 'd'] },
450
- tags: ['e', 'f']
451
- });
452
-
453
- await service.create(WithNestedNestedLists, {
454
- sub: { names: ['g', 'h'] },
455
- tags: []
456
- });
457
-
458
- await service.create(WithNestedNestedLists, {
459
- sub: {},
460
- tags: []
461
- });
515
+ await this.saveAll(WithNestedNestedLists, [
516
+ WithNestedNestedLists.from({ tags: ['a', 'b'] }),
517
+ WithNestedNestedLists.from({ sub: { names: ['c', 'd'] } }),
518
+ WithNestedNestedLists.from({ sub: { names: ['c', 'd'] }, tags: ['e', 'f'] }),
519
+ WithNestedNestedLists.from({ sub: { names: ['g', 'h'] }, tags: [] }),
520
+ WithNestedNestedLists.from({ sub: {}, tags: [] })
521
+ ]);
462
522
 
463
523
  let total = await service.queryCount(WithNestedNestedLists, {
464
524
  where: {
@@ -466,6 +526,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
466
526
  }
467
527
  });
468
528
  assert(total === 3);
529
+
469
530
  total = await service.queryCount(WithNestedNestedLists, {
470
531
  where: {
471
532
  sub: { names: { $exists: false } }
@@ -504,5 +565,178 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
504
565
 
505
566
  assert(total === 1);
506
567
  }
507
- }
508
568
 
569
+ @Test('Verify queries on nested arrays of schemas')
570
+ async verifyNestedArraySchemaQuery() {
571
+ const service = await this.service;
572
+
573
+ await this.saveAll(Recipe, [
574
+ Recipe.from({
575
+ sections: [{ ingredients: [{ name: 'garlic' }, { name: 'onion' }] }]
576
+ }),
577
+ Recipe.from({
578
+ sections: [{ ingredients: [{ name: 'paprika' }, { name: 'salt' }] }]
579
+ }),
580
+ Recipe.from({
581
+ sections: [{ ingredients: [{ name: 'garlic' }, { name: 'thyme' }] }]
582
+ })
583
+ ]);
584
+
585
+ const matchSingle = await service.query(Recipe, {
586
+ where: {
587
+ sections: { ingredients: { name: 'garlic' } }
588
+ }
589
+ });
590
+ assert(matchSingle.length === 2);
591
+
592
+ const matchIn = await service.query(Recipe, {
593
+ where: {
594
+ sections: { ingredients: { name: { $in: ['onion', 'salt'] } } }
595
+ }
596
+ });
597
+ assert(matchIn.length === 2);
598
+
599
+ const matchNotEqual = await service.query(Recipe, {
600
+ where: {
601
+ sections: { ingredients: { name: { $ne: 'garlic' } } }
602
+ }
603
+ });
604
+ assert(matchNotEqual.length === 1);
605
+ }
606
+
607
+ @Test('Verify array of objects property queries')
608
+ async verifyArrayOfObjectsProperty() {
609
+ const service = await this.service;
610
+
611
+ await this.saveAll(Note, [
612
+ Note.from({
613
+ entities: [
614
+ { id: '1', label: 'one' },
615
+ { id: '2', label: 'two' }
616
+ ]
617
+ }),
618
+ Note.from({
619
+ entities: [
620
+ { id: '3', label: 'three' },
621
+ { id: '4', label: 'four' }
622
+ ]
623
+ }),
624
+ Note.from({
625
+ entities: [{ id: '5', label: 'five' }]
626
+ })
627
+ ]);
628
+
629
+ const inResults = await service.query(Note, {
630
+ where: {
631
+ entities: {
632
+ label: {
633
+ $in: ['one', 'four', 'missing']
634
+ }
635
+ }
636
+ }
637
+ });
638
+ assert(inResults.length === 2);
639
+
640
+ const eqResults = await service.query(Note, {
641
+ where: {
642
+ entities: {
643
+ label: 'five'
644
+ }
645
+ }
646
+ });
647
+ assert(eqResults.length === 1);
648
+ }
649
+
650
+ @Test('Verify nested object array property queries')
651
+ async verifyNestedObjectArrayProperty() {
652
+ const service = await this.service;
653
+
654
+ await this.saveAll(PersonFamily, [
655
+ PersonFamily.from({
656
+ family: { children: [{ name: 'one' }, { name: 'two' }] }
657
+ }),
658
+ PersonFamily.from({
659
+ family: { children: [{ name: 'three' }, { name: 'four' }] }
660
+ }),
661
+ PersonFamily.from({
662
+ family: { children: [{ name: 'five' }] }
663
+ })
664
+ ]);
665
+
666
+ const inResults = await service.query(PersonFamily, {
667
+ where: {
668
+ family: {
669
+ children: {
670
+ name: {
671
+ $in: ['one', 'two', 'three']
672
+ }
673
+ }
674
+ }
675
+ }
676
+ });
677
+ assert(inResults.length === 2);
678
+
679
+ const eqResults = await service.query(PersonFamily, {
680
+ where: {
681
+ family: {
682
+ children: {
683
+ name: 'five'
684
+ }
685
+ }
686
+ }
687
+ });
688
+ assert(eqResults.length === 1);
689
+
690
+ const neResults = await service.query(PersonFamily, {
691
+ where: {
692
+ family: {
693
+ children: {
694
+ name: {
695
+ $ne: 'five'
696
+ }
697
+ }
698
+ }
699
+ }
700
+ });
701
+ assert(neResults.length === 2);
702
+
703
+ const ninResults = await service.query(PersonFamily, {
704
+ where: {
705
+ family: {
706
+ children: {
707
+ name: {
708
+ $nin: ['one', 'two']
709
+ }
710
+ }
711
+ }
712
+ }
713
+ });
714
+ assert(ninResults.length === 2);
715
+
716
+ const allResults = await service.query(PersonFamily, {
717
+ where: {
718
+ family: {
719
+ children: {
720
+ name: {
721
+ $all: ['one', 'two']
722
+ }
723
+ }
724
+ }
725
+ }
726
+ });
727
+ assert(allResults.length === 1);
728
+
729
+ const existsResults = await service.query(PersonFamily, {
730
+ where: {
731
+ family: {
732
+ children: {
733
+ name: {
734
+ $exists: true
735
+ }
736
+ }
737
+ }
738
+ }
739
+ });
740
+ assert(existsResults.length === 3);
741
+ }
742
+ }