@travetto/model-query 8.0.3 → 8.0.4
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 +13 -18
- package/package.json +1 -1
- package/src/types/aggregate.ts +23 -21
- package/src/types/query.ts +7 -1
- package/src/util/aggregate.ts +28 -9
- package/support/test/aggregate.ts +50 -62
- package/support/test/polymorphism.ts +10 -11
- package/support/test/query.ts +8 -9
package/README.md
CHANGED
|
@@ -35,7 +35,13 @@ export interface ModelQuerySupport {
|
|
|
35
35
|
* @param query The query to search for
|
|
36
36
|
* @param failOnMany Should the query fail on more than one result found, defaults to true
|
|
37
37
|
*/
|
|
38
|
-
|
|
38
|
+
getByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
|
|
39
|
+
/**
|
|
40
|
+
* Find the count of matching documents by query.
|
|
41
|
+
* @param cls The model class
|
|
42
|
+
* @param query The query to count for
|
|
43
|
+
*/
|
|
44
|
+
countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
39
45
|
}
|
|
40
46
|
```
|
|
41
47
|
|
|
@@ -122,35 +128,24 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
122
128
|
```
|
|
123
129
|
|
|
124
130
|
### Aggregate
|
|
125
|
-
This contract provides the ability to run strongly-typed field-level
|
|
131
|
+
This contract provides the ability to run strongly-typed field-level statistics (such as min, max, count, avg, and sum) on models with optional query filtering.
|
|
126
132
|
|
|
127
133
|
**Code: Query Aggregate**
|
|
128
134
|
```typescript
|
|
129
135
|
export interface ModelQueryAggregateSupport extends ModelQuerySupport {
|
|
130
136
|
/**
|
|
131
|
-
* Run an aggregation on a field
|
|
137
|
+
* Run an aggregation on a comparable field.
|
|
138
|
+
* For numeric fields (number/bigint), returns count, min, max, avg, and sum.
|
|
139
|
+
* For date fields, returns count, min, and max.
|
|
132
140
|
* @param modelClass The model class to aggregate
|
|
133
|
-
* @param operation The operation to perform ('sum', 'avg', 'min', 'max')
|
|
134
141
|
* @param field The field to aggregate on
|
|
135
142
|
* @param query Additional query filtering
|
|
136
143
|
*/
|
|
137
|
-
aggregateFieldByQuery<
|
|
138
|
-
T extends ModelType,
|
|
139
|
-
Op extends AggregateOperation,
|
|
140
|
-
F extends (Op extends AggregateNumericOperation ? ValidNumericFields<T> : ValidComparableFields<T>)
|
|
141
|
-
>(
|
|
144
|
+
aggregateFieldByQuery<T extends ModelType, F extends ValidComparableFields<T>>(
|
|
142
145
|
modelClass: Class<T>,
|
|
143
|
-
operation: Op,
|
|
144
146
|
field: F,
|
|
145
147
|
query?: ModelQuery<T>
|
|
146
|
-
): Promise<
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Find the count of matching documents by query.
|
|
150
|
-
* @param cls The model class
|
|
151
|
-
* @param query The query to count for
|
|
152
|
-
*/
|
|
153
|
-
countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
148
|
+
): Promise<FieldAggregateResult<T, F>>;
|
|
154
149
|
}
|
|
155
150
|
```
|
|
156
151
|
|
package/package.json
CHANGED
package/src/types/aggregate.ts
CHANGED
|
@@ -2,13 +2,9 @@ import type { ModelType } from '@travetto/model';
|
|
|
2
2
|
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
import type { ModelQuery } from '../model/query.ts';
|
|
5
|
-
import type { ValidComparableFields
|
|
5
|
+
import type { ValidComparableFields } from '../model/where-clause.ts';
|
|
6
6
|
import type { ModelQuerySupport } from './query.ts';
|
|
7
7
|
|
|
8
|
-
export type AggregateNumericOperation = 'sum' | 'avg';
|
|
9
|
-
export type AggregateComparableOperation = 'min' | 'max';
|
|
10
|
-
export type AggregateOperation = AggregateNumericOperation | AggregateComparableOperation;
|
|
11
|
-
|
|
12
8
|
export type PathType<T, Path extends string> = Path extends `${infer Head}.${infer Tail}`
|
|
13
9
|
? Head extends keyof T
|
|
14
10
|
? PathType<NonNullable<T[Head]> extends (infer Element)[] ? Element : NonNullable<T[Head]>, Tail>
|
|
@@ -23,33 +19,39 @@ export type AggregateResultType<T, F extends string> = [PathType<T, F>] extends
|
|
|
23
19
|
? number | Date | bigint | undefined
|
|
24
20
|
: PathType<T, F> | undefined;
|
|
25
21
|
|
|
22
|
+
export type DateFieldAggregateResult = {
|
|
23
|
+
count: number;
|
|
24
|
+
min?: Date;
|
|
25
|
+
max?: Date;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export type NumericFieldAggregateResult<T, F extends string> = {
|
|
29
|
+
count: number;
|
|
30
|
+
min?: AggregateResultType<T, F>;
|
|
31
|
+
max?: AggregateResultType<T, F>;
|
|
32
|
+
avg?: AggregateResultType<T, F>;
|
|
33
|
+
sum?: AggregateResultType<T, F>;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export type FieldAggregateResult<T, F extends string> =
|
|
37
|
+
NonNullable<PathType<T, F>> extends Date ? DateFieldAggregateResult : NumericFieldAggregateResult<T, F>;
|
|
38
|
+
|
|
26
39
|
/**
|
|
27
40
|
* The contract for a model service with aggregate support
|
|
28
41
|
* @concrete
|
|
29
42
|
*/
|
|
30
43
|
export interface ModelQueryAggregateSupport extends ModelQuerySupport {
|
|
31
44
|
/**
|
|
32
|
-
* Run an aggregation on a field
|
|
45
|
+
* Run an aggregation on a comparable field.
|
|
46
|
+
* For numeric fields (number/bigint), returns count, min, max, avg, and sum.
|
|
47
|
+
* For date fields, returns count, min, and max.
|
|
33
48
|
* @param modelClass The model class to aggregate
|
|
34
|
-
* @param operation The operation to perform ('sum', 'avg', 'min', 'max')
|
|
35
49
|
* @param field The field to aggregate on
|
|
36
50
|
* @param query Additional query filtering
|
|
37
51
|
*/
|
|
38
|
-
aggregateFieldByQuery<
|
|
39
|
-
T extends ModelType,
|
|
40
|
-
Op extends AggregateOperation,
|
|
41
|
-
F extends (Op extends AggregateNumericOperation ? ValidNumericFields<T> : ValidComparableFields<T>)
|
|
42
|
-
>(
|
|
52
|
+
aggregateFieldByQuery<T extends ModelType, F extends ValidComparableFields<T>>(
|
|
43
53
|
modelClass: Class<T>,
|
|
44
|
-
operation: Op,
|
|
45
54
|
field: F,
|
|
46
55
|
query?: ModelQuery<T>
|
|
47
|
-
): Promise<
|
|
48
|
-
|
|
49
|
-
/**
|
|
50
|
-
* Find the count of matching documents by query.
|
|
51
|
-
* @param cls The model class
|
|
52
|
-
* @param query The query to count for
|
|
53
|
-
*/
|
|
54
|
-
countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
56
|
+
): Promise<FieldAggregateResult<T, F>>;
|
|
55
57
|
}
|
package/src/types/query.ts
CHANGED
|
@@ -20,5 +20,11 @@ export interface ModelQuerySupport {
|
|
|
20
20
|
* @param query The query to search for
|
|
21
21
|
* @param failOnMany Should the query fail on more than one result found, defaults to true
|
|
22
22
|
*/
|
|
23
|
-
|
|
23
|
+
getByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
|
|
24
|
+
/**
|
|
25
|
+
* Find the count of matching documents by query.
|
|
26
|
+
* @param cls The model class
|
|
27
|
+
* @param query The query to count for
|
|
28
|
+
*/
|
|
29
|
+
countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
24
30
|
}
|
package/src/util/aggregate.ts
CHANGED
|
@@ -2,7 +2,8 @@ import type { ModelType } from '@travetto/model';
|
|
|
2
2
|
import { castTo, type Class, hasFunction } from '@travetto/runtime';
|
|
3
3
|
import { DataUtil, SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type { ValidComparableFields } from '../model/where-clause.ts';
|
|
6
|
+
import type { AggregateResultType, FieldAggregateResult, ModelQueryAggregateSupport } from '../types/aggregate.ts';
|
|
6
7
|
|
|
7
8
|
export class ModelQueryAggregateUtil {
|
|
8
9
|
/**
|
|
@@ -11,19 +12,37 @@ export class ModelQueryAggregateUtil {
|
|
|
11
12
|
static isSupported = hasFunction<ModelQueryAggregateSupport>('aggregateFieldByQuery');
|
|
12
13
|
|
|
13
14
|
/**
|
|
14
|
-
* Resolve and coerce
|
|
15
|
+
* Resolve and coerce aggregate query results
|
|
15
16
|
* @param modelClass The model class being queried
|
|
16
|
-
* @param operation The aggregate operation
|
|
17
17
|
* @param field The field being aggregated
|
|
18
|
-
* @param
|
|
18
|
+
* @param rawValues The raw result values from the datastore
|
|
19
19
|
*/
|
|
20
|
-
static
|
|
20
|
+
static resolveAggregate<T extends ModelType, F extends ValidComparableFields<T>>(
|
|
21
21
|
modelClass: Class<T>,
|
|
22
|
-
operation: Op,
|
|
23
22
|
field: F,
|
|
24
|
-
|
|
25
|
-
):
|
|
23
|
+
rawValues: { count?: unknown; min?: unknown; max?: unknown; avg?: unknown; sum?: unknown }
|
|
24
|
+
): FieldAggregateResult<T, F> {
|
|
25
|
+
const count = Number(rawValues.count ?? 0);
|
|
26
|
+
if (count === 0) {
|
|
27
|
+
return castTo({ count: 0 });
|
|
28
|
+
}
|
|
26
29
|
const leafFieldType = SchemaRegistryIndex.getNestedFieldConfig(modelClass, field)!.type;
|
|
27
|
-
|
|
30
|
+
const coerce = (value: unknown): AggregateResultType<T, F> => castTo(DataUtil.coerceType(value, leafFieldType, false) ?? undefined);
|
|
31
|
+
|
|
32
|
+
if (leafFieldType === Date) {
|
|
33
|
+
return castTo({
|
|
34
|
+
count,
|
|
35
|
+
min: coerce(rawValues.min),
|
|
36
|
+
max: coerce(rawValues.max)
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return castTo({
|
|
41
|
+
count,
|
|
42
|
+
min: coerce(rawValues.min),
|
|
43
|
+
max: coerce(rawValues.max),
|
|
44
|
+
avg: coerce(rawValues.avg),
|
|
45
|
+
sum: coerce(rawValues.sum)
|
|
46
|
+
});
|
|
28
47
|
}
|
|
29
48
|
}
|
|
@@ -10,8 +10,30 @@ import { Aged, BigIntModel, Person } from './model.ts';
|
|
|
10
10
|
|
|
11
11
|
@Suite()
|
|
12
12
|
export abstract class ModelQueryAggregateSuite extends BaseModelSuite<ModelQueryAggregateSupport & ModelCrudSupport> {
|
|
13
|
-
@Test('verify
|
|
14
|
-
async
|
|
13
|
+
@Test('verify date field aggregate')
|
|
14
|
+
async testDateFieldAggregate() {
|
|
15
|
+
const service = await this.service;
|
|
16
|
+
|
|
17
|
+
const firstDate = new Date(2020, 0, 1);
|
|
18
|
+
const secondDate = new Date(2021, 0, 1);
|
|
19
|
+
const thirdDate = new Date(2022, 0, 1);
|
|
20
|
+
|
|
21
|
+
await this.saveAll(Aged, [
|
|
22
|
+
Aged.from({ createdAt: firstDate }),
|
|
23
|
+
Aged.from({ createdAt: secondDate }),
|
|
24
|
+
Aged.from({ createdAt: thirdDate })
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
const aggregate = await service.aggregateFieldByQuery(Aged, 'createdAt');
|
|
28
|
+
assert(aggregate.count === 3);
|
|
29
|
+
assert(aggregate.min instanceof Date);
|
|
30
|
+
assert(aggregate.min.getTime() === firstDate.getTime());
|
|
31
|
+
assert(aggregate.max instanceof Date);
|
|
32
|
+
assert(aggregate.max.getTime() === thirdDate.getTime());
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@Test('verify field aggregate')
|
|
36
|
+
async testFieldAggregate() {
|
|
15
37
|
const people = [
|
|
16
38
|
Person.from({ name: 'Bob', age: 20, gender: 'm', address: { street1: '1st St' } }),
|
|
17
39
|
Person.from({ name: 'Alice', age: 30, gender: 'f', address: { street1: '2nd St' } }),
|
|
@@ -23,67 +45,38 @@ export abstract class ModelQueryAggregateSuite extends BaseModelSuite<ModelQuery
|
|
|
23
45
|
const saved = await this.saveAll(Person, people);
|
|
24
46
|
assert(saved === 4);
|
|
25
47
|
|
|
26
|
-
const
|
|
27
|
-
assert(
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
assert(
|
|
48
|
+
const aggregate = await service.aggregateFieldByQuery(Person, 'age');
|
|
49
|
+
assert(aggregate.count === 4);
|
|
50
|
+
assert(aggregate.min === 20);
|
|
51
|
+
assert(aggregate.max === 50);
|
|
52
|
+
assert(aggregate.avg === 35);
|
|
53
|
+
assert(aggregate.sum === 140);
|
|
31
54
|
|
|
32
|
-
const
|
|
33
|
-
assert(minimumResult === 20);
|
|
34
|
-
|
|
35
|
-
const maximumResult = await service.aggregateFieldByQuery(Person, 'max', 'age');
|
|
36
|
-
assert(maximumResult === 50);
|
|
37
|
-
|
|
38
|
-
// Filtered aggregations
|
|
39
|
-
const filteredSumResult = await service.aggregateFieldByQuery(Person, 'sum', 'age', {
|
|
55
|
+
const filteredAggregate = await service.aggregateFieldByQuery(Person, 'age', {
|
|
40
56
|
where: {
|
|
41
57
|
age: { $gte: 30 }
|
|
42
58
|
}
|
|
43
59
|
});
|
|
44
|
-
assert(
|
|
60
|
+
assert(filteredAggregate.count === 3);
|
|
61
|
+
assert(filteredAggregate.min === 30);
|
|
62
|
+
assert(filteredAggregate.max === 50);
|
|
63
|
+
assert(filteredAggregate.avg === 40);
|
|
64
|
+
assert(filteredAggregate.sum === 120);
|
|
45
65
|
|
|
46
|
-
const
|
|
47
|
-
where: {
|
|
48
|
-
gender: 'f'
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
assert(filteredAverageResult === 40);
|
|
52
|
-
|
|
53
|
-
// No matches
|
|
54
|
-
const noMatchResult = await service.aggregateFieldByQuery(Person, 'sum', 'age', {
|
|
66
|
+
const emptyAggregate = await service.aggregateFieldByQuery(Person, 'age', {
|
|
55
67
|
where: {
|
|
56
68
|
age: { $gt: 1000 }
|
|
57
69
|
}
|
|
58
70
|
});
|
|
59
|
-
assert(
|
|
71
|
+
assert(emptyAggregate.count === 0);
|
|
72
|
+
assert(emptyAggregate.min === undefined);
|
|
73
|
+
assert(emptyAggregate.max === undefined);
|
|
74
|
+
assert(emptyAggregate.avg === undefined);
|
|
75
|
+
assert(emptyAggregate.sum === undefined);
|
|
60
76
|
}
|
|
61
77
|
|
|
62
|
-
@Test('verify
|
|
63
|
-
async
|
|
64
|
-
const service = await this.service;
|
|
65
|
-
|
|
66
|
-
const firstDate = new Date(2020, 0, 1);
|
|
67
|
-
const secondDate = new Date(2021, 0, 1);
|
|
68
|
-
const thirdDate = new Date(2022, 0, 1);
|
|
69
|
-
|
|
70
|
-
await this.saveAll(Aged, [
|
|
71
|
-
Aged.from({ createdAt: firstDate }),
|
|
72
|
-
Aged.from({ createdAt: secondDate }),
|
|
73
|
-
Aged.from({ createdAt: thirdDate })
|
|
74
|
-
]);
|
|
75
|
-
|
|
76
|
-
const minimumDateResult = await service.aggregateFieldByQuery(Aged, 'min', 'createdAt');
|
|
77
|
-
assert(minimumDateResult instanceof Date);
|
|
78
|
-
assert(minimumDateResult.getTime() === firstDate.getTime());
|
|
79
|
-
|
|
80
|
-
const maximumDateResult = await service.aggregateFieldByQuery(Aged, 'max', 'createdAt');
|
|
81
|
-
assert(maximumDateResult instanceof Date);
|
|
82
|
-
assert(maximumDateResult.getTime() === thirdDate.getTime());
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
@Test('verify bigint aggregations')
|
|
86
|
-
async testBigIntAggregate() {
|
|
78
|
+
@Test('verify bigint field aggregate')
|
|
79
|
+
async testBigIntFieldAggregate() {
|
|
87
80
|
const service = await this.service;
|
|
88
81
|
|
|
89
82
|
await this.saveAll(BigIntModel, [
|
|
@@ -92,16 +85,11 @@ export abstract class ModelQueryAggregateSuite extends BaseModelSuite<ModelQuery
|
|
|
92
85
|
BigIntModel.from({ largeNumber: 300n })
|
|
93
86
|
]);
|
|
94
87
|
|
|
95
|
-
const
|
|
96
|
-
assert(
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
assert(
|
|
100
|
-
|
|
101
|
-
const maximumResult = await service.aggregateFieldByQuery(BigIntModel, 'max', 'largeNumber');
|
|
102
|
-
assert(maximumResult === 300n);
|
|
103
|
-
|
|
104
|
-
const averageResult = await service.aggregateFieldByQuery(BigIntModel, 'avg', 'largeNumber');
|
|
105
|
-
assert(averageResult === 200n);
|
|
88
|
+
const aggregate = await service.aggregateFieldByQuery(BigIntModel, 'largeNumber');
|
|
89
|
+
assert(aggregate.count === 3);
|
|
90
|
+
assert(aggregate.min === 100n);
|
|
91
|
+
assert(aggregate.max === 300n);
|
|
92
|
+
assert(aggregate.avg === 200n);
|
|
93
|
+
assert(aggregate.sum === 600n);
|
|
106
94
|
}
|
|
107
95
|
}
|
|
@@ -18,9 +18,7 @@ import { ModelQueryFacetUtil } from '../../src/util/facet.ts';
|
|
|
18
18
|
import { ModelQuerySuggestUtil } from '../../src/util/suggest.ts';
|
|
19
19
|
|
|
20
20
|
@Suite()
|
|
21
|
-
export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<
|
|
22
|
-
ModelQuerySupport & ModelQueryAggregateSupport & ModelCrudSupport
|
|
23
|
-
> {
|
|
21
|
+
export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<ModelQuerySupport & ModelCrudSupport> {
|
|
24
22
|
@Test()
|
|
25
23
|
async testQuery() {
|
|
26
24
|
const svc = await this.service;
|
|
@@ -42,13 +40,13 @@ export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<
|
|
|
42
40
|
assert((await svc.countByQuery(Doctor, { where: { name: 'bob' } })) === 1);
|
|
43
41
|
assert((await svc.countByQuery(Engineer, { where: { name: 'bob' } })) === 0);
|
|
44
42
|
|
|
45
|
-
assert((await svc.
|
|
46
|
-
await assert.rejects(() => svc.
|
|
43
|
+
assert((await svc.getByQuery(Worker, { where: { name: 'bob' } })) instanceof Doctor);
|
|
44
|
+
await assert.rejects(() => svc.getByQuery(Firefighter, { where: { name: 'bob' } }), NotFoundError);
|
|
47
45
|
}
|
|
48
46
|
|
|
49
47
|
@Test({ skip: ModelQueryPolymorphismSuite.ifNot(ModelQueryCrudUtil.isSupported) })
|
|
50
48
|
async testCrudQuery() {
|
|
51
|
-
const svc: ModelQueryCrudSupport &
|
|
49
|
+
const svc: ModelQueryCrudSupport & ModelQuerySupport = castTo(await this.service);
|
|
52
50
|
const [doc, doc2, fire, eng] = [
|
|
53
51
|
Doctor.from({ name: 'bob', specialty: 'feet' }),
|
|
54
52
|
Doctor.from({ name: 'nob', specialty: 'eyes' }),
|
|
@@ -127,10 +125,11 @@ export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<
|
|
|
127
125
|
await this.saveAll(Worker, [doctorOne, doctorTwo, firefighterOne, firefighterTwo]);
|
|
128
126
|
assert((await this.getSize(Worker)) === 4);
|
|
129
127
|
|
|
130
|
-
const
|
|
131
|
-
assert(
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
assert(
|
|
128
|
+
const aggregate = await service.aggregateFieldByQuery(Firefighter, 'firehouse');
|
|
129
|
+
assert(aggregate.count === 2);
|
|
130
|
+
assert(aggregate.min === 20);
|
|
131
|
+
assert(aggregate.max === 30);
|
|
132
|
+
assert(aggregate.avg === 25);
|
|
133
|
+
assert(aggregate.sum === 50);
|
|
135
134
|
}
|
|
136
135
|
}
|
package/support/test/query.ts
CHANGED
|
@@ -6,12 +6,11 @@ import { Suite, Test } from '@travetto/test';
|
|
|
6
6
|
|
|
7
7
|
import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
|
|
8
8
|
|
|
9
|
-
import type { ModelQueryAggregateSupport } from '../../__index__.ts';
|
|
10
9
|
import type { ModelQuerySupport } from '../../src/types/query.ts';
|
|
11
10
|
import { Aged, Location, Names, Note, Person, PersonFamily, Recipe, SimpleList, WithNestedLists, WithNestedNestedLists } from './model.ts';
|
|
12
11
|
|
|
13
12
|
@Suite()
|
|
14
|
-
export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
13
|
+
export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport & ModelCrudSupport> {
|
|
15
14
|
supportsGeo = true;
|
|
16
15
|
|
|
17
16
|
@Test()
|
|
@@ -33,10 +32,10 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
33
32
|
|
|
34
33
|
await this.saveAll(Person, people);
|
|
35
34
|
|
|
36
|
-
const one = await svc.
|
|
35
|
+
const one = await svc.getByQuery(Person, { where: { id: people[0].id } });
|
|
37
36
|
assert(one.id === people[0].id);
|
|
38
37
|
|
|
39
|
-
const one2 = await svc.
|
|
38
|
+
const one2 = await svc.getByQuery(Person, { where: { id: { $eq: people[0].id } } });
|
|
40
39
|
assert(one2.id === people[0].id);
|
|
41
40
|
|
|
42
41
|
const none = await svc.countByQuery(Person, { where: { id: { $ne: people[0].id } } });
|
|
@@ -92,11 +91,11 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
|
|
|
92
91
|
)
|
|
93
92
|
);
|
|
94
93
|
|
|
95
|
-
await assert.rejects(() => service.
|
|
96
|
-
await assert.rejects(() => service.
|
|
97
|
-
await assert.rejects(() => service.
|
|
98
|
-
await assert.rejects(() => service.
|
|
99
|
-
await assert.rejects(() => service.
|
|
94
|
+
await assert.rejects(() => service.getByQuery(Person, { where: { gender: 'm' } }), /Invalid number of results/);
|
|
95
|
+
await assert.rejects(() => service.getByQuery(Person, { where: { gender: 'z' } }), NotFoundError);
|
|
96
|
+
await assert.rejects(() => service.getByQuery(Person, { where: { gender: 'z' } }), /No results found for query/);
|
|
97
|
+
await assert.rejects(() => service.getByQuery(Person, { where: { id: 'orange' } }), NotFoundError);
|
|
98
|
+
await assert.rejects(() => service.getByQuery(Person, { where: { id: 'orange' } }), /orange/);
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
@Test('Verify array $in queries work properly')
|