@travetto/model-query 8.0.4 → 8.0.5

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
@@ -141,7 +141,7 @@ export interface ModelQueryAggregateSupport extends ModelQuerySupport {
141
141
  * @param field The field to aggregate on
142
142
  * @param query Additional query filtering
143
143
  */
144
- aggregateFieldByQuery<T extends ModelType, F extends ValidComparableFields<T>>(
144
+ aggregateFieldByQuery<T extends ModelType, const F extends ValidComparableFields<T>>(
145
145
  modelClass: Class<T>,
146
146
  field: F,
147
147
  query?: ModelQuery<T>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@travetto/model-query",
3
- "version": "8.0.4",
3
+ "version": "8.0.5",
4
4
  "description": "Datastore abstraction for advanced query support.",
5
5
  "keywords": [
6
6
  "datastore",
@@ -16,7 +16,7 @@ export type PathType<T, Path extends string> = Path extends `${infer Head}.${inf
16
16
  : never;
17
17
 
18
18
  export type AggregateResultType<T, F extends string> = [PathType<T, F>] extends [never]
19
- ? number | Date | bigint | undefined
19
+ ? number | bigint | undefined
20
20
  : PathType<T, F> | undefined;
21
21
 
22
22
  export type DateFieldAggregateResult = {
@@ -25,16 +25,29 @@ export type DateFieldAggregateResult = {
25
25
  max?: Date;
26
26
  };
27
27
 
28
- export type NumericFieldAggregateResult<T, F extends string> = {
28
+ export type NumberFieldAggregateResult = {
29
29
  count: number;
30
- min?: AggregateResultType<T, F>;
31
- max?: AggregateResultType<T, F>;
32
- avg?: AggregateResultType<T, F>;
33
- sum?: AggregateResultType<T, F>;
30
+ min?: number;
31
+ max?: number;
32
+ avg?: number;
33
+ sum?: number;
34
34
  };
35
35
 
36
- export type FieldAggregateResult<T, F extends string> =
37
- NonNullable<PathType<T, F>> extends Date ? DateFieldAggregateResult : NumericFieldAggregateResult<T, F>;
36
+ export type BigIntFieldAggregateResult = {
37
+ count: number;
38
+ min?: bigint;
39
+ max?: bigint;
40
+ avg?: bigint;
41
+ sum?: bigint;
42
+ };
43
+
44
+ export type FieldAggregateResult<T, F extends string> = F extends any
45
+ ? NonNullable<PathType<T, F>> extends Date
46
+ ? DateFieldAggregateResult
47
+ : NonNullable<PathType<T, F>> extends bigint
48
+ ? BigIntFieldAggregateResult
49
+ : NumberFieldAggregateResult
50
+ : never;
38
51
 
39
52
  /**
40
53
  * The contract for a model service with aggregate support
@@ -49,7 +62,7 @@ export interface ModelQueryAggregateSupport extends ModelQuerySupport {
49
62
  * @param field The field to aggregate on
50
63
  * @param query Additional query filtering
51
64
  */
52
- aggregateFieldByQuery<T extends ModelType, F extends ValidComparableFields<T>>(
65
+ aggregateFieldByQuery<T extends ModelType, const F extends ValidComparableFields<T>>(
53
66
  modelClass: Class<T>,
54
67
  field: F,
55
68
  query?: ModelQuery<T>
@@ -3,7 +3,7 @@ import { castTo, type Class, hasFunction } from '@travetto/runtime';
3
3
  import { DataUtil, SchemaRegistryIndex } from '@travetto/schema';
4
4
 
5
5
  import type { ValidComparableFields } from '../model/where-clause.ts';
6
- import type { AggregateResultType, FieldAggregateResult, ModelQueryAggregateSupport } from '../types/aggregate.ts';
6
+ import type { FieldAggregateResult, ModelQueryAggregateSupport, NumberFieldAggregateResult } from '../types/aggregate.ts';
7
7
 
8
8
  export class ModelQueryAggregateUtil {
9
9
  /**
@@ -17,32 +17,31 @@ export class ModelQueryAggregateUtil {
17
17
  * @param field The field being aggregated
18
18
  * @param rawValues The raw result values from the datastore
19
19
  */
20
- static resolveAggregate<T extends ModelType, F extends ValidComparableFields<T>>(
20
+ static resolveAggregate<T extends ModelType, const F extends ValidComparableFields<T>>(
21
21
  modelClass: Class<T>,
22
22
  field: F,
23
- rawValues: { count?: unknown; min?: unknown; max?: unknown; avg?: unknown; sum?: unknown }
23
+ rawValues: Partial<Record<keyof NumberFieldAggregateResult, unknown>>
24
24
  ): FieldAggregateResult<T, F> {
25
25
  const count = Number(rawValues.count ?? 0);
26
26
  if (count === 0) {
27
27
  return castTo({ count: 0 });
28
28
  }
29
29
  const leafFieldType = SchemaRegistryIndex.getNestedFieldConfig(modelClass, field)!.type;
30
- const coerce = (value: unknown): AggregateResultType<T, F> => castTo(DataUtil.coerceType(value, leafFieldType, false) ?? undefined);
31
30
 
32
31
  if (leafFieldType === Date) {
33
32
  return castTo({
34
33
  count,
35
- min: coerce(rawValues.min),
36
- max: coerce(rawValues.max)
34
+ min: DataUtil.coerceType(rawValues.min, leafFieldType, true),
35
+ max: DataUtil.coerceType(rawValues.max, leafFieldType, true)
37
36
  });
38
37
  }
39
38
 
40
39
  return castTo({
41
40
  count,
42
- min: coerce(rawValues.min),
43
- max: coerce(rawValues.max),
44
- avg: coerce(rawValues.avg),
45
- sum: coerce(rawValues.sum)
41
+ min: DataUtil.coerceType(rawValues.min, leafFieldType, true),
42
+ max: DataUtil.coerceType(rawValues.max, leafFieldType, true),
43
+ avg: DataUtil.coerceType(rawValues.avg, leafFieldType, true),
44
+ sum: DataUtil.coerceType(rawValues.sum, leafFieldType, true)
46
45
  });
47
46
  }
48
47
  }
@@ -1,12 +1,12 @@
1
1
  import assert from 'node:assert';
2
2
 
3
- import type { ModelCrudSupport } from '@travetto/model';
3
+ import type { ModelCrudSupport, ModelType } from '@travetto/model';
4
4
  import { Suite, Test } from '@travetto/test';
5
5
 
6
6
  import { BaseModelSuite } from '@travetto/model/support/test/base.ts';
7
7
 
8
- import type { ModelQueryAggregateSupport } from '../../src/types/aggregate.ts';
9
- import { Aged, BigIntModel, Person } from './model.ts';
8
+ import type { DateFieldAggregateResult, ModelQueryAggregateSupport, NumberFieldAggregateResult } from '../../src/types/aggregate.ts';
9
+ import { Aged, BigIntModel, MultiFieldModel, Person } from './model.ts';
10
10
 
11
11
  @Suite()
12
12
  export abstract class ModelQueryAggregateSuite extends BaseModelSuite<ModelQueryAggregateSupport & ModelCrudSupport> {
@@ -32,6 +32,27 @@ export abstract class ModelQueryAggregateSuite extends BaseModelSuite<ModelQuery
32
32
  assert(aggregate.max.getTime() === thirdDate.getTime());
33
33
  }
34
34
 
35
+ @Test('verify model with multiple field types aggregate')
36
+ async testMultiFieldModelAggregate() {
37
+ const service = await this.service;
38
+ const now = new Date();
39
+ await this.saveAll(MultiFieldModel, [
40
+ MultiFieldModel.from({ createdAt: now, score: 10 }),
41
+ MultiFieldModel.from({ createdAt: now, score: 20 })
42
+ ]);
43
+
44
+ const dateStats = await service.aggregateFieldByQuery(MultiFieldModel, 'createdAt');
45
+ const dateCheck: DateFieldAggregateResult = dateStats;
46
+ assert(dateCheck.count === 2);
47
+ assert(dateCheck.min instanceof Date);
48
+
49
+ const numberStats = await service.aggregateFieldByQuery(MultiFieldModel, 'score');
50
+ const numberCheck: NumberFieldAggregateResult = numberStats;
51
+ assert(numberCheck.count === 2);
52
+ assert(numberCheck.min === 10);
53
+ assert(numberCheck.avg === 15);
54
+ }
55
+
35
56
  @Test('verify field aggregate')
36
57
  async testFieldAggregate() {
37
58
  const people = [
@@ -74,6 +74,13 @@ export class Aged {
74
74
  createdAt: Date;
75
75
  }
76
76
 
77
+ @Model()
78
+ export class MultiFieldModel {
79
+ id: string;
80
+ createdAt: Date;
81
+ score: number;
82
+ }
83
+
77
84
  @Model()
78
85
  export class WithNestedLists {
79
86
  id: string;