@travetto/model-query 8.0.2 → 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 CHANGED
@@ -35,13 +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
- queryOne<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
38
+ getByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
39
39
  /**
40
40
  * Find the count of matching documents by query.
41
41
  * @param cls The model class
42
42
  * @param query The query to count for
43
43
  */
44
- queryCount<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
44
+ countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
45
45
  }
46
46
  ```
47
47
 
@@ -128,28 +128,24 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
128
128
  ```
129
129
 
130
130
  ### Aggregate
131
- This contract provides the ability to run strongly-typed field-level aggregations (such as sum, avg, min, and max) on models with optional query filtering.
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.
132
132
 
133
133
  **Code: Query Aggregate**
134
134
  ```typescript
135
135
  export interface ModelQueryAggregateSupport extends ModelQuerySupport {
136
136
  /**
137
- * 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.
138
140
  * @param modelClass The model class to aggregate
139
- * @param operation The operation to perform ('sum', 'avg', 'min', 'max')
140
141
  * @param field The field to aggregate on
141
142
  * @param query Additional query filtering
142
143
  */
143
- aggregateFieldByQuery<
144
- T extends ModelType,
145
- Op extends AggregateOperation,
146
- F extends (Op extends AggregateNumericOperation ? ValidNumericFields<T> : ValidComparableFields<T>)
147
- >(
144
+ aggregateFieldByQuery<T extends ModelType, F extends ValidComparableFields<T>>(
148
145
  modelClass: Class<T>,
149
- operation: Op,
150
146
  field: F,
151
147
  query?: ModelQuery<T>
152
- ): Promise<AggregateResultType<T, F>>;
148
+ ): Promise<FieldAggregateResult<T, F>>;
153
149
  }
154
150
  ```
155
151
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-query",
3
- "version": "8.0.2",
3
+ "version": "8.0.4",
4
4
  "description": "Datastore abstraction for advanced query support.",
5
5
  "keywords": [
6
6
  "datastore",
@@ -31,11 +31,11 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@travetto/di": "^8.0.1",
34
- "@travetto/model": "^8.0.2",
34
+ "@travetto/model": "^8.0.3",
35
35
  "@travetto/schema": "^8.0.2"
36
36
  },
37
37
  "peerDependencies": {
38
- "@travetto/test": "^8.0.1"
38
+ "@travetto/test": "^8.0.2"
39
39
  },
40
40
  "peerDependenciesMeta": {
41
41
  "@travetto/test": {
@@ -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, ValidNumericFields } from '../model/where-clause.ts';
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,26 +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<AggregateResultType<T, F>>;
56
+ ): Promise<FieldAggregateResult<T, F>>;
48
57
  }
@@ -20,11 +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
- queryOne<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
23
+ getByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>, failOnMany?: boolean): Promise<T>;
24
24
  /**
25
25
  * Find the count of matching documents by query.
26
26
  * @param cls The model class
27
27
  * @param query The query to count for
28
28
  */
29
- queryCount<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
29
+ countByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
30
30
  }
@@ -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 { AggregateOperation, AggregateResultType, ModelQueryAggregateSupport } from '../types/aggregate.ts';
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 the aggregate query result using DataUtil.coerceType
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 rawValue The raw result value from the datastore
18
+ * @param rawValues The raw result values from the datastore
19
19
  */
