@travetto/model-query 8.0.0-alpha.8 → 8.0.0
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 +24 -14
- package/__index__.ts +5 -6
- package/package.json +16 -16
- package/src/internal/types.ts +16 -10
- package/src/model/indexes.ts +36 -0
- package/src/model/query.ts +5 -7
- package/src/model/where-clause.ts +31 -29
- package/src/types/crud.ts +4 -4
- package/src/types/facet.ts +5 -5
- 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 +2 -2
- package/src/util/query.ts +29 -26
- package/src/util/suggest.ts +28 -11
- package/src/verifier.ts +24 -30
- package/support/doc.support.tsx +6 -7
- package/support/test/crud.ts +90 -52
- package/support/test/facet.ts +23 -15
- package/support/test/model.ts +34 -2
- package/support/test/polymorphism.ts +24 -26
- package/support/test/query.ts +353 -119
- package/support/test/suggest.ts +60 -8
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
|
|
@@ -86,7 +86,7 @@ export interface ModelQueryFacetSupport extends ModelQuerySupport {
|
|
|
86
86
|
* @param field The field to facet on
|
|
87
87
|
* @param query Additional query filtering
|
|
88
88
|
*/
|
|
89
|
-
|
|
89
|
+
facetByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<ModelQueryFacet[]>;
|
|
90
90
|
}
|
|
91
91
|
```
|
|
92
92
|
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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,13 +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
4
|
export * from './src/types/crud.ts';
|
|
4
|
-
export * from './src/types/query.ts';
|
|
5
5
|
export * from './src/types/facet.ts';
|
|
6
|
+
export * from './src/types/query.ts';
|
|
6
7
|
export * from './src/types/suggest.ts';
|
|
7
|
-
|
|
8
|
+
export * from './src/util/crud.ts';
|
|
9
|
+
export * from './src/util/facet.ts';
|
|
8
10
|
export * from './src/util/query.ts';
|
|
9
11
|
export * from './src/util/suggest.ts';
|
|
10
|
-
export * from './src/
|
|
11
|
-
export * from './src/util/crud.ts';
|
|
12
|
-
|
|
13
|
-
export * from './src/verifier.ts';
|
|
12
|
+
export * from './src/verifier.ts';
|
package/package.json
CHANGED
|
@@ -1,38 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@travetto/model-query",
|
|
3
|
-
"version": "8.0.0
|
|
4
|
-
"type": "module",
|
|
3
|
+
"version": "8.0.0",
|
|
5
4
|
"description": "Datastore abstraction for advanced query support.",
|
|
6
5
|
"keywords": [
|
|
7
6
|
"datastore",
|
|
8
7
|
"decorators",
|
|
9
8
|
"schema",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
9
|
+
"travetto",
|
|
10
|
+
"typescript"
|
|
12
11
|
],
|
|
13
12
|
"homepage": "https://travetto.io",
|
|
14
13
|
"license": "MIT",
|
|
15
14
|
"author": {
|
|
16
|
-
"
|
|
17
|
-
"
|
|
15
|
+
"name": "Travetto Framework",
|
|
16
|
+
"email": "travetto.framework@gmail.com"
|
|
17
|
+
},
|
|
18
|
+
"repository": {
|
|
19
|
+
"url": "git+https://github.com/travetto/travetto.git",
|
|
20
|
+
"directory": "module/model-query"
|
|
18
21
|
},
|
|
19
22
|
"files": [
|
|
20
23
|
"__index__.ts",
|
|
21
24
|
"src",
|
|
22
25
|
"support"
|
|
23
26
|
],
|
|
27
|
+
"type": "module",
|
|
24
28
|
"main": "__index__.ts",
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"directory": "module/model-query"
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
28
31
|
},
|
|
29
32
|
"dependencies": {
|
|
30
|
-
"@travetto/di": "^8.0.0
|
|
31
|
-
"@travetto/model": "^8.0.0
|
|
32
|
-
"@travetto/schema": "^8.0.0
|
|
33
|
+
"@travetto/di": "^8.0.0",
|
|
34
|
+
"@travetto/model": "^8.0.0",
|
|
35
|
+
"@travetto/schema": "^8.0.0"
|
|
33
36
|
},
|
|
34
37
|
"peerDependencies": {
|
|
35
|
-
"@travetto/test": "^8.0.0
|
|
38
|
+
"@travetto/test": "^8.0.0"
|
|
36
39
|
},
|
|
37
40
|
"peerDependenciesMeta": {
|
|
38
41
|
"@travetto/test": {
|
|
@@ -41,8 +44,5 @@
|
|
|
41
44
|
},
|
|
42
45
|
"travetto": {
|
|
43
46
|
"displayName": "Data Model Querying"
|
|
44
|
-
},
|
|
45
|
-
"publishConfig": {
|
|
46
|
-
"access": "public"
|
|
47
47
|
}
|
|
48
48
|
}
|
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
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type IndexConfig, ModelRegistryIndex, type ModelType } from '@travetto/model';
|
|
2
|
+
import { type Class, type Primitive, RuntimeError, type ValidFields } from '@travetto/runtime';
|
|
3
|
+
|
|
4
|
+
type RetainPrimitiveFields<T> = Pick<T, ValidFields<T, Primitive | Date>>;
|
|
5
|
+
|
|
6
|
+
type IndexClauseRaw<T> = {
|
|
7
|
+
[P in keyof T]?: T[P] extends object ? IndexClauseRaw<RetainPrimitiveFields<T[P]>> : 1 | -1 | true;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export type IndexField<T extends ModelType> = IndexClauseRaw<RetainPrimitiveFields<T>>;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Index options
|
|
14
|
+
*/
|
|
15
|
+
export interface QueryIndexConfig<T extends ModelType> extends IndexConfig<'query', T> {
|
|
16
|
+
/**
|
|
17
|
+
* Fields and sort order
|
|
18
|
+
*/
|
|
19
|
+
fields: IndexClauseRaw<RetainPrimitiveFields<T>>[];
|
|
20
|
+
/**
|
|
21
|
+
* Is this a unique index
|
|
22
|
+
*/
|
|
23
|
+
unique?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function QueryIndex<T extends ModelType>(index: Omit<QueryIndexConfig<T>, 'class' | 'type'>) {
|
|
27
|
+
if (index.fields.some(field => field === 'id')) {
|
|
28
|
+
throw new RuntimeError('Cannot create an index with the id field');
|
|
29
|
+
}
|
|
30
|
+
return function (cls: Class<T>): void {
|
|
31
|
+
ModelRegistryIndex.getForRegister(cls).register({ indices: { [index.name]: { ...index, type: 'query', class: cls } } });
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export const isModelQueryIndex = (idx: unknown): idx is QueryIndexConfig<ModelType> =>
|
|
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,10 +1,11 @@
|
|
|
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;
|
|
5
5
|
export type QueryPrimitiveArray = QueryPrimitive[];
|
|
6
6
|
export type DistanceUnit = 'mi' | 'm' | 'km' | 'ft' | 'rad';
|
|
7
7
|
export type RetainQueryPrimitiveFields<T> = Pick<T, ValidFields<T, QueryPrimitive>>;
|
|
8
|
+
export type ValidStringFields<T> = KeyPaths<T, String | string | string[] | String[] | undefined>;
|
|
8
9
|
|
|
9
10
|
type General<T> = {
|
|
10
11
|
$eq?: T;
|
|
@@ -25,13 +26,14 @@ type ComparableField<T> = {
|
|
|
25
26
|
};
|
|
26
27
|
|
|
27
28
|
type ArrayField<T> =
|
|
28
|
-
{ $exists?: boolean }
|
|
29
|
-
{ $eq?: T | T[] }
|
|
30
|
-
{ $ne?: T | T[] }
|
|
31
|
-
{ $all?: T[] }
|
|
32
|
-
{ $in?: T[] }
|
|
33
|
-
PropWhereClause<RetainQueryPrimitiveFields<T>>
|
|
34
|
-
|
|
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[];
|
|
35
37
|
|
|
36
38
|
type StringField = { $regex?: RegExp | string };
|
|
37
39
|
|
|
@@ -43,35 +45,35 @@ type GeoField = {
|
|
|
43
45
|
};
|
|
44
46
|
|
|
45
47
|
export type PropWhereClause<T> = {
|
|
46
|
-
[P in keyof T]?:
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
48
|
+
-readonly [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;
|
|
55
65
|
};
|
|
56
66
|
|
|
57
67
|
/**
|
|
58
68
|
* Raw query type
|
|
59
69
|
*/
|
|
60
70
|
export type WhereClauseRaw<T> =
|
|
61
|
-
({ $and: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
62
|
-
({ $or: WhereClauseRaw<T>[] } & { [P in keyof T]?: never })
|
|
63
|
-
({ $not: WhereClauseRaw<T> } & { [P in keyof T]?: never })
|
|
64
|
-
(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 });
|
|
65
75
|
|
|
66
76
|
/**
|
|
67
77
|
* Full where clause, typed against the input type T
|
|
68
78
|
*/
|
|
69
79
|
export type WhereClause<T> = WhereClauseRaw<RetainQueryPrimitiveFields<T>>;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Provides all the valid string type fields from a given type T
|
|
73
|
-
*/
|
|
74
|
-
export type ValidStringFields<T> = {
|
|
75
|
-
[K in Extract<keyof T, string>]:
|
|
76
|
-
(T[K] extends (String | string | string[] | String[] | undefined) ? K : never)
|
|
77
|
-
}[Extract<keyof T, string>];
|
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
|
|
@@ -18,5 +18,5 @@ export interface ModelQueryFacetSupport extends ModelQuerySupport {
|
|
|
18
18
|
* @param field The field to facet on
|
|
19
19
|
* @param query Additional query filtering
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
}
|
|
21
|
+
facetByQuery<T extends ModelType>(cls: Class<T>, field: ValidStringFields<T>, query?: ModelQuery<T>): Promise<ModelQueryFacet[]>;
|
|
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
|
-
|
|
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
|
-
|
|
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
|
@@ -6,5 +6,5 @@ export class ModelQueryFacetUtil {
|
|
|
6
6
|
/**
|
|
7
7
|
* Type guard for determining if service supports query facet operations
|
|
8
8
|
*/
|
|
9
|
-
static isSupported = hasFunction<ModelQueryFacetSupport>('
|
|
10
|
-
}
|
|
9
|
+
static isSupported = hasFunction<ModelQueryFacetSupport>('facetByQuery');
|
|
10
|
+
}
|
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
|
+
}
|