objection 3.5.0 → 3.7.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 +3 -3
- package/lib/model/AjvValidator.js +33 -10
- package/lib/model/modelClone.js +0 -2
- package/lib/queryBuilder/InternalOptions.js +2 -0
- package/lib/queryBuilder/QueryBuilder.js +56 -10
- package/lib/queryBuilder/RelationExpression.js +6 -4
- package/lib/queryBuilder/graph/GraphOptions.js +141 -22
- package/lib/queryBuilder/graph/GraphUpsert.js +57 -0
- package/lib/queryBuilder/graph/insert/GraphInsert.js +2 -0
- package/lib/queryBuilder/graph/patch/GraphPatchAction.js +3 -0
- package/lib/queryBuilder/operations/InstanceUpdateOperation.js +6 -1
- package/lib/queryBuilder/operations/UpdateOperation.js +11 -0
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeMsSqlOperation.js +3 -1
- package/lib/queryBuilder/operations/whereInComposite/WhereInCompositeSqliteOperation.js +4 -9
- package/lib/queryBuilder/parsers/jsonFieldExpressionParser.js +434 -364
- package/lib/queryBuilder/parsers/relationExpressionParser.js +513 -548
- package/lib/queryBuilder/parsers/relationExpressionParser.pegjs +1 -1
- package/lib/queryBuilder/transformations/WrapMysqlModifySubqueryTransformation.js +4 -7
- package/lib/relations/Relation.js +7 -0
- package/lib/relations/RelationFindOperation.js +27 -40
- package/lib/relations/belongsToOne/BelongsToOneDeleteOperation.js +4 -2
- package/lib/relations/belongsToOne/BelongsToOneRelation.js +9 -0
- package/lib/relations/manyToMany/ManyToManyModifyMixin.js +41 -13
- package/lib/relations/manyToMany/ManyToManyMySqlModifyMixin.js +31 -42
- package/lib/relations/manyToMany/ManyToManyRelation.js +15 -18
- package/lib/relations/manyToMany/ManyToManySqliteModifyMixin.js +0 -13
- package/lib/relations/manyToMany/find/ManyToManyFindOperation.js +2 -3
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateMySqlOperation.js +1 -5
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperation.js +1 -5
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateOperationBase.js +9 -0
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelateSqliteOperation.js +1 -5
- package/lib/relations/manyToMany/update/ManyToManyUpdateOperationBase.js +28 -2
- package/lib/utils/identifierMapping.js +8 -17
- package/lib/utils/objectUtils.js +109 -2
- package/lib/utils/parseFieldExpression.js +10 -3
- package/package.json +5 -2
- package/typings/objection/index.d.ts +56 -36
- package/lib/relations/manyToMany/ManyToManyPostgresModifyMixin.js +0 -27
- package/lib/relations/manyToMany/unrelate/ManyToManyUnrelatePostgresOperation.js +0 -16
- package/lib/relations/manyToMany/update/ManyToManyUpdatePostgresOperation.js +0 -12
- package/lib/utils/clone.js +0 -2329
|
@@ -15,9 +15,18 @@ class ManyToManyUpdateOperationBase extends UpdateOperation {
|
|
|
15
15
|
this.joinTablePatchFilterQuery = null;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
get modifiesJoinTable() {
|
|
19
|
+
return this.hasExtraProps;
|
|
20
|
+
}
|
|
21
|
+
|
|
18
22
|
onAdd(builder, args) {
|
|
19
23
|
const obj = args[0];
|
|
20
24
|
|
|
25
|
+
// The modified rows are known to be related to the owner, so they are
|
|
26
|
+
// filtered by their own ids instead of a subquery that joins the join
|
|
27
|
+
// table. On MySQL, concurrent updates with that subquery deadlock (#135).
|
|
28
|
+
this.skipRelationFilter = builder.internalOptions().skipRelationFilter;
|
|
29
|
+
|
|
21
30
|
// Copy all extra properties to the `joinTablePatch` object.
|
|
22
31
|
for (const extra of this.relation.joinTableExtras) {
|
|
23
32
|
if (extra.aliasProp in obj) {
|
|
@@ -42,7 +51,9 @@ class ManyToManyUpdateOperationBase extends UpdateOperation {
|
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
async onAfter1(builder, result) {
|
|
45
|
-
|
|
54
|
+
// A query resolved without updating anything, e.g. with `none()`, leaves
|
|
55
|
+
// the join rows untouched too.
|
|
56
|
+
if (this.hasExtraProps && !this.isResolvedWithoutUpdate(builder)) {
|
|
46
57
|
const joinTableUpdateQuery = this.relation
|
|
47
58
|
.getJoinModelClass(builder.knex())
|
|
48
59
|
.query()
|
|
@@ -50,7 +61,9 @@ class ManyToManyUpdateOperationBase extends UpdateOperation {
|
|
|
50
61
|
.patch(this.joinTablePatch);
|
|
51
62
|
|
|
52
63
|
addWhereGroup(joinTableUpdateQuery, () =>
|
|
53
|
-
this.
|
|
64
|
+
this.skipRelationFilter
|
|
65
|
+
? this.applyIdFilterForJoinTable(joinTableUpdateQuery)
|
|
66
|
+
: this.applyModifyFilterForJoinTable(joinTableUpdateQuery),
|
|
54
67
|
);
|
|
55
68
|
addWhereGroup(joinTableUpdateQuery, () =>
|
|
56
69
|
joinTableUpdateQuery.modify(this.relation.joinTableModify),
|
|
@@ -63,6 +76,19 @@ class ManyToManyUpdateOperationBase extends UpdateOperation {
|
|
|
63
76
|
}
|
|
64
77
|
}
|
|
65
78
|
|
|
79
|
+
// Filters the join rows by the ids of the owner and the updated model.
|
|
80
|
+
applyIdFilterForJoinTable(builder) {
|
|
81
|
+
return builder
|
|
82
|
+
.whereInComposite(
|
|
83
|
+
this.relation.joinTableOwnerProp.refs(builder),
|
|
84
|
+
this.owner.getProps(this.relation),
|
|
85
|
+
)
|
|
86
|
+
.whereComposite(
|
|
87
|
+
this.relation.joinTableRelatedProp.refs(builder),
|
|
88
|
+
this.relation.relatedProp.getProps(this.model),
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
|
|
66
92
|
/* istanbul ignore next */
|
|
67
93
|
applyModifyFilterForRelatedTable(builder) {
|
|
68
94
|
throw new Error('not implemented');
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { isObject
|
|
3
|
+
const { isObject } = require('./objectUtils');
|
|
4
4
|
|
|
5
5
|
// Super fast memoize for single argument functions.
|
|
6
6
|
function memoize(func) {
|
|
@@ -149,9 +149,8 @@ function mapLastPart(mapper, separator) {
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
// Returns a function that takes an object as an input and maps the object's keys
|
|
152
|
-
// using `mapper
|
|
153
|
-
|
|
154
|
-
function keyMapper(mapper, valueMapper) {
|
|
152
|
+
// using `mapper`. If the input is not an object, the input is returned unchanged.
|
|
153
|
+
function keyMapper(mapper) {
|
|
155
154
|
return (obj) => {
|
|
156
155
|
if (!isObject(obj) || Array.isArray(obj)) {
|
|
157
156
|
return obj;
|
|
@@ -162,7 +161,7 @@ function keyMapper(mapper, valueMapper) {
|
|
|
162
161
|
|
|
163
162
|
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
164
163
|
const key = keys[i];
|
|
165
|
-
out[mapper(key)] =
|
|
164
|
+
out[mapper(key)] = obj[key];
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
return out;
|
|
@@ -190,16 +189,10 @@ function snakeCaseMappers(opt = {}) {
|
|
|
190
189
|
};
|
|
191
190
|
}
|
|
192
191
|
|
|
193
|
-
function knexIdentifierMappers({ parse, format, idSeparator = ':'
|
|
192
|
+
function knexIdentifierMappers({ parse, format, idSeparator = ':' } = {}) {
|
|
194
193
|
const formatId = memoize(mapLastPart(format, idSeparator));
|
|
195
194
|
const parseId = memoize(mapLastPart(parse, idSeparator));
|
|
196
195
|
const parseKeys = keyMapper(parseId);
|
|
197
|
-
// With `mapNestedKeys`, also map the keys of plain objects one level down, as
|
|
198
|
-
// in the rows knex returns with its `nestTables: true` option on MySQL:
|
|
199
|
-
// `{ tableName: { columnName: value } }`.
|
|
200
|
-
const parseRow = mapNestedKeys
|
|
201
|
-
? keyMapper(parseId, (value) => (isPlainObject(value) ? parseKeys(value) : value))
|
|
202
|
-
: parseKeys;
|
|
203
196
|
|
|
204
197
|
return {
|
|
205
198
|
wrapIdentifier(identifier, origWrap) {
|
|
@@ -211,12 +204,12 @@ function knexIdentifierMappers({ parse, format, idSeparator = ':', mapNestedKeys
|
|
|
211
204
|
const output = new Array(result.length);
|
|
212
205
|
|
|
213
206
|
for (let i = 0, l = result.length; i < l; ++i) {
|
|
214
|
-
output[i] =
|
|
207
|
+
output[i] = parseKeys(result[i]);
|
|
215
208
|
}
|
|
216
209
|
|
|
217
210
|
return output;
|
|
218
211
|
} else {
|
|
219
|
-
return
|
|
212
|
+
return parseKeys(result);
|
|
220
213
|
}
|
|
221
214
|
},
|
|
222
215
|
};
|
|
@@ -226,11 +219,10 @@ function knexSnakeCaseMappers(opt = {}) {
|
|
|
226
219
|
return knexIdentifierMappers({
|
|
227
220
|
parse: (str) => camelCase(str, opt),
|
|
228
221
|
format: (str) => snakeCase(str, opt),
|
|
229
|
-
mapNestedKeys: opt.mapNestedKeys,
|
|
230
222
|
});
|
|
231
223
|
}
|
|
232
224
|
|
|
233
|
-
function knexIdentifierMapping(colToProp
|
|
225
|
+
function knexIdentifierMapping(colToProp) {
|
|
234
226
|
const propToCol = Object.keys(colToProp).reduce((propToCol, column) => {
|
|
235
227
|
propToCol[colToProp[column]] = column;
|
|
236
228
|
return propToCol;
|
|
@@ -239,7 +231,6 @@ function knexIdentifierMapping(colToProp, { mapNestedKeys } = {}) {
|
|
|
239
231
|
return knexIdentifierMappers({
|
|
240
232
|
parse: (column) => colToProp[column] || column,
|
|
241
233
|
format: (prop) => propToCol[prop] || prop,
|
|
242
|
-
mapNestedKeys,
|
|
243
234
|
});
|
|
244
235
|
}
|
|
245
236
|
|
package/lib/utils/objectUtils.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const { clone, cloneDeep } = require('./clone');
|
|
4
3
|
const SMALL_ARRAY_SIZE = 10;
|
|
5
4
|
|
|
6
5
|
function isEmpty(item) {
|
|
@@ -389,6 +388,115 @@ function mergeMaps(map1, map2) {
|
|
|
389
388
|
return map;
|
|
390
389
|
}
|
|
391
390
|
|
|
391
|
+
// Returns the tag of `value` like `Object.prototype.toString()`, but ignores a
|
|
392
|
+
// `Symbol.toStringTag` that can be overwritten, like lodash's `baseGetTag()`.
|
|
393
|
+
// Objects that define a writable tag, like decimal.js numbers, are plain
|
|
394
|
+
// objects then, while built-ins like `URL` keep their own tag.
|
|
395
|
+
function getRawTag(value) {
|
|
396
|
+
const tag = Object.prototype.toString.call(value);
|
|
397
|
+
|
|
398
|
+
if (!(Symbol.toStringTag in value)) {
|
|
399
|
+
return tag;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
const isOwn = Object.prototype.hasOwnProperty.call(value, Symbol.toStringTag);
|
|
403
|
+
const ownTag = value[Symbol.toStringTag];
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
value[Symbol.toStringTag] = undefined;
|
|
407
|
+
} catch (err) {
|
|
408
|
+
return tag;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
const rawTag = Object.prototype.toString.call(value);
|
|
412
|
+
|
|
413
|
+
if (isOwn) {
|
|
414
|
+
value[Symbol.toStringTag] = ownTag;
|
|
415
|
+
} else {
|
|
416
|
+
delete value[Symbol.toStringTag];
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
return rawTag;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// Clones `value` deeply, like lodash's `cloneDeep()`: plain objects and class
|
|
423
|
+
// instances keep their prototype and get copies of their own enumerable
|
|
424
|
+
// properties, symbols included. Arrays, dates, regular expressions, maps,
|
|
425
|
+
// sets, buffers, typed arrays and array buffers are copied, circular
|
|
426
|
+
// references are kept. Anything else, like functions, errors and built-ins
|
|
427
|
+
// with internal state like `URL`, is kept as it is.
|
|
428
|
+
function cloneDeep(value, clones = new Map()) {
|
|
429
|
+
if (!isObject(value)) {
|
|
430
|
+
return value;
|
|
431
|
+
} else if (clones.has(value)) {
|
|
432
|
+
return clones.get(value);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
const Ctor = value.constructor;
|
|
436
|
+
const tag = getRawTag(value);
|
|
437
|
+
let copy;
|
|
438
|
+
|
|
439
|
+
if (tag === '[object Object]') {
|
|
440
|
+
copy = Object.create(Object.getPrototypeOf(value));
|
|
441
|
+
clones.set(value, copy);
|
|
442
|
+
|
|
443
|
+
for (const key of Object.keys(value)) {
|
|
444
|
+
const item = cloneDeep(value[key], clones);
|
|
445
|
+
|
|
446
|
+
if (key === '__proto__') {
|
|
447
|
+
// Assigning it would set the prototype instead of the property.
|
|
448
|
+
Object.defineProperty(copy, key, {
|
|
449
|
+
value: item,
|
|
450
|
+
writable: true,
|
|
451
|
+
enumerable: true,
|
|
452
|
+
configurable: true,
|
|
453
|
+
});
|
|
454
|
+
} else {
|
|
455
|
+
copy[key] = item;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
for (const symbol of Object.getOwnPropertySymbols(value)) {
|
|
460
|
+
if (Object.prototype.propertyIsEnumerable.call(value, symbol)) {
|
|
461
|
+
copy[symbol] = cloneDeep(value[symbol], clones);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
} else if (tag === '[object Array]') {
|
|
465
|
+
copy = new Ctor(value.length);
|
|
466
|
+
clones.set(value, copy);
|
|
467
|
+
|
|
468
|
+
for (let i = 0, l = value.length; i < l; ++i) {
|
|
469
|
+
copy[i] = cloneDeep(value[i], clones);
|
|
470
|
+
}
|
|
471
|
+
} else if (Buffer.isBuffer(value)) {
|
|
472
|
+
copy = Buffer.from(value);
|
|
473
|
+
} else if (tag === '[object DataView]') {
|
|
474
|
+
copy = new Ctor(value.buffer.slice(0), value.byteOffset, value.byteLength);
|
|
475
|
+
} else if (ArrayBuffer.isView(value)) {
|
|
476
|
+
copy = value.slice();
|
|
477
|
+
} else if (tag === '[object ArrayBuffer]') {
|
|
478
|
+
copy = value.slice(0);
|
|
479
|
+
} else if (tag === '[object Date]') {
|
|
480
|
+
copy = new Ctor(+value);
|
|
481
|
+
} else if (tag === '[object RegExp]') {
|
|
482
|
+
copy = new Ctor(value.source, value.flags);
|
|
483
|
+
copy.lastIndex = value.lastIndex;
|
|
484
|
+
} else if (tag === '[object Map]') {
|
|
485
|
+
copy = new Ctor();
|
|
486
|
+
clones.set(value, copy);
|
|
487
|
+
value.forEach((item, key) => copy.set(key, cloneDeep(item, clones)));
|
|
488
|
+
} else if (tag === '[object Set]') {
|
|
489
|
+
copy = new Ctor();
|
|
490
|
+
clones.set(value, copy);
|
|
491
|
+
value.forEach((item) => copy.add(cloneDeep(item, clones)));
|
|
492
|
+
} else {
|
|
493
|
+
return value;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
clones.set(value, copy);
|
|
497
|
+
return copy;
|
|
498
|
+
}
|
|
499
|
+
|
|
392
500
|
module.exports = {
|
|
393
501
|
isEmpty,
|
|
394
502
|
isString,
|
|
@@ -411,7 +519,6 @@ module.exports = {
|
|
|
411
519
|
values,
|
|
412
520
|
union,
|
|
413
521
|
chunk,
|
|
414
|
-
clone,
|
|
415
522
|
omit,
|
|
416
523
|
once,
|
|
417
524
|
last,
|
|
@@ -66,10 +66,17 @@ function toPostgresJsonPath(access) {
|
|
|
66
66
|
// `whereIn(ref(...), values)`), so the keys need to be escaped instead: single
|
|
67
67
|
// quotes are doubled, and `?` is escaped so that knex doesn't read it as a
|
|
68
68
|
// binding placeholder. Backslashes are always doubled by `toPostgresJsonPath()`,
|
|
69
|
-
// so they can't escape the closing quote either.
|
|
69
|
+
// so they can't escape the closing quote either. But knex's Postgres dialect
|
|
70
|
+
// drops all backslashes in front of an escaped `?`, so if the path contains any,
|
|
71
|
+
// an escape string literal is used in which backslashes are written as `\134`.
|
|
70
72
|
function toPostgresJsonPathLiteral(access) {
|
|
71
|
-
const path = toPostgresJsonPath(access).replace(/'/g, "''")
|
|
72
|
-
|
|
73
|
+
const path = toPostgresJsonPath(access).replace(/'/g, "''");
|
|
74
|
+
|
|
75
|
+
if (path.includes('\\?')) {
|
|
76
|
+
return `E'${path.replace(/\\/g, '\\134').replace(/\?/g, '\\?')}'`;
|
|
77
|
+
} else {
|
|
78
|
+
return `'${path.replace(/\?/g, '\\?')}'`;
|
|
79
|
+
}
|
|
73
80
|
}
|
|
74
81
|
|
|
75
82
|
module.exports = {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "objection",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "An SQL-friendly ORM for Node.js",
|
|
5
5
|
"main": "lib/objection.js",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
"test": "npm run eslint && mocha --slow 10 --timeout 15000 --reporter spec --recursive tests --exclude \"tests/unit/relations/files/**\" && npm run test:typings",
|
|
9
9
|
"test:fast": "mocha --slow 10 --timeout 15000 --reporter spec --recursive tests --bail --exclude \"tests/unit/relations/files/**\"",
|
|
10
10
|
"test:typings": "tsc && prettier --check \"typings/**/*.ts\" \"tests/ts/**/*.ts\"",
|
|
11
|
+
"db:up": "docker compose up -d --wait",
|
|
12
|
+
"db:down": "docker compose down -v",
|
|
13
|
+
"build:parsers": "npx --yes peggy@5.1.0 --format commonjs -o lib/queryBuilder/parsers/jsonFieldExpressionParser.js lib/queryBuilder/parsers/jsonFieldExpressionParser.pegjs && npx --yes peggy@5.1.0 --format commonjs -o lib/queryBuilder/parsers/relationExpressionParser.js lib/queryBuilder/parsers/relationExpressionParser.pegjs && prettier --write \"lib/queryBuilder/parsers/*Parser.js\"",
|
|
11
14
|
"prettier": "prettier --write \"{examples,lib,tests,testUtils,typings,doc}/**/*.{js,ts}\"",
|
|
12
|
-
"eslint": "eslint --format codeframe \"examples/**/*.js\" \"lib/**/*.js\" \"tests/**/*.js\"",
|
|
15
|
+
"eslint": "eslint --format codeframe \"examples/**/*.js\" \"lib/**/*.js\" \"tests/**/*.js\" \"testUtils/**/*.js\" \"*.js\"",
|
|
13
16
|
"docs:dev": "vitepress dev doc",
|
|
14
17
|
"docs:build": "vitepress build doc",
|
|
15
18
|
"docs:preview": "vitepress preview doc"
|
|
@@ -374,7 +374,8 @@ declare namespace Objection {
|
|
|
374
374
|
|
|
375
375
|
/**
|
|
376
376
|
* Converts the query builder NQB to the same result kind as QB:
|
|
377
|
-
* array, single, maybe-single or page.
|
|
377
|
+
* array, single, maybe-single or page. Any other result type, e.g. after
|
|
378
|
+
* `castTo()` or `patch()`, falls back to QB, without narrowing.
|
|
378
379
|
*/
|
|
379
380
|
type WithResultKind<QB extends AnyQueryBuilder, NQB extends AnyQueryBuilder> = [
|
|
380
381
|
ResultType<QB>,
|
|
@@ -423,7 +424,9 @@ declare namespace Objection {
|
|
|
423
424
|
? I extends Model
|
|
424
425
|
? I[] | PartialModelObject<I>[]
|
|
425
426
|
: Expression<T[K]>
|
|
426
|
-
:
|
|
427
|
+
: NonNullable<T[K]> extends Model
|
|
428
|
+
? Expression<T[K]> | PartialModelObject<NonNullable<T[K]>> // nullable to-one relations
|
|
429
|
+
: Expression<T[K]>;
|
|
427
430
|
};
|
|
428
431
|
|
|
429
432
|
/**
|
|
@@ -1000,14 +1003,36 @@ declare namespace Objection {
|
|
|
1000
1003
|
includeTriggerModifications?: boolean;
|
|
1001
1004
|
}
|
|
1002
1005
|
|
|
1006
|
+
type ReturningColumn = string | Raw | readonly (string | Raw)[];
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* The arguments of `returning()`: any number of columns or column arrays,
|
|
1010
|
+
* optionally followed by an options object.
|
|
1011
|
+
*/
|
|
1012
|
+
type ReturningArgs =
|
|
1013
|
+
ReturningColumn[] | [ReturningColumn, ...ReturningColumn[], ReturningOptions | undefined];
|
|
1014
|
+
|
|
1003
1015
|
interface ReturningMethod {
|
|
1004
1016
|
<QB extends AnyQueryBuilder>(
|
|
1005
1017
|
this: QB,
|
|
1006
|
-
|
|
1007
|
-
options?: ReturningOptions,
|
|
1018
|
+
...args: ReturningArgs
|
|
1008
1019
|
): QB extends NumberQueryBuilder<QB> ? ArrayQueryBuilder<QB> : QB;
|
|
1009
1020
|
}
|
|
1010
1021
|
|
|
1022
|
+
/**
|
|
1023
|
+
* With `returning()`, `relate()` returns the inserted join rows for
|
|
1024
|
+
* many-to-many relations, the updated related rows for has-many relations
|
|
1025
|
+
* and the updated owner rows for belongs-to-one relations. The typings can't
|
|
1026
|
+
* tell these apart, so the rows are typed as `any`.
|
|
1027
|
+
*/
|
|
1028
|
+
interface RelateReturningMethod<M extends Model> {
|
|
1029
|
+
(...args: ReturningArgs): QueryBuilder<M, any[]>;
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
type RelateQueryBuilder<QB extends AnyQueryBuilder> = {
|
|
1033
|
+
returning: RelateReturningMethod<ModelType<QB>>;
|
|
1034
|
+
} & NumberQueryBuilder<QB>;
|
|
1035
|
+
|
|
1011
1036
|
interface TimeoutOptions {
|
|
1012
1037
|
cancel: boolean;
|
|
1013
1038
|
}
|
|
@@ -1058,8 +1083,9 @@ declare namespace Objection {
|
|
|
1058
1083
|
}
|
|
1059
1084
|
|
|
1060
1085
|
export interface InsertGraphOptions {
|
|
1061
|
-
relate?: boolean | string[];
|
|
1086
|
+
relate?: boolean | string | string[];
|
|
1062
1087
|
allowRefs?: boolean;
|
|
1088
|
+
allowMarkers?: boolean;
|
|
1063
1089
|
}
|
|
1064
1090
|
|
|
1065
1091
|
interface InsertGraphMethod<M extends Model> {
|
|
@@ -1077,16 +1103,17 @@ declare namespace Objection {
|
|
|
1077
1103
|
}
|
|
1078
1104
|
|
|
1079
1105
|
export interface UpsertGraphOptions {
|
|
1080
|
-
relate?: boolean | string[];
|
|
1081
|
-
unrelate?: boolean | string[];
|
|
1082
|
-
insertMissing?: boolean | string[];
|
|
1083
|
-
update?: boolean | string[];
|
|
1084
|
-
noInsert?: boolean | string[];
|
|
1085
|
-
noUpdate?: boolean | string[];
|
|
1086
|
-
noDelete?: boolean | string[];
|
|
1087
|
-
noRelate?: boolean | string[];
|
|
1088
|
-
noUnrelate?: boolean | string[];
|
|
1106
|
+
relate?: boolean | string | string[];
|
|
1107
|
+
unrelate?: boolean | string | string[];
|
|
1108
|
+
insertMissing?: boolean | string | string[];
|
|
1109
|
+
update?: boolean | string | string[];
|
|
1110
|
+
noInsert?: boolean | string | string[];
|
|
1111
|
+
noUpdate?: boolean | string | string[];
|
|
1112
|
+
noDelete?: boolean | string | string[];
|
|
1113
|
+
noRelate?: boolean | string | string[];
|
|
1114
|
+
noUnrelate?: boolean | string | string[];
|
|
1089
1115
|
allowRefs?: boolean;
|
|
1116
|
+
allowMarkers?: boolean;
|
|
1090
1117
|
}
|
|
1091
1118
|
|
|
1092
1119
|
interface UpsertGraphMethod<M extends Model> {
|
|
@@ -1386,7 +1413,7 @@ declare namespace Objection {
|
|
|
1386
1413
|
|
|
1387
1414
|
relate(
|
|
1388
1415
|
ids: MaybeCompositeId | MaybeCompositeId[] | PartialModelObject<M> | PartialModelObject<M>[],
|
|
1389
|
-
):
|
|
1416
|
+
): RelateQueryBuilder<this>;
|
|
1390
1417
|
|
|
1391
1418
|
unrelate(): NumberQueryBuilder<this>;
|
|
1392
1419
|
for(ids: ForIdValue | ForIdValue[]): this;
|
|
@@ -1718,34 +1745,25 @@ declare namespace Objection {
|
|
|
1718
1745
|
constructor(config: AjvConfig);
|
|
1719
1746
|
}
|
|
1720
1747
|
|
|
1721
|
-
|
|
1748
|
+
/**
|
|
1749
|
+
* The options shared by `snakeCaseMappers` and `knexSnakeCaseMappers`.
|
|
1750
|
+
*/
|
|
1751
|
+
export interface SnakeCaseOptions {
|
|
1722
1752
|
upperCase?: boolean;
|
|
1723
1753
|
underscoreBeforeDigits?: boolean;
|
|
1724
1754
|
underscoreBetweenUppercaseLetters?: boolean;
|
|
1725
1755
|
noDoubleUnderscores?: boolean;
|
|
1756
|
+
}
|
|
1757
|
+
|
|
1758
|
+
export interface SnakeCaseMappersOptions extends SnakeCaseOptions {
|
|
1726
1759
|
/**
|
|
1727
1760
|
* Only convert the column part of field expressions like
|
|
1728
1761
|
* `jsonColumn:someKey` and keep their JSON keys as written.
|
|
1729
|
-
* Only used by `snakeCaseMappers`.
|
|
1730
1762
|
*/
|
|
1731
1763
|
preserveJsonKeys?: boolean;
|
|
1732
|
-
/**
|
|
1733
|
-
* Also convert the keys of plain objects one level down in the results,
|
|
1734
|
-
* e.g. the rows of knex's `nestTables: true` option on MySQL. Also applies
|
|
1735
|
-
* to JSON columns parsed by the driver.
|
|
1736
|
-
* Only used by `knexSnakeCaseMappers`.
|
|
1737
|
-
*/
|
|
1738
|
-
mapNestedKeys?: boolean;
|
|
1739
1764
|
}
|
|
1740
1765
|
|
|
1741
|
-
export interface
|
|
1742
|
-
/**
|
|
1743
|
-
* Also convert the keys of plain objects one level down in the results,
|
|
1744
|
-
* e.g. the rows of knex's `nestTables: true` option on MySQL. Also applies
|
|
1745
|
-
* to JSON columns parsed by the driver.
|
|
1746
|
-
*/
|
|
1747
|
-
mapNestedKeys?: boolean;
|
|
1748
|
-
}
|
|
1766
|
+
export interface KnexSnakeCaseMappersOptions extends SnakeCaseOptions {}
|
|
1749
1767
|
|
|
1750
1768
|
export interface ColumnNameMappers {
|
|
1751
1769
|
parse(json: Pojo): Pojo;
|
|
@@ -1762,11 +1780,11 @@ declare namespace Objection {
|
|
|
1762
1780
|
}
|
|
1763
1781
|
|
|
1764
1782
|
export interface KnexSnakeCaseMappersFactory {
|
|
1765
|
-
(options?:
|
|
1783
|
+
(options?: KnexSnakeCaseMappersOptions): KnexMappers;
|
|
1766
1784
|
}
|
|
1767
1785
|
|
|
1768
1786
|
export interface KnexIdentifierMappingFactory {
|
|
1769
|
-
(colToProp: Record<string, string
|
|
1787
|
+
(colToProp: Record<string, string>): KnexMappers;
|
|
1770
1788
|
}
|
|
1771
1789
|
|
|
1772
1790
|
export type ValidationErrorType =
|
|
@@ -2040,14 +2058,16 @@ declare namespace Objection {
|
|
|
2040
2058
|
static bindKnex<M>(this: M, trxOrKnex: TransactionOrKnex): M;
|
|
2041
2059
|
static bindTransaction<M>(this: M, trxOrKnex: TransactionOrKnex): M;
|
|
2042
2060
|
|
|
2043
|
-
|
|
2061
|
+
// E defaults to `string` so that `fetchGraph<Person>(...)` still compiles,
|
|
2062
|
+
// without narrowing, as TypeScript doesn't infer the remaining arguments.
|
|
2063
|
+
static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
|
|
2044
2064
|
this: ConstructorType<M>,
|
|
2045
2065
|
modelOrObject: PartialModelObject<M>,
|
|
2046
2066
|
expression: E,
|
|
2047
2067
|
options?: FetchGraphOptions,
|
|
2048
2068
|
): SingleQueryBuilder<WithGraphModelQueryBuilder<M, E>>;
|
|
2049
2069
|
|
|
2050
|
-
static fetchGraph<M extends Model, const E extends RelationExpression<M
|
|
2070
|
+
static fetchGraph<M extends Model, const E extends RelationExpression<M> = string>(
|
|
2051
2071
|
this: ConstructorType<M>,
|
|
2052
2072
|
modelOrObject: PartialModelObject<M>[],
|
|
2053
2073
|
expression: E,
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { ManyToManyModifyMixin } = require('./ManyToManyModifyMixin');
|
|
4
|
-
|
|
5
|
-
// Like `ManyToManySqliteModifyMixin`, this identifies the join rows to modify
|
|
6
|
-
// by their physical row id, so that only the join rows that match the filters
|
|
7
|
-
// are modified (#1853). `tableoid` makes `ctid` unique for partitioned tables.
|
|
8
|
-
const ManyToManyPostgresModifyMixin = (Operation) => {
|
|
9
|
-
return class extends ManyToManyModifyMixin(Operation) {
|
|
10
|
-
applyModifyFilterForJoinTable(builder) {
|
|
11
|
-
const joinTableOwnerRefs = this.relation.joinTableOwnerProp.refs(builder);
|
|
12
|
-
const tableRef = builder.tableRefFor(this.relation.getJoinModelClass(builder));
|
|
13
|
-
const rowIdRefs = [`${tableRef}.tableoid`, `${tableRef}.ctid`];
|
|
14
|
-
|
|
15
|
-
const ownerValues = this.owner.getProps(this.relation);
|
|
16
|
-
const subquery = this.modifyFilterSubquery.clone().select(rowIdRefs);
|
|
17
|
-
|
|
18
|
-
return builder
|
|
19
|
-
.whereInComposite(rowIdRefs, subquery)
|
|
20
|
-
.whereInComposite(joinTableOwnerRefs, ownerValues);
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
|
|
25
|
-
module.exports = {
|
|
26
|
-
ManyToManyPostgresModifyMixin,
|
|
27
|
-
};
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { ManyToManyUnrelateOperationBase } = require('./ManyToManyUnrelateOperationBase');
|
|
4
|
-
const { ManyToManyPostgresModifyMixin } = require('../ManyToManyPostgresModifyMixin');
|
|
5
|
-
|
|
6
|
-
class ManyToManyUnrelatePostgresOperation extends ManyToManyPostgresModifyMixin(
|
|
7
|
-
ManyToManyUnrelateOperationBase,
|
|
8
|
-
) {
|
|
9
|
-
get modifyMainQuery() {
|
|
10
|
-
return false;
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
module.exports = {
|
|
15
|
-
ManyToManyUnrelatePostgresOperation,
|
|
16
|
-
};
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const { ManyToManyUpdateOperationBase } = require('./ManyToManyUpdateOperationBase');
|
|
4
|
-
const { ManyToManyPostgresModifyMixin } = require('../ManyToManyPostgresModifyMixin');
|
|
5
|
-
|
|
6
|
-
class ManyToManyUpdatePostgresOperation extends ManyToManyPostgresModifyMixin(
|
|
7
|
-
ManyToManyUpdateOperationBase,
|
|
8
|
-
) {}
|
|
9
|
-
|
|
10
|
-
module.exports = {
|
|
11
|
-
ManyToManyUpdatePostgresOperation,
|
|
12
|
-
};
|