20
- static resolveResult<T extends ModelType, Op extends AggregateOperation, F extends string>(
20
+ static resolveAggregate<T extends ModelType, F extends ValidComparableFields<T>>(
21
21
  modelClass: Class<T>,
22
- operation: Op,
23
22
  field: F,
24
- rawValue: unknown
25
- ): AggregateResultType<T, F> {
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
- return castTo(DataUtil.coerceType(rawValue, leafFieldType, false) ?? undefined);
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 basic aggregations')
14
- async testAggregate() {
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 sumResult = await service.aggregateFieldByQuery(Person, 'sum', 'age');
27
- assert(sumResult === 140);
28
-
29
- const averageResult = await service.aggregateFieldByQuery(Person, 'avg', 'age');
30
- assert(averageResult === 35);
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 minimumResult = await service.aggregateFieldByQuery(Person, 'min', 'age');
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(filteredSumResult === 120);
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 filteredAverageResult = await service.aggregateFieldByQuery(Person, 'avg', 'age', {
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(noMatchResult === undefined);
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 date aggregations')
63
- async testDateAggregate() {
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 sumResult = await service.aggregateFieldByQuery(BigIntModel, 'sum', 'largeNumber');
96
- assert(sumResult === 600n);
97
-
98
- const minimumResult = await service.aggregateFieldByQuery(BigIntModel, 'min', 'largeNumber');
99
- assert(minimumResult === 100n);
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
  }
@@ -1,12 +1,13 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import { Model, type ModelCrudSupport, NotFoundError, UniqueError } from '@travetto/model';
4
- import { castTo } from '@travetto/runtime';
3
+ import { Model, type ModelCrudSupport, type ModelType, NotFoundError, UniqueError } from '@travetto/model';
4
+ import { castTo, type Class } from '@travetto/runtime';
5
5
  import { Suite, Test } from '@travetto/test';
6
6
 
7
7
  import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
8
8
 
9
- import { QueryIndex } from '../../__index__.ts';
9
+ import type { WhereClause } from '../../__index__.ts';
10
+ import { QueryIndex } from '../../src/model/indexes.ts';
10
11
  import type { ModelQueryCrudSupport } from '../../src/types/crud.ts';
11
12
  import { Address, BigIntModel, Person, Todo } from './model.ts';
12
13
 
@@ -25,6 +26,11 @@ class UniqueUser2 {
25
26
  export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudSupport & ModelCrudSupport> {
26
27
  supportsUniqueIndexes = true;
27
28
 
29
+ async countByQuery<T extends ModelType>(cls: Class<T>, query: WhereClause<T>): Promise<number> {
30
+ const svc = await this.service;
31
+ return svc.query(cls, { where: query, limit: 10000 }).then(v => v.length);
32
+ }
33
+
28
34
  @Test({ skip: self => !castTo<ModelQueryCrudSuite>(self).supportsUniqueIndexes })
29
35
  async testUnique() {
30
36
  const svc = await this.service;
@@ -147,13 +153,13 @@ export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudS
147
153
 
148
154
  assert(c === 2);
149
155
 
150
- assert((await svc.queryCount(Person, {})) === 3);
156
+ assert((await this.countByQuery(Person, {})) === 3);
151
157
 
152
158
  const c2 = await svc.deleteByQuery(Person, { where: { age: { $lte: 3 } } });
153
159
 
154
160
  assert(c2 === 3);
155
161
 
156
- assert((await svc.queryCount(Person, {})) === 0);
162
+ assert((await this.countByQuery(Person, {})) === 0);
157
163
  }
158
164
 
159
165
  @Test()
@@ -177,7 +183,7 @@ export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudS
177
183
 
178
184
  assert(count === 5);
179
185
 
180
- assert((await svc.queryCount(Person, { where: { gender: 'm' } })) === 5);
186
+ assert((await this.countByQuery(Person, { gender: 'm' })) === 5);
181
187
 
182
188
  const c = await svc.updatePartialByQuery(Person, { where: { age: { $gt: 3 } } }, { gender: 'f' });
183
189
 
@@ -185,14 +191,14 @@ export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudS
185
191
 
186
192
  assert(c === 2);
187
193
 
188
- assert((await svc.queryCount(Person, { where: { gender: 'm' } })) === 3);
194
+ assert((await this.countByQuery(Person, { gender: 'm' })) === 3);
189
195
 
190
196
  const c2 = await svc.updatePartialByQuery(Person, { where: { gender: 'm' } }, { gender: 'f' });
191
197
 
192
198
  assert(c2 === 3);
193
199
 
194
- assert((await svc.queryCount(Person, { where: { gender: 'f' } })) === 5);
195
- assert((await svc.queryCount(Person, { where: { gender: 'm' } })) === 0);
200
+ assert((await this.countByQuery(Person, { gender: 'f' })) === 5);
201
+ assert((await this.countByQuery(Person, { gender: 'm' })) === 0);
196
202
  }
197
203
 
198
204
  @Test()
@@ -237,7 +243,7 @@ export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudS
237
243
  assert(withOptional.every(x => x.optionalBigInt !== undefined));
238
244
 
239
245
  // Test count with bigint condition
240
- const countResult = await svc.queryCount(BigIntModel, { where: { largeNumber: { $gte: 1000n } } });
246
+ const countResult = await this.countByQuery(BigIntModel, { largeNumber: { $gte: 1000n } });
241
247
  assert(countResult === 2);
242
248
  }
243
249
 
@@ -285,7 +291,7 @@ export abstract class ModelQueryCrudSuite extends BaseModelSuite<ModelQueryCrudS
285
291
  assert(deleted === 2);
286
292
 
287
293
  // Verify deletions
288
- const remaining = await svc.queryCount(BigIntModel, {});
294
+ const remaining = await this.countByQuery(BigIntModel, {});
289
295
  assert(remaining === 3);
290
296
 
291
297
  const all = await svc.query(BigIntModel, {});
@@ -36,12 +36,12 @@ export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<ModelQu
36
36
  assert((await svc.query(Doctor, {})).length === 2);
37
37
  assert((await svc.query(Engineer, {})).length === 1);
38
38
 
39
- assert((await svc.queryCount(Worker, { where: { name: 'bob' } })) === 1);
40
- assert((await svc.queryCount(Doctor, { where: { name: 'bob' } })) === 1);
41
- assert((await svc.queryCount(Engineer, { where: { name: 'bob' } })) === 0);
39
+ assert((await svc.countByQuery(Worker, { where: { name: 'bob' } })) === 1);
40
+ assert((await svc.countByQuery(Doctor, { where: { name: 'bob' } })) === 1);
41
+ assert((await svc.countByQuery(Engineer, { where: { name: 'bob' } })) === 0);
42
42
 
43
- assert((await svc.queryOne(Worker, { where: { name: 'bob' } })) instanceof Doctor);
44
- await assert.rejects(() => svc.queryOne(Firefighter, { where: { name: 'bob' } }), NotFoundError);
43
+ assert((await svc.getByQuery(Worker, { where: { name: 'bob' } })) instanceof Doctor);
44
+ await assert.rejects(() => svc.getByQuery(Firefighter, { where: { name: 'bob' } }), NotFoundError);
45
45
  }
46
46
 
47
47
  @Test({ skip: ModelQueryPolymorphismSuite.ifNot(ModelQueryCrudUtil.isSupported) })
@@ -60,7 +60,7 @@ export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<ModelQu
60
60
  const c = await svc.updatePartialByQuery(Doctor, { where: { specialty: 'feet' } }, { specialty: 'eyes' });
61
61
  assert(c === 1);
62
62
 
63
- assert((await svc.queryCount(Doctor, { where: { specialty: 'eyes' } })) === 2);
63
+ assert((await svc.countByQuery(Doctor, { where: { specialty: 'eyes' } })) === 2);
64
64
 
65
65
  const removed = await svc.deleteByQuery(Worker, { where: { name: 'rob' } });
66
66
 
@@ -125,10 +125,11 @@ export abstract class ModelQueryPolymorphismSuite extends BaseModelSuite<ModelQu
125
125
  await this.saveAll(Worker, [doctorOne, doctorTwo, firefighterOne, firefighterTwo]);
126
126
  assert((await this.getSize(Worker)) === 4);
127
127
 
128
- const sumFirehouse = await service.aggregateFieldByQuery(Firefighter, 'sum', 'firehouse');
129
- assert(sumFirehouse === 50);
130
-
131
- const averageFirehouse = await service.aggregateFieldByQuery(Firefighter, 'avg', 'firehouse');
132
- assert(averageFirehouse === 25);
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);
133
134
  }
134
135
  }
@@ -32,13 +32,13 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
32
32
 
33
33
  await this.saveAll(Person, people);
34
34
 
35
- const one = await svc.queryOne(Person, { where: { id: people[0].id } });
35
+ const one = await svc.getByQuery(Person, { where: { id: people[0].id } });
36
36
  assert(one.id === people[0].id);
37
37
 
38
- const one2 = await svc.queryOne(Person, { where: { id: { $eq: people[0].id } } });
38
+ const one2 = await svc.getByQuery(Person, { where: { id: { $eq: people[0].id } } });
39
39
  assert(one2.id === people[0].id);
40
40
 
41
- const none = await svc.queryCount(Person, { where: { id: { $ne: people[0].id } } });
41
+ const none = await svc.countByQuery(Person, { where: { id: { $ne: people[0].id } } });
42
42
  assert(none === 3);
43
43
 
44
44
  const noneIds = await svc.query(Person, { where: { id: { $ne: people[0].id } } });
@@ -91,11 +91,11 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
91
91
  )
