@loopback/repository 2.11.1 → 3.1.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/CHANGELOG.md +66 -0
- package/dist/common-types.d.ts +5 -3
- package/dist/common-types.js +9 -3
- package/dist/common-types.js.map +1 -1
- package/dist/connectors/crud.connector.d.ts +3 -3
- package/dist/connectors/kv.connector.d.ts +3 -3
- package/dist/decorators/model.decorator.js +7 -0
- package/dist/decorators/model.decorator.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +9 -1
- package/dist/index.js.map +1 -1
- package/dist/mixins/repository.mixin.d.ts +9 -3
- package/dist/model.d.ts +29 -6
- package/dist/model.js.map +1 -1
- package/dist/relations/belongs-to/belongs-to.decorator.js +10 -1
- package/dist/relations/belongs-to/belongs-to.decorator.js.map +1 -1
- package/dist/relations/has-many/has-many-through-repository.factory.d.ts +8 -2
- package/dist/relations/has-many/has-many-through-repository.factory.js +6 -0
- package/dist/relations/has-many/has-many-through-repository.factory.js.map +1 -1
- package/dist/relations/has-many/has-many-through.inclusion.resolver.d.ts +16 -0
- package/dist/relations/has-many/has-many-through.inclusion.resolver.js +74 -0
- package/dist/relations/has-many/has-many-through.inclusion.resolver.js.map +1 -0
- package/dist/relations/has-many/has-many.inclusion-resolver.js.map +1 -1
- package/dist/relations/has-many/has-many.repository.d.ts +1 -1
- package/dist/relations/has-one/has-one.repository.d.ts +1 -1
- package/dist/relations/has-one/has-one.repository.js.map +1 -1
- package/dist/relations/relation.helpers.d.ts +2 -2
- package/dist/relations/relation.helpers.js +1 -1
- package/dist/relations/relation.helpers.js.map +1 -1
- package/dist/relations/relation.types.d.ts +1 -1
- package/dist/relations/relation.types.js +1 -1
- package/dist/repositories/constraint-utils.d.ts +1 -1
- package/dist/repositories/constraint-utils.js +4 -4
- package/dist/repositories/constraint-utils.js.map +1 -1
- package/dist/repositories/legacy-juggler-bridge.d.ts +1 -1
- package/dist/repositories/legacy-juggler-bridge.js +1 -1
- package/dist/repositories/legacy-juggler-bridge.js.map +1 -1
- package/dist/repositories/repository.d.ts +1 -1
- package/dist/repositories/repository.js.map +1 -1
- package/package.json +16 -12
- package/src/common-types.ts +10 -4
- package/src/connectors/crud.connector.ts +3 -3
- package/src/connectors/kv.connector.ts +3 -3
- package/src/decorators/model.decorator.ts +9 -0
- package/src/index.ts +1 -1
- package/src/model.ts +32 -2
- package/src/relations/belongs-to/belongs-to.decorator.ts +21 -5
- package/src/relations/belongs-to/belongs-to.inclusion-resolver.ts +1 -1
- package/src/relations/has-many/has-many-through-repository.factory.ts +20 -3
- package/src/relations/has-many/has-many-through.inclusion.resolver.ts +135 -0
- package/src/relations/has-many/has-many.inclusion-resolver.ts +1 -1
- package/src/relations/has-many/has-many.repository.ts +1 -1
- package/src/relations/has-one/has-one.inclusion-resolver.ts +1 -1
- package/src/relations/has-one/has-one.repository.ts +1 -1
- package/src/relations/relation.helpers.ts +2 -5
- package/src/relations/relation.types.ts +1 -1
- package/src/repositories/constraint-utils.ts +1 -1
- package/src/repositories/legacy-juggler-bridge.ts +8 -7
- package/src/repositories/repository.ts +3 -2
- package/dist/query.d.ts +0 -382
- package/dist/query.js +0 -487
- package/dist/query.js.map +0 -1
- package/src/query.ts +0 -729
package/src/query.ts
DELETED
|
@@ -1,729 +0,0 @@
|
|
|
1
|
-
// Copyright IBM Corp. 2017,2020. All Rights Reserved.
|
|
2
|
-
// Node module: @loopback/repository
|
|
3
|
-
// This file is licensed under the MIT License.
|
|
4
|
-
// License text available at https://opensource.org/licenses/MIT
|
|
5
|
-
|
|
6
|
-
import assert from 'assert';
|
|
7
|
-
import {AnyObject} from './common-types';
|
|
8
|
-
|
|
9
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
10
|
-
|
|
11
|
-
const nonWhereFields = [
|
|
12
|
-
'fields',
|
|
13
|
-
'order',
|
|
14
|
-
'limit',
|
|
15
|
-
'skip',
|
|
16
|
-
'offset',
|
|
17
|
-
'include',
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
const filterFields = ['where', ...nonWhereFields];
|
|
21
|
-
|
|
22
|
-
/**
|
|
23
|
-
* Operators for where clauses
|
|
24
|
-
*/
|
|
25
|
-
export type Operators =
|
|
26
|
-
| 'eq' // Equal
|
|
27
|
-
| 'neq' // Not Equal
|
|
28
|
-
| 'gt' // >
|
|
29
|
-
| 'gte' // >=
|
|
30
|
-
| 'lt' // <
|
|
31
|
-
| 'lte' // <=
|
|
32
|
-
| 'inq' // IN
|
|
33
|
-
| 'nin' // NOT IN
|
|
34
|
-
| 'between' // BETWEEN [val1, val2]
|
|
35
|
-
| 'exists'
|
|
36
|
-
| 'and' // AND
|
|
37
|
-
| 'or' // OR
|
|
38
|
-
| 'like' // LIKE
|
|
39
|
-
| 'nlike' // NOT LIKE
|
|
40
|
-
| 'ilike' // ILIKE'
|
|
41
|
-
| 'nilike' // NOT ILIKE
|
|
42
|
-
| 'regexp'; // REGEXP'
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Matching predicate comparison
|
|
46
|
-
*/
|
|
47
|
-
export type PredicateComparison<PT> = {
|
|
48
|
-
eq?: PT;
|
|
49
|
-
neq?: PT;
|
|
50
|
-
gt?: PT;
|
|
51
|
-
gte?: PT;
|
|
52
|
-
lt?: PT;
|
|
53
|
-
lte?: PT;
|
|
54
|
-
inq?: PT[];
|
|
55
|
-
nin?: PT[];
|
|
56
|
-
between?: [PT, PT];
|
|
57
|
-
exists?: boolean;
|
|
58
|
-
like?: PT;
|
|
59
|
-
nlike?: PT;
|
|
60
|
-
ilike?: PT;
|
|
61
|
-
nilike?: PT;
|
|
62
|
-
regexp?: string | RegExp;
|
|
63
|
-
// [extendedOperation: string]: any;
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Value types for `{propertyName: value}`
|
|
68
|
-
*/
|
|
69
|
-
export type ShortHandEqualType = string | number | boolean | Date;
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Key types of a given model, excluding operators
|
|
73
|
-
*/
|
|
74
|
-
export type KeyOf<MT extends object> = Exclude<
|
|
75
|
-
Extract<keyof MT, string>,
|
|
76
|
-
Operators
|
|
77
|
-
>;
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* Condition clause
|
|
81
|
-
*
|
|
82
|
-
* @example
|
|
83
|
-
* ```ts
|
|
84
|
-
* {
|
|
85
|
-
* name: {inq: ['John', 'Mary']},
|
|
86
|
-
* status: 'ACTIVE',
|
|
87
|
-
* age: {gte: 40}
|
|
88
|
-
* }
|
|
89
|
-
* ```
|
|
90
|
-
*/
|
|
91
|
-
export type Condition<MT extends object> = {
|
|
92
|
-
[P in KeyOf<MT>]?:
|
|
93
|
-
| PredicateComparison<MT[P]> // {x: {lt: 1}}
|
|
94
|
-
| (MT[P] & ShortHandEqualType); // {x: 1},
|
|
95
|
-
};
|
|
96
|
-
|
|
97
|
-
/**
|
|
98
|
-
* Where clause
|
|
99
|
-
*
|
|
100
|
-
* @example
|
|
101
|
-
* ```ts
|
|
102
|
-
* {
|
|
103
|
-
* name: {inq: ['John', 'Mary']},
|
|
104
|
-
* status: 'ACTIVE'
|
|
105
|
-
* and: [...],
|
|
106
|
-
* or: [...],
|
|
107
|
-
* }
|
|
108
|
-
* ```
|
|
109
|
-
*/
|
|
110
|
-
export type Where<MT extends object = AnyObject> =
|
|
111
|
-
| Condition<MT>
|
|
112
|
-
| AndClause<MT>
|
|
113
|
-
| OrClause<MT>;
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* And clause
|
|
117
|
-
*
|
|
118
|
-
* @example
|
|
119
|
-
* ```ts
|
|
120
|
-
* {
|
|
121
|
-
* and: [...],
|
|
122
|
-
* }
|
|
123
|
-
* ```
|
|
124
|
-
*/
|
|
125
|
-
export interface AndClause<MT extends object> {
|
|
126
|
-
and: Where<MT>[];
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Or clause
|
|
131
|
-
*
|
|
132
|
-
* @example
|
|
133
|
-
* ```ts
|
|
134
|
-
* {
|
|
135
|
-
* or: [...],
|
|
136
|
-
* }
|
|
137
|
-
* ```
|
|
138
|
-
*/
|
|
139
|
-
export interface OrClause<MT extends object> {
|
|
140
|
-
or: Where<MT>[];
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Order by direction
|
|
145
|
-
*/
|
|
146
|
-
export type Direction = 'ASC' | 'DESC';
|
|
147
|
-
|
|
148
|
-
/**
|
|
149
|
-
* Order by
|
|
150
|
-
*
|
|
151
|
-
* Example:
|
|
152
|
-
* `{afieldname: 'ASC'}`
|
|
153
|
-
*/
|
|
154
|
-
export type Order<MT = AnyObject> = {[P in keyof MT]: Direction};
|
|
155
|
-
|
|
156
|
-
/**
|
|
157
|
-
* Selection of fields
|
|
158
|
-
*
|
|
159
|
-
* Example:
|
|
160
|
-
* `{afieldname: true}`
|
|
161
|
-
*/
|
|
162
|
-
export type Fields<MT = AnyObject> = {[P in keyof MT]?: boolean};
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* Inclusion of related items
|
|
166
|
-
*
|
|
167
|
-
* Note: scope means filter on related items
|
|
168
|
-
*
|
|
169
|
-
* Example:
|
|
170
|
-
* `{relation: 'aRelationName', scope: {<AFilterObject>}}`
|
|
171
|
-
*/
|
|
172
|
-
export interface Inclusion {
|
|
173
|
-
relation: string;
|
|
174
|
-
|
|
175
|
-
// Technically, we should restrict the filter to target model.
|
|
176
|
-
// That is unfortunately rather difficult to achieve, because
|
|
177
|
-
// an Entity does not provide type information about related models.
|
|
178
|
-
// We could use {ModelName}WithRelations interface for first-level inclusion,
|
|
179
|
-
// but that won't handle second-level (and deeper) inclusions.
|
|
180
|
-
// To keep things simple, we allow any filter here, effectively turning off
|
|
181
|
-
// compiler checks.
|
|
182
|
-
scope?: Filter<AnyObject>;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
/**
|
|
186
|
-
* Query filter object
|
|
187
|
-
*/
|
|
188
|
-
export interface Filter<MT extends object = AnyObject> {
|
|
189
|
-
/**
|
|
190
|
-
* The matching criteria
|
|
191
|
-
*/
|
|
192
|
-
where?: Where<MT>;
|
|
193
|
-
/**
|
|
194
|
-
* To include/exclude fields
|
|
195
|
-
*/
|
|
196
|
-
fields?: Fields<MT>;
|
|
197
|
-
/**
|
|
198
|
-
* Sorting order for matched entities. Each item should be formatted as
|
|
199
|
-
* `fieldName ASC` or `fieldName DESC`.
|
|
200
|
-
* For example: `['f1 ASC', 'f2 DESC', 'f3 ASC']`.
|
|
201
|
-
*
|
|
202
|
-
* We might want to use `Order` in the future. Keep it as `string[]` for now
|
|
203
|
-
* for compatibility with LoopBack 3.x.
|
|
204
|
-
*/
|
|
205
|
-
order?: string[];
|
|
206
|
-
/**
|
|
207
|
-
* Maximum number of entities
|
|
208
|
-
*/
|
|
209
|
-
limit?: number;
|
|
210
|
-
/**
|
|
211
|
-
* Skip N number of entities
|
|
212
|
-
*/
|
|
213
|
-
skip?: number;
|
|
214
|
-
/**
|
|
215
|
-
* Offset N number of entities. An alias for `skip`
|
|
216
|
-
*/
|
|
217
|
-
offset?: number;
|
|
218
|
-
/**
|
|
219
|
-
* To include related objects
|
|
220
|
-
*/
|
|
221
|
-
include?: Inclusion[];
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Filter without `where` property
|
|
226
|
-
*/
|
|
227
|
-
export type FilterExcludingWhere<MT extends object = AnyObject> = Omit<
|
|
228
|
-
Filter<MT>,
|
|
229
|
-
'where'
|
|
230
|
-
>;
|
|
231
|
-
|
|
232
|
-
/**
|
|
233
|
-
* TypeGuard for Filter
|
|
234
|
-
* @param candidate
|
|
235
|
-
*/
|
|
236
|
-
export function isFilter<MT extends object>(
|
|
237
|
-
candidate: any,
|
|
238
|
-
): candidate is Filter<MT> {
|
|
239
|
-
if (typeof candidate !== 'object') return false;
|
|
240
|
-
for (const key in candidate) {
|
|
241
|
-
if (!filterFields.includes(key)) {
|
|
242
|
-
return false;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
return true;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
/**
|
|
249
|
-
* A builder for Where object. It provides fluent APIs to add clauses such as
|
|
250
|
-
* `and`, `or`, and other operators.
|
|
251
|
-
*
|
|
252
|
-
* @example
|
|
253
|
-
* ```ts
|
|
254
|
-
* const whereBuilder = new WhereBuilder();
|
|
255
|
-
* const where = whereBuilder
|
|
256
|
-
* .eq('a', 1)
|
|
257
|
-
* .and({x: 'x'}, {y: {gt: 1}})
|
|
258
|
-
* .and({b: 'b'}, {c: {lt: 1}})
|
|
259
|
-
* .or({d: 'd'}, {e: {neq: 1}})
|
|
260
|
-
* .build();
|
|
261
|
-
* ```
|
|
262
|
-
*/
|
|
263
|
-
export class WhereBuilder<MT extends object = AnyObject> {
|
|
264
|
-
where: Where<MT>;
|
|
265
|
-
|
|
266
|
-
constructor(w?: Where<MT>) {
|
|
267
|
-
this.where = w ?? {};
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
private add(w: Where<MT>): this {
|
|
271
|
-
for (const k of Object.keys(w)) {
|
|
272
|
-
if (k in this.where) {
|
|
273
|
-
// Found conflicting keys, create an `and` operator to join the existing
|
|
274
|
-
// conditions with the new one
|
|
275
|
-
const where = {and: [this.where, w]};
|
|
276
|
-
this.where = where;
|
|
277
|
-
return this;
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
// Merge the where items
|
|
281
|
-
this.where = Object.assign(this.where, w);
|
|
282
|
-
return this;
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
/**
|
|
286
|
-
* @deprecated
|
|
287
|
-
* Starting from TypeScript 3.2, we don't have to cast any more. This method
|
|
288
|
-
* should be considered as `deprecated`.
|
|
289
|
-
*
|
|
290
|
-
* Cast an `and`, `or`, or condition clause to Where
|
|
291
|
-
* @param clause - And/Or/Condition clause
|
|
292
|
-
*/
|
|
293
|
-
cast(clause: AndClause<MT> | OrClause<MT> | Condition<MT>): Where<MT> {
|
|
294
|
-
return clause;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Add an `and` clause.
|
|
299
|
-
* @param w - One or more where objects
|
|
300
|
-
*/
|
|
301
|
-
and(...w: (Where<MT> | Where<MT>[])[]): this {
|
|
302
|
-
let clauses: Where<MT>[] = [];
|
|
303
|
-
w.forEach(where => {
|
|
304
|
-
clauses = clauses.concat(Array.isArray(where) ? where : [where]);
|
|
305
|
-
});
|
|
306
|
-
return this.add({and: clauses});
|
|
307
|
-
}
|
|
308
|
-
|
|
309
|
-
/**
|
|
310
|
-
* Add an `or` clause.
|
|
311
|
-
* @param w - One or more where objects
|
|
312
|
-
*/
|
|
313
|
-
or(...w: (Where<MT> | Where<MT>[])[]): this {
|
|
314
|
-
let clauses: Where<MT>[] = [];
|
|
315
|
-
w.forEach(where => {
|
|
316
|
-
clauses = clauses.concat(Array.isArray(where) ? where : [where]);
|
|
317
|
-
});
|
|
318
|
-
return this.add({or: clauses});
|
|
319
|
-
}
|
|
320
|
-
|
|
321
|
-
/**
|
|
322
|
-
* Add an `=` condition
|
|
323
|
-
* @param key - Property name
|
|
324
|
-
* @param val - Property value
|
|
325
|
-
*/
|
|
326
|
-
eq<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
327
|
-
const w: Where<MT> = {};
|
|
328
|
-
w[key] = val;
|
|
329
|
-
return this.add(w);
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* Add a `!=` condition
|
|
334
|
-
* @param key - Property name
|
|
335
|
-
* @param val - Property value
|
|
336
|
-
*/
|
|
337
|
-
neq<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
338
|
-
const w: Where<MT> = {};
|
|
339
|
-
w[key] = {neq: val};
|
|
340
|
-
return this.add(w);
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
/**
|
|
344
|
-
* Add a `>` condition
|
|
345
|
-
* @param key - Property name
|
|
346
|
-
* @param val - Property value
|
|
347
|
-
*/
|
|
348
|
-
gt<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
349
|
-
const w: Where<MT> = {};
|
|
350
|
-
w[key] = {gt: val};
|
|
351
|
-
return this.add(w);
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
/**
|
|
355
|
-
* Add a `>=` condition
|
|
356
|
-
* @param key - Property name
|
|
357
|
-
* @param val - Property value
|
|
358
|
-
*/
|
|
359
|
-
gte<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
360
|
-
const w: Where<MT> = {};
|
|
361
|
-
w[key] = {gte: val};
|
|
362
|
-
return this.add(w);
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
/**
|
|
366
|
-
* Add a `<` condition
|
|
367
|
-
* @param key - Property name
|
|
368
|
-
* @param val - Property value
|
|
369
|
-
*/
|
|
370
|
-
lt<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
371
|
-
const w: Where<MT> = {};
|
|
372
|
-
w[key] = {lt: val};
|
|
373
|
-
return this.add(w);
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
/**
|
|
377
|
-
* Add a `<=` condition
|
|
378
|
-
* @param key - Property name
|
|
379
|
-
* @param val - Property value
|
|
380
|
-
*/
|
|
381
|
-
lte<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
382
|
-
const w: Where<MT> = {};
|
|
383
|
-
w[key] = {lte: val};
|
|
384
|
-
return this.add(w);
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
* Add a `inq` condition
|
|
389
|
-
* @param key - Property name
|
|
390
|
-
* @param val - An array of property values
|
|
391
|
-
*/
|
|
392
|
-
inq<K extends KeyOf<MT>>(key: K, val: MT[K][]): this {
|
|
393
|
-
const w: Where<MT> = {};
|
|
394
|
-
w[key] = {inq: val};
|
|
395
|
-
return this.add(w);
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
/**
|
|
399
|
-
* Add a `nin` condition
|
|
400
|
-
* @param key - Property name
|
|
401
|
-
* @param val - An array of property values
|
|
402
|
-
*/
|
|
403
|
-
nin<K extends KeyOf<MT>>(key: K, val: MT[K][]): this {
|
|
404
|
-
const w: Where<MT> = {};
|
|
405
|
-
w[key] = {nin: val};
|
|
406
|
-
return this.add(w);
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
/**
|
|
410
|
-
* Add a `between` condition
|
|
411
|
-
* @param key - Property name
|
|
412
|
-
* @param val1 - Property value lower bound
|
|
413
|
-
* @param val2 - Property value upper bound
|
|
414
|
-
*/
|
|
415
|
-
between<K extends KeyOf<MT>>(key: K, val1: MT[K], val2: MT[K]): this {
|
|
416
|
-
const w: Where<MT> = {};
|
|
417
|
-
w[key] = {between: [val1, val2]};
|
|
418
|
-
return this.add(w);
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
/**
|
|
422
|
-
* Add a `exists` condition
|
|
423
|
-
* @param key - Property name
|
|
424
|
-
* @param val - Exists or not
|
|
425
|
-
*/
|
|
426
|
-
exists<K extends KeyOf<MT>>(key: K, val?: boolean): this {
|
|
427
|
-
const w: Where<MT> = {};
|
|
428
|
-
w[key] = {exists: !!val || val == null};
|
|
429
|
-
return this.add(w);
|
|
430
|
-
}
|
|
431
|
-
/**
|
|
432
|
-
* Add a where object. For conflicting keys with the existing where object,
|
|
433
|
-
* create an `and` clause.
|
|
434
|
-
* @param where - Where filter
|
|
435
|
-
*/
|
|
436
|
-
impose(where: Where<MT>): this {
|
|
437
|
-
if (!this.where) {
|
|
438
|
-
this.where = where || {};
|
|
439
|
-
} else {
|
|
440
|
-
this.add(where);
|
|
441
|
-
}
|
|
442
|
-
return this;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
/**
|
|
446
|
-
* Add a `like` condition
|
|
447
|
-
* @param key - Property name
|
|
448
|
-
* @param val - Regexp condition
|
|
449
|
-
*/
|
|
450
|
-
like<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
451
|
-
const w: Where<MT> = {};
|
|
452
|
-
w[key] = {like: val};
|
|
453
|
-
return this.add(w);
|
|
454
|
-
}
|
|
455
|
-
|
|
456
|
-
/**
|
|
457
|
-
* Add a `nlike` condition
|
|
458
|
-
* @param key - Property name
|
|
459
|
-
* @param val - Regexp condition
|
|
460
|
-
*/
|
|
461
|
-
nlike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
462
|
-
const w: Where<MT> = {};
|
|
463
|
-
w[key] = {nlike: val};
|
|
464
|
-
return this.add(w);
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/**
|
|
468
|
-
* Add a `ilike` condition
|
|
469
|
-
* @param key - Property name
|
|
470
|
-
* @param val - Regexp condition
|
|
471
|
-
*/
|
|
472
|
-
ilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
473
|
-
const w: Where<MT> = {};
|
|
474
|
-
w[key] = {ilike: val};
|
|
475
|
-
return this.add(w);
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
/**
|
|
479
|
-
* Add a `nilike` condition
|
|
480
|
-
* @param key - Property name
|
|
481
|
-
* @param val - Regexp condition
|
|
482
|
-
*/
|
|
483
|
-
nilike<K extends KeyOf<MT>>(key: K, val: MT[K]): this {
|
|
484
|
-
const w: Where<MT> = {};
|
|
485
|
-
w[key] = {nilike: val};
|
|
486
|
-
return this.add(w);
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
/**
|
|
490
|
-
* Add a `regexp` condition
|
|
491
|
-
* @param key - Property name
|
|
492
|
-
* @param val - Regexp condition
|
|
493
|
-
*/
|
|
494
|
-
regexp<K extends KeyOf<MT>>(key: K, val: string | RegExp): this {
|
|
495
|
-
const w: Where<MT> = {};
|
|
496
|
-
w[key] = {regexp: val};
|
|
497
|
-
return this.add(w);
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
/**
|
|
501
|
-
* Get the where object
|
|
502
|
-
*/
|
|
503
|
-
build() {
|
|
504
|
-
return this.where;
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
|
|
508
|
-
/**
|
|
509
|
-
* A builder for Filter. It provides fleunt APIs to add clauses such as
|
|
510
|
-
* `fields`, `order`, `where`, `limit`, `offset`, and `include`.
|
|
511
|
-
*
|
|
512
|
-
* @example
|
|
513
|
-
* ```ts
|
|
514
|
-
* const filterBuilder = new FilterBuilder();
|
|
515
|
-
* const filter = filterBuilder
|
|
516
|
-
* .fields('id', a', 'b')
|
|
517
|
-
* .limit(10)
|
|
518
|
-
* .offset(0)
|
|
519
|
-
* .order(['a ASC', 'b DESC'])
|
|
520
|
-
* .where({id: 1})
|
|
521
|
-
* .build();
|
|
522
|
-
* ```
|
|
523
|
-
*/
|
|
524
|
-
export class FilterBuilder<MT extends object = AnyObject> {
|
|
525
|
-
filter: Filter<MT>;
|
|
526
|
-
|
|
527
|
-
constructor(f?: Filter<MT>) {
|
|
528
|
-
this.filter = f ?? {};
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
/**
|
|
532
|
-
* Set `limit`
|
|
533
|
-
* @param limit - Maximum number of records to be returned
|
|
534
|
-
*/
|
|
535
|
-
limit(limit: number): this {
|
|
536
|
-
assert(limit >= 1, `Limit ${limit} must a positive number`);
|
|
537
|
-
this.filter.limit = limit;
|
|
538
|
-
return this;
|
|
539
|
-
}
|
|
540
|
-
|
|
541
|
-
/**
|
|
542
|
-
* Set `offset`
|
|
543
|
-
* @param offset - Offset of the number of records to be returned
|
|
544
|
-
*/
|
|
545
|
-
offset(offset: number): this {
|
|
546
|
-
this.filter.offset = offset;
|
|
547
|
-
return this;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
/**
|
|
551
|
-
* Alias to `offset`
|
|
552
|
-
* @param skip
|
|
553
|
-
*/
|
|
554
|
-
skip(skip: number): this {
|
|
555
|
-
return this.offset(skip);
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
/**
|
|
559
|
-
* Describe what fields to be included/excluded
|
|
560
|
-
* @param f - A field name to be included, an array of field names to be
|
|
561
|
-
* included, or an Fields object for the inclusion/exclusion
|
|
562
|
-
*/
|
|
563
|
-
fields(...f: (Fields<MT> | (keyof MT)[] | keyof MT)[]): this {
|
|
564
|
-
if (!this.filter.fields) {
|
|
565
|
-
this.filter.fields = {};
|
|
566
|
-
}
|
|
567
|
-
const fields = this.filter.fields;
|
|
568
|
-
for (const field of f) {
|
|
569
|
-
if (Array.isArray(field)) {
|
|
570
|
-
(field as (keyof MT)[]).forEach(i => (fields[i] = true));
|
|
571
|
-
} else if (typeof field === 'string') {
|
|
572
|
-
fields[field as keyof MT] = true;
|
|
573
|
-
} else {
|
|
574
|
-
Object.assign(fields, field);
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
return this;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
private validateOrder(order: string) {
|
|
581
|
-
assert(order.match(/^[^\s]+( (ASC|DESC))?$/), 'Invalid order: ' + order);
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
/**
|
|
585
|
-
* Describe the sorting order
|
|
586
|
-
* @param o - A field name with optional direction, an array of field names,
|
|
587
|
-
* or an Order object for the field/direction pairs
|
|
588
|
-
*/
|
|
589
|
-
order(...o: (string | string[] | Order<MT>)[]): this {
|
|
590
|
-
if (!this.filter.order) {
|
|
591
|
-
this.filter.order = [];
|
|
592
|
-
}
|
|
593
|
-
o.forEach(order => {
|
|
594
|
-
if (typeof order === 'string') {
|
|
595
|
-
this.validateOrder(order);
|
|
596
|
-
if (!order.endsWith(' ASC') && !order.endsWith(' DESC')) {
|
|
597
|
-
order = order + ' ASC';
|
|
598
|
-
}
|
|
599
|
-
this.filter.order!.push(order);
|
|
600
|
-
return this;
|
|
601
|
-
}
|
|
602
|
-
if (Array.isArray(order)) {
|
|
603
|
-
order.forEach(this.validateOrder);
|
|
604
|
-
order = order.map(i => {
|
|
605
|
-
if (!i.endsWith(' ASC') && !i.endsWith(' DESC')) {
|
|
606
|
-
i = i + ' ASC';
|
|
607
|
-
}
|
|
608
|
-
return i;
|
|
609
|
-
});
|
|
610
|
-
this.filter.order = this.filter.order!.concat(order);
|
|
611
|
-
return this;
|
|
612
|
-
}
|
|
613
|
-
for (const i in order) {
|
|
614
|
-
this.filter.order!.push(`${i} ${order[i]}`);
|
|
615
|
-
}
|
|
616
|
-
});
|
|
617
|
-
return this;
|
|
618
|
-
}
|
|
619
|
-
|
|
620
|
-
/**
|
|
621
|
-
* Declare `include`
|
|
622
|
-
* @param i - A relation name, an array of relation names, or an `Inclusion`
|
|
623
|
-
* object for the relation/scope definitions
|
|
624
|
-
*/
|
|
625
|
-
include(...i: (string | string[] | Inclusion)[]): this {
|
|
626
|
-
if (this.filter.include == null) {
|
|
627
|
-
this.filter.include = [];
|
|
628
|
-
}
|
|
629
|
-
for (const include of i) {
|
|
630
|
-
if (typeof include === 'string') {
|
|
631
|
-
this.filter.include.push({relation: include});
|
|
632
|
-
} else if (Array.isArray(include)) {
|
|
633
|
-
for (const inc of include) this.filter.include.push({relation: inc});
|
|
634
|
-
} else {
|
|
635
|
-
this.filter.include!.push(include);
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
return this;
|
|
639
|
-
}
|
|
640
|
-
|
|
641
|
-
/**
|
|
642
|
-
* Declare a where clause
|
|
643
|
-
* @param w - Where object
|
|
644
|
-
*/
|
|
645
|
-
where(w: Where<MT>): this {
|
|
646
|
-
this.filter.where = w;
|
|
647
|
-
return this;
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
/**
|
|
651
|
-
* Add a Filter or Where constraint object. If it is a filter object, create
|
|
652
|
-
* an `and` clause for conflicting keys with its where object. For any other
|
|
653
|
-
* properties, throw an error. If it's not a Filter, coerce it to a filter,
|
|
654
|
-
* and carry out the same logic.
|
|
655
|
-
*
|
|
656
|
-
* @param constraint - a constraint object to merge with own filter object
|
|
657
|
-
*/
|
|
658
|
-
impose(constraint: Filter<MT> | Where<MT>): this {
|
|
659
|
-
if (!this.filter) {
|
|
660
|
-
// if constraint is a Where, turn into a Filter
|
|
661
|
-
if (!isFilter(constraint)) {
|
|
662
|
-
constraint = {where: constraint};
|
|
663
|
-
}
|
|
664
|
-
this.filter = (constraint as Filter<MT>) || {};
|
|
665
|
-
} else {
|
|
666
|
-
if (isFilter(constraint)) {
|
|
667
|
-
// throw error if imposed Filter has non-where fields
|
|
668
|
-
for (const key of Object.keys(constraint)) {
|
|
669
|
-
if (nonWhereFields.includes(key)) {
|
|
670
|
-
throw new Error(
|
|
671
|
-
'merging strategy for selection, pagination, and sorting not implemented',
|
|
672
|
-
);
|
|
673
|
-
}
|
|
674
|
-
}
|
|
675
|
-
}
|
|
676
|
-
this.filter.where = isFilter(constraint)
|
|
677
|
-
? new WhereBuilder(this.filter.where).impose(constraint.where!).build()
|
|
678
|
-
: new WhereBuilder(this.filter.where).impose(constraint).build();
|
|
679
|
-
}
|
|
680
|
-
return this;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
/**
|
|
684
|
-
* Return the filter object
|
|
685
|
-
*/
|
|
686
|
-
build() {
|
|
687
|
-
return this.filter;
|
|
688
|
-
}
|
|
689
|
-
}
|
|
690
|
-
|
|
691
|
-
/**
|
|
692
|
-
* Get nested properties by path
|
|
693
|
-
* @param value - Value of an object
|
|
694
|
-
* @param path - Path to the property
|
|
695
|
-
*/
|
|
696
|
-
function getDeepProperty(value: AnyObject, path: string): any {
|
|
697
|
-
const props = path.split('.');
|
|
698
|
-
for (const p of props) {
|
|
699
|
-
value = value[p];
|
|
700
|
-
if (value == null) {
|
|
701
|
-
return null;
|
|
702
|
-
}
|
|
703
|
-
}
|
|
704
|
-
return value;
|
|
705
|
-
}
|
|
706
|
-
|
|
707
|
-
export function filterTemplate(strings: TemplateStringsArray, ...keys: any[]) {
|
|
708
|
-
return function filter(ctx: AnyObject) {
|
|
709
|
-
const tokens = [strings[0]];
|
|
710
|
-
keys.forEach((key, i) => {
|
|
711
|
-
if (
|
|
712
|
-
typeof key === 'object' ||
|
|
713
|
-
typeof key === 'boolean' ||
|
|
714
|
-
typeof key === 'number'
|
|
715
|
-
) {
|
|
716
|
-
tokens.push(JSON.stringify(key), strings[i + 1]);
|
|
717
|
-
return;
|
|
718
|
-
}
|
|
719
|
-
const value = getDeepProperty(ctx, key);
|
|
720
|
-
tokens.push(JSON.stringify(value), strings[i + 1]);
|
|
721
|
-
});
|
|
722
|
-
const result = tokens.join('');
|
|
723
|
-
try {
|
|
724
|
-
return JSON.parse(result);
|
|
725
|
-
} catch (e) {
|
|
726
|
-
throw new Error('Invalid JSON: ' + result);
|
|
727
|
-
}
|
|
728
|
-
};
|
|
729
|
-
}
|