@travetto/model-query 8.0.0-alpha.23 → 8.0.0-alpha.24
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 +12 -8
- package/src/verifier.ts +24 -30
- package/support/doc.support.tsx +6 -7
- package/support/test/crud.ts +70 -56
- package/support/test/facet.ts +21 -13
- package/support/test/model.ts +2 -2
- package/support/test/polymorphism.ts +14 -16
- package/support/test/query.ts +106 -74
- package/support/test/suggest.ts +4 -4
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @travetto/model-query
|
|
|
13
13
|
yarn add @travetto/model-query
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
This module provides an enhanced query contract for [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations.
|
|
16
|
+
This module provides an enhanced query contract for [Data Modeling Support](https://github.com/travetto/travetto/tree/main/module/model#readme "Datastore abstraction for core operations.") implementations. This contract has been externalized due to it being more complex than many implementations can natively support.
|
|
17
17
|
|
|
18
18
|
## Contracts
|
|
19
19
|
|
|
@@ -46,7 +46,7 @@ export interface ModelQuerySupport {
|
|
|
46
46
|
```
|
|
47
47
|
|
|
48
48
|
### Crud
|
|
49
|
-
Reinforcing the complexity provided in these contracts, the [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/types/crud.ts#L11) contract allows for bulk update/deletion by query.
|
|
49
|
+
Reinforcing the complexity provided in these contracts, the [Query Crud](https://github.com/travetto/travetto/tree/main/module/model-query/src/types/crud.ts#L11) contract allows for bulk update/deletion by query. This requires the underlying implementation to support these operations.
|
|
50
50
|
|
|
51
51
|
**Code: Query Crud**
|
|
52
52
|
```typescript
|
|
@@ -56,7 +56,7 @@ export interface ModelQueryCrudSupport extends ModelCrudSupport, ModelQuerySuppo
|
|
|
56
56
|
* @param cls The model class
|
|
57
57
|
* @param data The data
|
|
58
58
|
* @param query The additional query to validate
|
|
59
|
-
|
|
59
|
+
*/
|
|
60
60
|
updateByQuery<T extends ModelType>(cls: Class<T>, data: T, query: ModelQuery<T>): Promise<T>;
|
|
61
61
|
/**
|
|
62
62
|
* Update all with partial data, by query
|
|
@@ -104,7 +104,12 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
104
104
|
* @param prefix The search prefix for the given field
|
|
105
105
|
* @param query A query to filter the search on, in addition to the prefix
|
|
106
106
|
*/
|
|
107
|
-
suggestByQuery<T extends ModelType>(
|
|
107
|
+
suggestByQuery<T extends ModelType>(
|
|
108
|
+
cls: Class<T>,
|
|
109
|
+
field: ValidStringFields<T>,
|
|
110
|
+
prefix?: string,
|
|
111
|
+
query?: PageableModelQuery<T>
|
|
112
|
+
): Promise<T[]>;
|
|
108
113
|
/**
|
|
109
114
|
* Suggest distinct values for a given cls and a given field
|
|
110
115
|
*
|
|
@@ -113,7 +118,12 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
113
118
|
* @param prefix The search prefix for the given field
|
|
114
119
|
* @param query A query to filter the search on, in addition to the prefix
|
|
115
120
|
*/
|
|
116
|
-
suggestValuesByQuery<T extends ModelType>(
|
|
121
|
+
suggestValuesByQuery<T extends ModelType>(
|
|
122
|
+
cls: Class<T>,
|
|
123
|
+
field: ValidStringFields<T>,
|
|
124
|
+
prefix?: string,
|
|
125
|
+
query?: PageableModelQuery<T>
|
|
126
|
+
): Promise<string[]>;
|
|
117
127
|
}
|
|
118
128
|
```
|
|
119
129
|
|
|
@@ -125,7 +135,7 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
125
135
|
|[SQL Model Service](https://github.com/travetto/travetto/tree/main/module/model-sql#readme "SQL backing for the travetto model module, with real-time modeling support for SQL schemas.")|X'|X'|X'|
|
|
126
136
|
|
|
127
137
|
## Querying
|
|
128
|
-
One of the complexities of abstracting multiple storage mechanisms, is providing a consistent query language.
|
|
138
|
+
One of the complexities of abstracting multiple storage mechanisms, is providing a consistent query language. The query language the module uses is a derivation of [mongodb](https://mongodb.com)'s query language, with some restrictions, additions, and caveats. Additionally, given the nature of typescript, all queries are statically typed, and will catch type errors at compile time.
|
|
129
139
|
|
|
130
140
|
### General Fields
|
|
131
141
|
|
|
@@ -153,7 +163,7 @@ One of the complexities of abstracting multiple storage mechanisms, is providing
|
|
|
153
163
|
* `field: { $gt: Date | RelativeTime }` checks if value is greater than
|
|
154
164
|
* `field: { $gte: Date | RelativeTime }` checks if value is greater than or equal to
|
|
155
165
|
|
|
156
|
-
**Note**: Relative times are strings consisting of a number and a unit.
|
|
166
|
+
**Note**: Relative times are strings consisting of a number and a unit. e.g. -1w or 30d. These times are always relative to Date.now, but should make building queries more natural.
|
|
157
167
|
|
|
158
168
|
### Array Fields
|
|
159
169
|
|
|
@@ -179,6 +189,7 @@ A sample query for `User`'s might be:
|
|
|
179
189
|
**Code: Using the query structure for specific queries**
|
|
180
190
|
```typescript
|
|
181
191
|
import type { ModelQuerySupport } from '@travetto/model-query';
|
|
192
|
+
|
|
182
193
|
import { User } from './user.ts';
|
|
183
194
|
|
|
184
195
|
export class UserSearch {
|
|
@@ -214,25 +225,24 @@ In addition to the provided contracts, the module also provides common utilities
|
|
|
214
225
|
|
|
215
226
|
**Code: MongoDB Service Test Configuration**
|
|
216
227
|
```typescript
|
|
217
|
-
import { Suite } from '@travetto/test';
|
|
218
228
|
import { Config } from '@travetto/config';
|
|
219
229
|
import { Injectable } from '@travetto/di';
|
|
220
|
-
import type { ModelQueryFacetSupport, ModelQuerySuggestSupport
|
|
230
|
+
import type { ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySuggestSupport } from '@travetto/model-query';
|
|
231
|
+
import { Suite } from '@travetto/test';
|
|
221
232
|
|
|
222
|
-
import { ModelQuerySuite } from '@travetto/model-query/support/test/query.ts';
|
|
223
233
|
import { ModelQueryCrudSuite } from '@travetto/model-query/support/test/crud.ts';
|
|
224
234
|
import { ModelQueryFacetSuite } from '@travetto/model-query/support/test/facet.ts';
|
|
225
235
|
import { ModelQueryPolymorphismSuite } from '@travetto/model-query/support/test/polymorphism.ts';
|
|
236
|
+
import { ModelQuerySuite } from '@travetto/model-query/support/test/query.ts';
|
|
226
237
|
import { ModelQuerySuggestSuite } from '@travetto/model-query/support/test/suggest.ts';
|
|
227
238
|
|
|
228
239
|
import { QueryModelService } from './query-service.ts';
|
|
229
240
|
|
|
230
241
|
@Config('model.custom')
|
|
231
|
-
class CustomModelConfig {
|
|
242
|
+
class CustomModelConfig {}
|
|
232
243
|
|
|
233
244
|
@Injectable()
|
|
234
|
-
class CustomModelService extends QueryModelService implements ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySuggestSupport {
|
|
235
|
-
}
|
|
245
|
+
class CustomModelService extends QueryModelService implements ModelQueryCrudSupport, ModelQueryFacetSupport, ModelQuerySuggestSupport {}
|
|
236
246
|
|
|
237
247
|
@Suite()
|
|
238
248
|
class CustomQuerySuite extends ModelQuerySuite {
|
package/__index__.ts
CHANGED
|
@@ -1,15 +1,12 @@
|
|
|
1
|
+
export * from './src/model/indexes.ts';
|
|
1
2
|
export * from './src/model/query.ts';
|
|
2
3
|
export * from './src/model/where-clause.ts';
|
|
3
|
-
export * from './src/model/indexes.ts';
|
|
4
4
|
export * from './src/types/crud.ts';
|
|
5
|
-
export * from './src/types/query.ts';
|
|
6
5
|
export * from './src/types/facet.ts';
|
|
6
|
+
export * from './src/types/query.ts';
|
|
7
7
|
export * from './src/types/suggest.ts';
|
|
8
|
-
|
|
8
|
+
export * from './src/util/crud.ts';
|
|
9
|
+
export * from './src/util/facet.ts';
|
|
9
10
|
export * from './src/util/query.ts';
|
|
10
11
|
export * from './src/util/suggest.ts';
|
|
11
|
-
export * from './src/
|
|
12
|
-
export * from './src/util/crud.ts';
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
export * from './src/verifier.ts';
|
|
12
|
+
export * from './src/verifier.ts';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-query",
|
|
3
|
-
"version": "8.0.0-alpha.
|
|
3
|
+
"version": "8.0.0-alpha.24",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Datastore abstraction for advanced query support.",
|
|
6
6
|
"keywords": [
|
|
@@ -27,12 +27,12 @@
|
|
|
27
27
|
"directory": "module/model-query"
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@travetto/di": "^8.0.0-alpha.
|
|
31
|
-
"@travetto/model": "^8.0.0-alpha.
|
|
32
|
-
"@travetto/schema": "^8.0.0-alpha.
|
|
30
|
+
"@travetto/di": "^8.0.0-alpha.20",
|
|
31
|
+
"@travetto/model": "^8.0.0-alpha.23",
|
|
32
|
+
"@travetto/schema": "^8.0.0-alpha.22"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@travetto/test": "^8.0.0-alpha.
|
|
35
|
+
"@travetto/test": "^8.0.0-alpha.21"
|
|
36
36
|
},
|
|
37
37
|
"peerDependenciesMeta": {
|
|
38
38
|
"@travetto/test": {
|
package/src/internal/types.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import {
|
|
1
|
+
import { type Class, castTo, toConcrete } from '@travetto/runtime';
|
|
2
|
+
import type { Point, SchemaFieldConfig } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
const st = (value: string | string[], isArray: boolean = false): Set<string> =>
|
|
5
|
-
new Set((Array.isArray(value) ? value : [value]).map(item => isArray ? `${item}[]` : item));
|
|
5
|
+
new Set((Array.isArray(value) ? value : [value]).map(item => (isArray ? `${item}[]` : item)));
|
|
6
6
|
|
|
7
7
|
const basic = (types: Set<string>): Record<string, Set<string>> => ({ $ne: types, $eq: types, $exists: st('boolean') });
|
|
8
8
|
const scalar = (types: Set<string>): Record<string, Set<string>> => ({ $in: types, $nin: types });
|
|
@@ -42,12 +42,18 @@ export class TypeUtil {
|
|
|
42
42
|
static getDeclaredType(field: SchemaFieldConfig | Function | Class): keyof typeof TypeUtil.OPERATORS | undefined {
|
|
43
43
|
const type = 'type' in field ? field.type : field;
|
|
44
44
|
switch (type) {
|
|
45
|
-
case String:
|
|
46
|
-
|
|
47
|
-
case
|
|
48
|
-
|
|
49
|
-
case
|
|
50
|
-
|
|
45
|
+
case String:
|
|
46
|
+
return 'string';
|
|
47
|
+
case Number:
|
|
48
|
+
return 'number';
|
|
49
|
+
case Boolean:
|
|
50
|
+
return 'boolean';
|
|
51
|
+
case BigInt:
|
|
52
|
+
return 'bigint';
|
|
53
|
+
case Date:
|
|
54
|
+
return 'Date';
|
|
55
|
+
case PointConcrete:
|
|
56
|
+
return 'Point';
|
|
51
57
|
default: {
|
|
52
58
|
if ('type' in field && field.array) {
|
|
53
59
|
return this.getDeclaredType(field.type);
|
|
@@ -77,4 +83,4 @@ export class TypeUtil {
|
|
|
77
83
|
}
|
|
78
84
|
throw new Error(`Unknown type for ${value}`);
|
|
79
85
|
}
|
|
80
|
-
}
|
|
86
|
+
}
|
package/src/model/indexes.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { type IndexConfig, ModelRegistryIndex, type ModelType } from '@travetto/model';
|
|
2
|
+
import { type Class, type Primitive, RuntimeError, type ValidFields } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
type RetainPrimitiveFields<T> = Pick<T, ValidFields<T, Primitive | Date>>;
|
|
5
5
|
|
|
6
6
|
type IndexClauseRaw<T> = {
|
|
7
|
-
[P in keyof T]?:
|
|
8
|
-
T[P] extends object ? IndexClauseRaw<RetainPrimitiveFields<T[P]>> : 1 | -1 | true;
|
|
7
|
+
[P in keyof T]?: T[P] extends object ? IndexClauseRaw<RetainPrimitiveFields<T[P]>> : 1 | -1 | true;
|
|
9
8
|
};
|
|
10
9
|
|
|
11
10
|
export type IndexField<T extends ModelType> = IndexClauseRaw<RetainPrimitiveFields<T>>;
|
|
@@ -34,4 +33,4 @@ export function QueryIndex<T extends ModelType>(index: Omit<QueryIndexConfig<T>,
|
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
export const isModelQueryIndex = (idx: unknown): idx is QueryIndexConfig<ModelType> =>
|
|
37
|
-
typeof idx === 'object' && idx !== null && 'type' in idx && typeof idx.type === 'string' && idx.type === 'query';
|
|
36
|
+
typeof idx === 'object' && idx !== null && 'type' in idx && typeof idx.type === 'string' && idx.type === 'query';
|
package/src/model/query.ts
CHANGED
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { RetainQueryPrimitiveFields, WhereClauseRaw } from './where-clause.ts';
|
|
2
2
|
|
|
3
3
|
type SelectClauseRaw<T> = {
|
|
4
|
-
[P in keyof T]?:
|
|
5
|
-
T[P] extends object ? SelectClauseRaw<RetainQueryPrimitiveFields<T[P]>> : (1 | 0 | boolean);
|
|
4
|
+
[P in keyof T]?: T[P] extends object ? SelectClauseRaw<RetainQueryPrimitiveFields<T[P]>> : 1 | 0 | boolean;
|
|
6
5
|
};
|
|
7
6
|
|
|
8
7
|
type GroupClauseRaw<T> = {
|
|
9
|
-
[P in keyof T]?: T[P] extends object ? GroupClauseRaw<RetainQueryPrimitiveFields<T[P]>> :
|
|
8
|
+
[P in keyof T]?: T[P] extends object ? GroupClauseRaw<RetainQueryPrimitiveFields<T[P]>> : 1 | 0 | boolean;
|
|
10
9
|
};
|
|
11
10
|
|
|
12
11
|
type SortClauseRaw<T> = {
|
|
13
|
-
[P in keyof T]?:
|
|
14
|
-
T[P] extends object ? SortClauseRaw<RetainQueryPrimitiveFields<T[P]>> : 1 | -1;
|
|
12
|
+
[P in keyof T]?: T[P] extends object ? SortClauseRaw<RetainQueryPrimitiveFields<T[P]>> : 1 | -1;
|
|
15
13
|
};
|
|
16
14
|
|
|
17
15
|
type QueryOptionsRaw<T> = {
|
|
@@ -58,4 +56,4 @@ export type GroupClause<T> = GroupClauseRaw<RetainQueryPrimitiveFields<T>>;
|
|
|
58
56
|
/**
|
|
59
57
|
* Model query, requiring a model object
|
|
60
58
|
*/
|
|
61
|
-
export type ModelQuery<T> = ModelQueryRaw<RetainQueryPrimitiveFields<T>>;
|
|
59
|
+
export type ModelQuery<T> = ModelQueryRaw<RetainQueryPrimitiveFields<T>>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { KeyPaths, Primitive, TimeSpan, ValidFields } from '@travetto/runtime';
|
|
2
2
|
import type { Point } from '@travetto/schema';
|
|
3
3
|
|
|
4
4
|
export type QueryPrimitive = Primitive | Date | Point;
|
|
@@ -26,13 +26,14 @@ type ComparableField<T> = {
|
|
|
26
26
|
};
|
|
27
27
|
|
|
28
28
|
type ArrayField<T> =
|
|
29
|
-
{ $exists?: boolean }
|
|
30
|
-
{ $eq?: T | T[] }
|
|
31
|
-
{ $ne?: T | T[] }
|
|
32
|
-
{ $all?: T[] }
|
|
33
|
-
{ $in?: T[] }
|
|
34
|
-
PropWhereClause<RetainQueryPrimitiveFields<T>>
|
|
35
|
-
|
|
29
|
+
| { $exists?: boolean }
|
|
30
|
+
| { $eq?: T | T[] }
|
|
31
|
+
| { $ne?: T | T[] }
|
|
32
|
+
| { $all?: T[] }
|
|
33
|
+
| { $in?: T[] }
|
|
34
|
+
| PropWhereClause<RetainQueryPrimitiveFields<T>>
|
|
35
|
+
| T
|
|
36
|
+
| T[];
|
|
36
37
|
|
|
37
38
|
type StringField = { $regex?: RegExp | string };
|
|
38
39
|
|
|
@@ -44,25 +45,33 @@ type GeoField = {
|
|
|
44
45
|
};
|
|
45
46
|
|
|
46
47
|
export type PropWhereClause<T> = {
|
|
47
|
-
[P in keyof T]?:
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
48
|
+
[P in keyof T]?: T[P] extends number | undefined
|
|
49
|
+
? General<number> | ScalarField<number> | ComparableField<number> | number
|
|
50
|
+
: T[P] extends bigint | undefined
|
|
51
|
+
? General<bigint> | ScalarField<bigint> | ComparableField<bigint> | bigint
|
|
52
|
+
: T[P] extends string | undefined
|
|
53
|
+
? General<string> | ScalarField<string> | StringField | string
|
|
54
|
+
: T[P] extends boolean | undefined
|
|
55
|
+
? General<boolean> | boolean
|
|
56
|
+
: T[P] extends Date | undefined
|
|
57
|
+
? General<Date> | ScalarField<Date> | ComparableField<Date | TimeSpan> | Date
|
|
58
|
+
: T[P] extends Point | undefined
|
|
59
|
+
? General<Point> | ScalarField<Point> | GeoField | Point
|
|
60
|
+
: T[P] extends (infer U)[] | undefined
|
|
61
|
+
? ArrayField<U>
|
|
62
|
+
: T[P] extends object | undefined
|
|
63
|
+
? PropWhereClause<RetainQueryPrimitiveFields<T[P]>>
|
|
64
|
+
: never;
|
|
56
65
|
};
|
|
57
66
|
|
|
58
67
|
/**
|
|
59
68
|
* Raw query type
|
|
60
69
|
*/
|
|
61
70
|
export type WhereClauseRaw<T> =
|
|
62
|
-
({ $and: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
63
|
-
({ $or: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
64
|
-
({ $not: WhereClauseRaw<T> } & { [P in keyof T]?: never })
|
|
65
|
-
(PropWhereClause<T> & { $and?: never
|
|
71
|
+
| ({ $and: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
72
|
+
| ({ $or: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
73
|
+
| ({ $not: WhereClauseRaw<T> } & { [P in keyof T]?: never })
|
|
74
|
+
| (PropWhereClause<T> & { $and?: never; $or?: never; $not?: never });
|
|
66
75
|
|
|
67
76
|
/**
|
|
68
77
|
* Full where clause, typed against the input type T
|
package/src/types/crud.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelCrudSupport, ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
|
-
import type { ModelQuerySupport } from './query.ts';
|
|
5
4
|
import type { ModelQuery } from '../model/query.ts';
|
|
5
|
+
import type { ModelQuerySupport } from './query.ts';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* The contract for a model service with query support
|
|
@@ -14,7 +14,7 @@ export interface ModelQueryCrudSupport extends ModelCrudSupport, ModelQuerySuppo
|
|
|
14
14
|
* @param cls The model class
|
|
15
15
|
* @param data The data
|
|
16
16
|
* @param query The additional query to validate
|
|
17
|
-
|
|
17
|
+
*/
|
|
18
18
|
updateByQuery<T extends ModelType>(cls: Class<T>, data: T, query: ModelQuery<T>): Promise<T>;
|
|
19
19
|
/**
|
|
20
20
|
* Update all with partial data, by query
|
|
@@ -29,4 +29,4 @@ export interface ModelQueryCrudSupport extends ModelCrudSupport, ModelQuerySuppo
|
|
|
29
29
|
* @param query Query to search for deletable elements
|
|
30
30
|
*/
|
|
31
31
|
deleteByQuery<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
32
|
-
}
|
|
32
|
+
}
|
package/src/types/facet.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
import type { ModelQuery } from '../model/query.ts';
|
|
5
|
-
import type { ModelQuerySupport } from './query.ts';
|
|
6
5
|
import type { ValidStringFields } from '../model/where-clause.ts';
|
|
6
|
+
import type { ModelQuerySupport } from './query.ts';
|
|
7
7
|
|
|
8
|
-
export type ModelQueryFacet = { key: string
|
|
8
|
+
export type ModelQueryFacet = { key: string; count: number };
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* The contract for a model service with faceting support
|
|
@@ -19,4 +19,4 @@ export interface ModelQueryFacetSupport extends ModelQuerySupport {
|
|
|
19
19
|
* @param query Additional query filtering
|
|
20
20
|
*/
|
|
21
21
|
facetByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<ModelQueryFacet[]>;
|
|
22
|
-
}
|
|
22
|
+
}
|
package/src/types/query.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
import type { ModelQuery, PageableModelQuery } from '../model/query.ts';
|
|
5
5
|
|
|
@@ -27,4 +27,4 @@ export interface ModelQuerySupport {
|
|
|
27
27
|
* @param query The query to count for
|
|
28
28
|
*/
|
|
29
29
|
queryCount<T extends ModelType>(cls: Class<T>, query: ModelQuery<T>): Promise<number>;
|
|
30
|
-
}
|
|
30
|
+
}
|
package/src/types/suggest.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { Class } from '@travetto/runtime';
|
|
2
1
|
import type { ModelType } from '@travetto/model';
|
|
2
|
+
import type { Class } from '@travetto/runtime';
|
|
3
3
|
|
|
4
4
|
import type { PageableModelQuery } from '../model/query.ts';
|
|
5
|
-
import type { ModelQuerySupport } from './query.ts';
|
|
6
5
|
import type { ValidStringFields } from '../model/where-clause.ts';
|
|
6
|
+
import type { ModelQuerySupport } from './query.ts';
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* The contract for a model service with suggesting support
|
|
@@ -18,7 +18,12 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
18
18
|
* @param prefix The search prefix for the given field
|
|
19
19
|
* @param query A query to filter the search on, in addition to the prefix
|
|
20
20
|
*/
|
|
21
|
-
suggestByQuery<T extends ModelType>(
|
|
21
|
+
suggestByQuery<T extends ModelType>(
|
|
22
|
+
cls: Class<T>,
|
|
23
|
+
field: ValidStringFields<T>,
|
|
24
|
+
prefix?: string,
|
|
25
|
+
query?: PageableModelQuery<T>
|
|
26
|
+
): Promise<T[]>;
|
|
22
27
|
/**
|
|
23
28
|
* Suggest distinct values for a given cls and a given field
|
|
24
29
|
*
|
|
@@ -27,5 +32,10 @@ export interface ModelQuerySuggestSupport extends ModelQuerySupport {
|
|
|
27
32
|
* @param prefix The search prefix for the given field
|
|
28
33
|
* @param query A query to filter the search on, in addition to the prefix
|
|
29
34
|
*/
|
|
30
|
-
suggestValuesByQuery<T extends ModelType>(
|
|
31
|
-
|
|
35
|
+
suggestValuesByQuery<T extends ModelType>(
|
|
36
|
+
cls: Class<T>,
|
|
37
|
+
field: ValidStringFields<T>,
|
|
38
|
+
prefix?: string,
|
|
39
|
+
query?: PageableModelQuery<T>
|
|
40
|
+
): Promise<string[]>;
|
|
41
|
+
}
|
package/src/util/crud.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import { type ModelCrudSupport, ModelRegistryIndex, type ModelType } from '@travetto/model';
|
|
1
2
|
import { type Class, hasFunction } from '@travetto/runtime';
|
|
2
|
-
import { type ModelType, type ModelCrudSupport, ModelRegistryIndex } from '@travetto/model';
|
|
3
3
|
|
|
4
4
|
import type { ModelQueryCrudSupport } from '../types/crud.ts';
|
|
5
5
|
|
|
@@ -23,4 +23,4 @@ export class ModelQueryCrudUtil {
|
|
|
23
23
|
});
|
|
24
24
|
return count ?? 0;
|
|
25
25
|
}
|
|
26
|
-
}
|
|
26
|
+
}
|
package/src/util/facet.ts
CHANGED
package/src/util/query.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type
|
|
1
|
+
import { ModelRegistryIndex, type ModelType, NotFoundError } from '@travetto/model';
|
|
2
|
+
import { type Class, castTo, hasFunction, RuntimeError, TimeUtil } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
5
|
import type { WhereClause, WhereClauseRaw } from '../model/where-clause.ts';
|
|
@@ -9,7 +9,6 @@ import type { ModelQuerySupport } from '../types/query.ts';
|
|
|
9
9
|
* Common model utils, that should be usable by end users
|
|
10
10
|
*/
|
|
11
11
|
export class ModelQueryUtil {
|
|
12
|
-
|
|
13
12
|
/**
|
|
14
13
|
* Type guard for determining if service supports query operations
|
|
15
14
|
*/
|
|
@@ -33,10 +32,10 @@ export class ModelQueryUtil {
|
|
|
33
32
|
*/
|
|
34
33
|
static verifyGetSingleCounts<T extends ModelType>(cls: Class<T>, failOnMany: boolean, result?: T[], where?: WhereClause<T>): T {
|
|
35
34
|
result = result ?? [];
|
|
36
|
-
if (result.length === 1 || result.length > 1 && !failOnMany) {
|
|
35
|
+
if (result.length === 1 || (result.length > 1 && !failOnMany)) {
|
|
37
36
|
return result[0]!;
|
|
38
37
|
}
|
|
39
|
-
const requestedId =
|
|
38
|
+
const requestedId = where && 'id' in where && typeof where.id === 'string' ? where.id : undefined;
|
|
40
39
|
if (result.length === 0) {
|
|
41
40
|
if (requestedId) {
|
|
42
41
|
throw new NotFoundError(cls, requestedId);
|
|
@@ -54,27 +53,34 @@ export class ModelQueryUtil {
|
|
|
54
53
|
* Get a where clause with type
|
|
55
54
|
*/
|
|
56
55
|
static getWhereClause<T extends ModelType>(cls: Class<T>, where: WhereClause<T> | undefined, checkExpiry = true): WhereClause<T> {
|
|
57
|
-
const clauses: WhereClauseRaw<T>[] =
|
|
56
|
+
const clauses: WhereClauseRaw<T>[] = where ? [where] : [];
|
|
58
57
|
|
|
59
58
|
const polymorphicConfig = SchemaRegistryIndex.getDiscriminatedConfig(cls);
|
|
60
59
|
if (polymorphicConfig) {
|
|
61
|
-
clauses.push(
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
60
|
+
clauses.push(
|
|
61
|
+
castTo(
|
|
62
|
+
polymorphicConfig.discriminatedBase
|
|
63
|
+
? {
|
|
64
|
+
[polymorphicConfig.discriminatedField]: { $in: SchemaRegistryIndex.getDiscriminatedTypes(cls) }
|
|
65
|
+
}
|
|
66
|
+
: {
|
|
67
|
+
[polymorphicConfig.discriminatedField]: polymorphicConfig.discriminatedType
|
|
68
|
+
}
|
|
69
|
+
)
|
|
70
|
+
);
|
|
67
71
|
}
|
|
68
72
|
|
|
69
73
|
const indexConfig = ModelRegistryIndex.getConfig(cls);
|
|
70
74
|
if (checkExpiry && indexConfig.expiresAt) {
|
|
71
|
-
clauses.push(
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
75
|
+
clauses.push(
|
|
76
|
+
castTo({
|
|
77
|
+
$or: [
|
|
78
|
+
{ [indexConfig.expiresAt]: { $exists: false } },
|
|
79
|
+
{ [indexConfig.expiresAt]: undefined },
|
|
80
|
+
{ [indexConfig.expiresAt]: { $gte: new Date() } }
|
|
81
|
+
]
|
|
82
|
+
})
|
|
83
|
+
);
|
|
78
84
|
}
|
|
79
85
|
if (clauses.length > 1) {
|
|
80
86
|
where = { $and: clauses };
|
|
@@ -84,10 +90,7 @@ export class ModelQueryUtil {
|
|
|
84
90
|
return where!;
|
|
85
91
|
}
|
|
86
92
|
|
|
87
|
-
static has$And = (value: unknown): value is
|
|
88
|
-
|
|
89
|
-
static has$
|
|
90
|
-
|
|
91
|
-
static has$Not = (value: unknown): value is ({ $not: WhereClause<unknown> }) =>
|
|
92
|
-
!!value && typeof value === 'object' && '$not' in value;
|
|
93
|
-
}
|
|
93
|
+
static has$And = (value: unknown): value is { $and: WhereClause<unknown>[] } => !!value && typeof value === 'object' && '$and' in value;
|
|
94
|
+
static has$Or = (value: unknown): value is { $or: WhereClause<unknown>[] } => !!value && typeof value === 'object' && '$or' in value;
|
|
95
|
+
static has$Not = (value: unknown): value is { $not: WhereClause<unknown> } => !!value && typeof value === 'object' && '$not' in value;
|
|
96
|
+
}
|
package/src/util/suggest.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { ModelRegistryIndex, type ModelType } from '@travetto/model';
|
|
2
|
-
import { castKey, castTo,
|
|
2
|
+
import { type Class, castKey, castTo, hasFunction } from '@travetto/runtime';
|
|
3
3
|
import { SchemaRegistryIndex } from '@travetto/schema';
|
|
4
4
|
|
|
5
5
|
import type { PageableModelQuery, Query } from '../model/query.ts';
|
|
@@ -10,7 +10,6 @@ import type { ModelQuerySuggestSupport } from '../types/suggest.ts';
|
|
|
10
10
|
* Tools for building suggestion queries
|
|
11
11
|
*/
|
|
12
12
|
export class ModelQuerySuggestUtil {
|
|
13
|
-
|
|
14
13
|
/**
|
|
15
14
|
* Type guard for determining if service supports query suggest operations
|
|
16
15
|
*/
|
|
@@ -44,9 +43,9 @@ export class ModelQuerySuggestUtil {
|
|
|
44
43
|
const polymorphicConfig = SchemaRegistryIndex.getDiscriminatedConfig(cls);
|
|
45
44
|
if (polymorphicConfig) {
|
|
46
45
|
clauses.push({
|
|
47
|
-
[polymorphicConfig.discriminatedField]: polymorphicConfig.discriminatedBase
|
|
48
|
-
{ $in: SchemaRegistryIndex.getDiscriminatedTypes(cls) }
|
|
49
|
-
polymorphicConfig.discriminatedType
|
|
46
|
+
[polymorphicConfig.discriminatedField]: polymorphicConfig.discriminatedBase
|
|
47
|
+
? { $in: SchemaRegistryIndex.getDiscriminatedTypes(cls) }
|
|
48
|
+
: polymorphicConfig.discriminatedType
|
|
50
49
|
});
|
|
51
50
|
if (query?.select) {
|
|
52
51
|
Object.assign(select, { [polymorphicConfig.discriminatedField]: true });
|
|
@@ -98,7 +97,7 @@ export class ModelQuerySuggestUtil {
|
|
|
98
97
|
}
|
|
99
98
|
return out
|
|
100
99
|
.toSorted((a, b) => a[0].localeCompare(b[0]))
|
|
101
|
-
.map(
|
|
100
|
+
.map(a => a[1])
|
|
102
101
|
.filter((result, i, arr) => result !== arr[i - 1])
|
|
103
102
|
.slice(0, limit ?? 10);
|
|
104
103
|
}
|
|
@@ -106,7 +105,12 @@ export class ModelQuerySuggestUtil {
|
|
|
106
105
|
/**
|
|
107
106
|
* Build suggestion query
|
|
108
107
|
*/
|
|
109
|
-
static getSuggestFieldQuery<T extends ModelType>(
|
|
108
|
+
static getSuggestFieldQuery<T extends ModelType>(
|
|
109
|
+
cls: Class<T>,
|
|
110
|
+
field: ValidStringFields<T>,
|
|
111
|
+
prefix?: string,
|
|
112
|
+
query?: PageableModelQuery<T>
|
|
113
|
+
): Query<T> {
|
|
110
114
|
return this.getSuggestQuery<T>(cls, castTo(field), prefix, query);
|
|
111
115
|
}
|
|
112
|
-
}
|
|
116
|
+
}
|