92
92
  );
93
93
 
94
- await assert.rejects(() => service.queryOne(Person, { where: { gender: 'm' } }), /Invalid number of results/);
95
- await assert.rejects(() => service.queryOne(Person, { where: { gender: 'z' } }), NotFoundError);
96
- await assert.rejects(() => service.queryOne(Person, { where: { gender: 'z' } }), /No results found for query/);
97
- await assert.rejects(() => service.queryOne(Person, { where: { id: 'orange' } }), NotFoundError);
98
- await assert.rejects(() => service.queryOne(Person, { where: { id: 'orange' } }), /orange/);
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/);
99
99
  }
100
100
 
101
101
  @Test('Verify array $in queries work properly')
@@ -331,7 +331,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
331
331
 
332
332
  await this.saveAll(Location, toAdd);
333
333
 
334
- assert((await svc.queryCount(Location, {})) === 25);
334
+ assert((await svc.countByQuery(Location, {})) === 25);
335
335
 
336
336
  const result = await svc.query(Location, {
337
337
  limit: 100,
@@ -382,7 +382,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
382
382
  })
383
383
  );
384
384
 
385
- const out = await service.queryCount(Note, {
385
+ const out = await service.countByQuery(Note, {
386
386
  where: {
387
387
  entities: {
388
388
  id
@@ -413,7 +413,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
413
413
  )
414
414
  );
415
415
 
416
- const simple = await service.queryCount(Aged, {
416
+ const simple = await service.countByQuery(Aged, {
417
417
  where: {
418
418
  createdAt: {
419
419
  $gt: new Date()
@@ -422,7 +422,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
422
422
  });
423
423
  assert(simple === 5);
424
424
 
425
- const simple2 = await service.queryCount(Aged, {
425
+ const simple2 = await service.countByQuery(Aged, {
426
426
  where: {
427
427
  createdAt: {
428
428
  $gt: '-1d'
@@ -431,7 +431,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
431
431
  });
432
432
  assert(simple2 === 6);
433
433
 
434
- const simple3 = await service.queryCount(Aged, {
434
+ const simple3 = await service.countByQuery(Aged, {
435
435
  where: {
436
436
  createdAt: {
437
437
  $gt: '-1296m', // -1.9d
@@ -441,7 +441,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
441
441
  });
442
442
  assert(simple3 === 3);
443
443
 
444
- const simple4 = await service.queryCount(Aged, {
444
+ const simple4 = await service.countByQuery(Aged, {
445
445
  where: {
446
446
  createdAt: {
447
447
  $gt: TimeUtil.fromNow('-144m'), // -0.1d
@@ -463,35 +463,35 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
463
463
  WithNestedLists.from({ names: [], tags: [] })
464
464
  ]);
465
465
 
466
- let total = await service.queryCount(WithNestedLists, {
466
+ let total = await service.countByQuery(WithNestedLists, {
467
467
  where: {
468
468
  names: { $exists: true }
469
469
  }
470
470
  });
471
471
  assert(total === 3);
472
472
 
473
- total = await service.queryCount(WithNestedLists, {
473
+ total = await service.countByQuery(WithNestedLists, {
474
474
  where: {
475
475
  names: { $exists: false }
476
476
  }
477
477
  });
478
478
  assert(total === 2);
479
479
 
480
- total = await service.queryCount(WithNestedLists, {
480
+ total = await service.countByQuery(WithNestedLists, {
481
481
  where: {
482
482
  tags: { $exists: false }
483
483
  }
484
484
  });
485
485
  assert(total === 3);
486
486
 
487
- total = await service.queryCount(WithNestedLists, {
487
+ total = await service.countByQuery(WithNestedLists, {
488
488
  where: {
489
489
  tags: { $exists: true }
490
490
  }
491
491
  });
492
492
  assert(total === 2);
493
493
 
494
- total = await service.queryCount(WithNestedLists, {
494
+ total = await service.countByQuery(WithNestedLists, {
495
495
  where: {
496
496
  tags: { $exists: false },
497
497
  names: { $exists: false }
@@ -499,7 +499,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
499
499
  });
500
500
  assert(total === 1);
501
501
 
502
- total = await service.queryCount(WithNestedLists, {
502
+ total = await service.countByQuery(WithNestedLists, {
503
503
  where: {
504
504
  tags: { $exists: true },
505
505
  names: { $exists: true }
@@ -520,35 +520,35 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
520
520
  WithNestedNestedLists.from({ sub: {}, tags: [] })
521
521
  ]);
522
522
 
523
- let total = await service.queryCount(WithNestedNestedLists, {
523
+ let total = await service.countByQuery(WithNestedNestedLists, {
524
524
  where: {
525
525
  sub: { names: { $exists: true } }
526
526
  }
527
527
  });
528
528
  assert(total === 3);
529
529
 
530
- total = await service.queryCount(WithNestedNestedLists, {
530
+ total = await service.countByQuery(WithNestedNestedLists, {
531
531
  where: {
532
532
  sub: { names: { $exists: false } }
533
533
  }
534
534
  });
535
535
  assert(total === 2);
536
536
 
537
- total = await service.queryCount(WithNestedNestedLists, {
537
+ total = await service.countByQuery(WithNestedNestedLists, {
538
538
  where: {
539
539
  tags: { $exists: false }
540
540
  }
541
541
  });
542
542
  assert(total === 3);
543
543
 
544
- total = await service.queryCount(WithNestedNestedLists, {
544
+ total = await service.countByQuery(WithNestedNestedLists, {
545
545
  where: {
546
546
  tags: { $exists: true }
547
547
  }
548
548
  });
549
549
  assert(total === 2);
550
550
 
551
- total = await service.queryCount(WithNestedNestedLists, {
551
+ total = await service.countByQuery(WithNestedNestedLists, {
552
552
  where: {
553
553
  tags: { $exists: false },
554
554
  sub: { names: { $exists: false } }
@@ -556,7 +556,7 @@ export abstract class ModelQuerySuite extends BaseModelSuite<ModelQuerySupport &
556
556
  });
557
557
  assert(total === 1);
558
558
 
559
- total = await service.queryCount(WithNestedNestedLists, {
559
+ total = await service.countByQuery(WithNestedNestedLists, {
560
560
  where: {
561
561
  tags: { $exists: true },
562
562
  sub: { names: { $exists: true } }