@travetto/model-query 8.0.0-alpha.23 → 8.0.0-alpha.25
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -13
- package/__index__.ts +5 -8
- package/package.json +5 -5
- package/src/internal/types.ts +16 -10
- package/src/model/indexes.ts +4 -5
- package/src/model/query.ts +5 -7
- package/src/model/where-clause.ts +30 -21
- package/src/types/crud.ts +4 -4
- package/src/types/facet.ts +4 -4
- package/src/types/query.ts +2 -2
- package/src/types/suggest.ts +15 -5
- package/src/util/crud.ts +2 -2
- package/src/util/facet.ts +1 -1
- package/src/util/query.ts +29 -26
- package/src/util/suggest.ts +13 -9
- package/src/verifier.ts +24 -30
- package/support/doc.support.tsx +6 -7
- package/support/test/crud.ts +72 -58
- package/support/test/facet.ts +21 -13
- package/support/test/model.ts +18 -2
- package/support/test/polymorphism.ts +14 -16
- package/support/test/query.ts +268 -76
- package/support/test/suggest.ts +4 -4
package/support/test/query.ts
CHANGED
|
@@ -1,34 +1,34 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import { type ModelCrudSupport, NotFoundError } from '@travetto/model';
|
|
4
4
|
import { TimeUtil } from '@travetto/runtime';
|
|
5
|
-
import {
|
|
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, 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 =>
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
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,15 +48,20 @@ 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(
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
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);
|
|
@@ -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(
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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: {
|
|
@@ -161,7 +177,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
161
177
|
await this.saveAll(Names, [
|
|
162
178
|
Names.from({ values: ['a', 'b', 'c'] }),
|
|
163
179
|
Names.from({ values: ['a', 'b'] }),
|
|
164
|
-
Names.from({ values: ['b', 'c'] })
|
|
180
|
+
Names.from({ values: ['b', 'c'] })
|
|
165
181
|
]);
|
|
166
182
|
|
|
167
183
|
const results = await service.query(Names, {});
|
|
@@ -212,23 +228,27 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
212
228
|
async testSorting() {
|
|
213
229
|
const service = await this.service;
|
|
214
230
|
|
|
215
|
-
const people = [1, 2, 3, 8].map(x =>
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
231
|
+
const people = [1, 2, 3, 8].map(x =>
|
|
232
|
+
Person.from({
|
|
233
|
+
id: service.idSource.create(),
|
|
234
|
+
name: 'Bob',
|
|
235
|
+
age: 20 + x,
|
|
236
|
+
gender: 'm',
|
|
237
|
+
address: {
|
|
238
|
+
street1: 'a',
|
|
239
|
+
...(x === 1 ? { street2: 'b' } : {})
|
|
240
|
+
}
|
|
241
|
+
})
|
|
242
|
+
);
|
|
225
243
|
|
|
226
244
|
await this.saveAll(Person, people);
|
|
227
245
|
|
|
228
246
|
const all = await service.query(Person, {
|
|
229
|
-
sort: [
|
|
230
|
-
|
|
231
|
-
|
|
247
|
+
sort: [
|
|
248
|
+
{
|
|
249
|
+
age: -1
|
|
250
|
+
}
|
|
251
|
+
]
|
|
232
252
|
});
|
|
233
253
|
assert(all[0].age >= all[1].age);
|
|
234
254
|
}
|
|
@@ -243,23 +263,30 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
243
263
|
|
|
244
264
|
const toAdd = [];
|
|
245
265
|
|
|
246
|
-
for (let i = 0; i < 5; i
|
|
247
|
-
for (let j = 0; j < 5; j
|
|
248
|
-
toAdd.push(
|
|
249
|
-
|
|
250
|
-
|
|
266
|
+
for (let i = 0; i < 5; i += 1) {
|
|
267
|
+
for (let j = 0; j < 5; j += 1) {
|
|
268
|
+
toAdd.push(
|
|
269
|
+
Location.from({
|
|
270
|
+
point: [i, j]
|
|
271
|
+
})
|
|
272
|
+
);
|
|
251
273
|
}
|
|
252
274
|
}
|
|
253
275
|
|
|
254
276
|
await this.saveAll(Location, toAdd);
|
|
255
277
|
|
|
256
|
-
assert(await svc.queryCount(Location, {}) === 25);
|
|
278
|
+
assert((await svc.queryCount(Location, {})) === 25);
|
|
257
279
|
|
|
258
280
|
const result = await svc.query(Location, {
|
|
259
281
|
limit: 100,
|
|
260
282
|
where: {
|
|
261
283
|
point: {
|
|
262
|
-
$geoWithin: [
|
|
284
|
+
$geoWithin: [
|
|
285
|
+
[-1, -1],
|
|
286
|
+
[-1, 6],
|
|
287
|
+
[6, 6],
|
|
288
|
+
[6, -1]
|
|
289
|
+
]
|
|
263
290
|
}
|
|
264
291
|
}
|
|
265
292
|
});
|
|
@@ -286,15 +313,18 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
286
313
|
|
|
287
314
|
const id = service.idSource.create();
|
|
288
315
|
|
|
289
|
-
await service.create(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
316
|
+
await service.create(
|
|
317
|
+
Note,
|
|
318
|
+
Note.from({
|
|
319
|
+
id,
|
|
320
|
+
entities: [
|
|
321
|
+
{
|
|
322
|
+
label: 'hi',
|
|
323
|
+
id
|
|
324
|
+
}
|
|
325
|
+
]
|
|
326
|
+
})
|
|
327
|
+
);
|
|
298
328
|
|
|
299
329
|
const out = await service.queryCount(Note, {
|
|
300
330
|
where: {
|
|
@@ -320,9 +350,12 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
320
350
|
@Test()
|
|
321
351
|
async verifyDateRange() {
|
|
322
352
|
const service = await this.service;
|
|
323
|
-
await this.saveAll(
|
|
324
|
-
Aged
|
|
325
|
-
|
|
353
|
+
await this.saveAll(
|
|
354
|
+
Aged,
|
|
355
|
+
(['-5d', '-4d', '-3d', '-2d', '-1d', '0d', '1d', '2d', '3d', '4d', '5d'] as const).map(delta =>
|
|
356
|
+
Aged.from({ createdAt: TimeUtil.fromNow(delta) })
|
|
357
|
+
)
|
|
358
|
+
);
|
|
326
359
|
|
|
327
360
|
const simple = await service.queryCount(Aged, {
|
|
328
361
|
where: {
|
|
@@ -371,7 +404,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
371
404
|
});
|
|
372
405
|
|
|
373
406
|
await service.create(WithNestedLists, {
|
|
374
|
-
names: ['c', 'd']
|
|
407
|
+
names: ['c', 'd']
|
|
375
408
|
});
|
|
376
409
|
|
|
377
410
|
await service.create(WithNestedLists, {
|
|
@@ -442,7 +475,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
442
475
|
});
|
|
443
476
|
|
|
444
477
|
await service.create(WithNestedNestedLists, {
|
|
445
|
-
sub: { names: ['c', 'd'] }
|
|
478
|
+
sub: { names: ['c', 'd'] }
|
|
446
479
|
});
|
|
447
480
|
|
|
448
481
|
await service.create(WithNestedNestedLists, {
|
|
@@ -504,5 +537,164 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
504
537
|
|
|
505
538
|
assert(total === 1);
|
|
506
539
|
}
|
|
507
|
-
}
|
|
508
540
|
|
|
541
|
+
@Test('Verify array of objects property queries')
|
|
542
|
+
async verifyArrayOfObjectsProperty() {
|
|
543
|
+
const service = await this.service;
|
|
544
|
+
|
|
545
|
+
await service.create(
|
|
546
|
+
Note,
|
|
547
|
+
Note.from({
|
|
548
|
+
entities: [
|
|
549
|
+
{ id: '1', label: 'one' },
|
|
550
|
+
{ id: '2', label: 'two' }
|
|
551
|
+
]
|
|
552
|
+
})
|
|
553
|
+
);
|
|
554
|
+
|
|
555
|
+
await service.create(
|
|
556
|
+
Note,
|
|
557
|
+
Note.from({
|
|
558
|
+
entities: [
|
|
559
|
+
{ id: '3', label: 'three' },
|
|
560
|
+
{ id: '4', label: 'four' }
|
|
561
|
+
]
|
|
562
|
+
})
|
|
563
|
+
);
|
|
564
|
+
|
|
565
|
+
await service.create(
|
|
566
|
+
Note,
|
|
567
|
+
Note.from({
|
|
568
|
+
entities: [{ id: '5', label: 'five' }]
|
|
569
|
+
})
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
const inResults = await service.query(Note, {
|
|
573
|
+
where: {
|
|
574
|
+
entities: {
|
|
575
|
+
label: {
|
|
576
|
+
$in: ['one', 'four', 'missing']
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
});
|
|
581
|
+
assert(inResults.length === 2);
|
|
582
|
+
|
|
583
|
+
const eqResults = await service.query(Note, {
|
|
584
|
+
where: {
|
|
585
|
+
entities: {
|
|
586
|
+
label: 'five'
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
});
|
|
590
|
+
assert(eqResults.length === 1);
|
|
591
|
+
}
|
|
592
|
+
|
|
593
|
+
@Test('Verify nested object array property queries')
|
|
594
|
+
async verifyNestedObjectArrayProperty() {
|
|
595
|
+
const service = await this.service;
|
|
596
|
+
|
|
597
|
+
await service.create(
|
|
598
|
+
PersonFamily,
|
|
599
|
+
PersonFamily.from({
|
|
600
|
+
family: {
|
|
601
|
+
children: [{ name: 'one' }, { name: 'two' }]
|
|
602
|
+
}
|
|
603
|
+
})
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
await service.create(
|
|
607
|
+
PersonFamily,
|
|
608
|
+
PersonFamily.from({
|
|
609
|
+
family: {
|
|
610
|
+
children: [{ name: 'three' }, { name: 'four' }]
|
|
611
|
+
}
|
|
612
|
+
})
|
|
613
|
+
);
|
|
614
|
+
|
|
615
|
+
await service.create(
|
|
616
|
+
PersonFamily,
|
|
617
|
+
PersonFamily.from({
|
|
618
|
+
family: {
|
|
619
|
+
children: [{ name: 'five' }]
|
|
620
|
+
}
|
|
621
|
+
})
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
const inResults = await service.query(PersonFamily, {
|
|
625
|
+
where: {
|
|
626
|
+
family: {
|
|
627
|
+
children: {
|
|
628
|
+
name: {
|
|
629
|
+
$in: ['one', 'two', 'three']
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
});
|
|
635
|
+
assert(inResults.length === 2);
|
|
636
|
+
|
|
637
|
+
const eqResults = await service.query(PersonFamily, {
|
|
638
|
+
where: {
|
|
639
|
+
family: {
|
|
640
|
+
children: {
|
|
641
|
+
name: 'five'
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
assert(eqResults.length === 1);
|
|
647
|
+
|
|
648
|
+
const neResults = await service.query(PersonFamily, {
|
|
649
|
+
where: {
|
|
650
|
+
family: {
|
|
651
|
+
children: {
|
|
652
|
+
name: {
|
|
653
|
+
$ne: 'five'
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
});
|
|
659
|
+
assert(neResults.length === 2);
|
|
660
|
+
|
|
661
|
+
const ninResults = await service.query(PersonFamily, {
|
|
662
|
+
where: {
|
|
663
|
+
family: {
|
|
664
|
+
children: {
|
|
665
|
+
name: {
|
|
666
|
+
$nin: ['one', 'two']
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
});
|
|
672
|
+
assert(ninResults.length === 2);
|
|
673
|
+
|
|
674
|
+
const allResults = await service.query(PersonFamily, {
|
|
675
|
+
where: {
|
|
676
|
+
family: {
|
|
677
|
+
children: {
|
|
678
|
+
name: {
|
|
679
|
+
$all: ['one', 'two']
|
|
680
|
+
}
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
});
|
|
685
|
+
assert(allResults.length === 1);
|
|
686
|
+
|
|
687
|
+
const existsResults = await service.query(PersonFamily, {
|
|
688
|
+
where: {
|
|
689
|
+
family: {
|
|
690
|
+
children: {
|
|
691
|
+
name: {
|
|
692
|
+
$exists: true
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
});
|
|
698
|
+
assert(existsResults.length === 3);
|
|
699
|
+
}
|
|
700
|
+
}
|
package/support/test/suggest.ts
CHANGED
|
@@ -5,12 +5,11 @@ import { Suite, Test } from '@travetto/test';
|
|
|
5
5
|
|
|
6
6
|
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
7
7
|
|
|
8
|
-
import { Person } from './model.ts';
|
|
9
8
|
import type { ModelQuerySuggestSupport } from '../../src/types/suggest.ts';
|
|
9
|
+
import { Person } from './model.ts';
|
|
10
10
|
|
|
11
11
|
@Suite()
|
|
12
12
|
export abstract class ModelQuerySuggestSuite extends BaseModelSuite<ModelQuerySuggestSupport & ModelCrudSupport> {
|
|
13
|
-
|
|
14
13
|
async #loadPeople() {
|
|
15
14
|
const names = ['Bob', 'Bo', 'Barry', 'Rob', 'Robert', 'Robbie'];
|
|
16
15
|
const people = [0, 1, 2, 3, 4, 5].map(x =>
|
|
@@ -22,7 +21,8 @@ export abstract class ModelQuerySuggestSuite extends BaseModelSuite<ModelQuerySu
|
|
|
22
21
|
street1: 'a',
|
|
23
22
|
...(x === 1 ? { street2: 'b' } : {})
|
|
24
23
|
}
|
|
25
|
-
})
|
|
24
|
+
})
|
|
25
|
+
);
|
|
26
26
|
|
|
27
27
|
await this.saveAll(Person, people);
|
|
28
28
|
}
|
|
@@ -65,4 +65,4 @@ export abstract class ModelQuerySuggestSuite extends BaseModelSuite<ModelQuerySu
|
|
|
65
65
|
assert(suggestedEntities[0] instanceof Person);
|
|
66
66
|
assert(suggestedEntities[1] instanceof Person);
|
|
67
67
|
}
|
|
68
|
-
}
|
|
68
|
+
}
|