@strapi/database 4.5.0-alpha.0 → 4.5.0-beta.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/lib/entity-manager/entity-repository.js +51 -12
- package/lib/entity-manager/index.js +337 -542
- package/lib/entity-manager/morph-relations.js +6 -2
- package/lib/entity-manager/regular-relations.js +283 -0
- package/lib/metadata/index.js +9 -2
- package/lib/metadata/relations.js +15 -2
- package/lib/query/helpers/populate/apply.js +646 -0
- package/lib/query/helpers/populate/index.js +9 -0
- package/lib/query/helpers/populate/process.js +96 -0
- package/lib/query/query-builder.js +29 -0
- package/package.json +2 -2
- package/lib/query/helpers/populate.js +0 -649
|
@@ -12,9 +12,12 @@ const {
|
|
|
12
12
|
isEmpty,
|
|
13
13
|
isArray,
|
|
14
14
|
isNull,
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
uniqWith,
|
|
16
|
+
isEqual,
|
|
17
|
+
differenceWith,
|
|
18
|
+
isNumber,
|
|
19
|
+
map,
|
|
20
|
+
difference,
|
|
18
21
|
} = require('lodash/fp');
|
|
19
22
|
const types = require('../types');
|
|
20
23
|
const { createField } = require('../fields');
|
|
@@ -23,25 +26,24 @@ const { createRepository } = require('./entity-repository');
|
|
|
23
26
|
const { deleteRelatedMorphOneRelationsAfterMorphToManyUpdate } = require('./morph-relations');
|
|
24
27
|
const {
|
|
25
28
|
isBidirectional,
|
|
26
|
-
isOneToAny,
|
|
27
|
-
isManyToAny,
|
|
28
29
|
isAnyToOne,
|
|
29
|
-
|
|
30
|
+
isOneToAny,
|
|
31
|
+
hasOrderColumn,
|
|
32
|
+
hasInverseOrderColumn,
|
|
30
33
|
} = require('../metadata/relations');
|
|
34
|
+
const {
|
|
35
|
+
deletePreviousOneToAnyRelations,
|
|
36
|
+
deletePreviousAnyToOneRelations,
|
|
37
|
+
deleteRelations,
|
|
38
|
+
cleanOrderColumns,
|
|
39
|
+
} = require('./regular-relations');
|
|
31
40
|
|
|
32
41
|
const toId = (value) => value.id || value;
|
|
33
42
|
const toIds = (value) => castArray(value || []).map(toId);
|
|
34
43
|
|
|
35
44
|
const isValidId = (value) => isString(value) || isInteger(value);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
return {
|
|
39
|
-
connect: toAssocs(data.connect),
|
|
40
|
-
disconnect: toAssocs(data.disconnect),
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return castArray(data)
|
|
45
|
+
const toIdArray = (data) => {
|
|
46
|
+
const array = castArray(data)
|
|
45
47
|
.filter((datum) => !isNil(datum))
|
|
46
48
|
.map((datum) => {
|
|
47
49
|
// if it is a string or an integer return an obj with id = to datum
|
|
@@ -56,6 +58,26 @@ const toAssocs = (data) => {
|
|
|
56
58
|
|
|
57
59
|
return datum;
|
|
58
60
|
});
|
|
61
|
+
return uniqWith(isEqual, array);
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const toAssocs = (data) => {
|
|
65
|
+
if (isArray(data) || isString(data) || isNumber(data) || isNull(data) || data?.id) {
|
|
66
|
+
return {
|
|
67
|
+
set: isNull(data) ? data : toIdArray(data),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (data?.set) {
|
|
72
|
+
return {
|
|
73
|
+
set: isNull(data.set) ? data.set : toIdArray(data.set),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return {
|
|
78
|
+
connect: toIdArray(data?.connect),
|
|
79
|
+
disconnect: toIdArray(data?.disconnect),
|
|
80
|
+
};
|
|
59
81
|
};
|
|
60
82
|
|
|
61
83
|
const processData = (metadata, data = {}, { withDefaults = false } = {}) => {
|
|
@@ -189,12 +211,24 @@ const createEntityManager = (db) => {
|
|
|
189
211
|
}
|
|
190
212
|
|
|
191
213
|
const dataToInsert = processData(metadata, data, { withDefaults: true });
|
|
214
|
+
let id;
|
|
215
|
+
|
|
216
|
+
const trx = await strapi.db.transaction();
|
|
217
|
+
try {
|
|
218
|
+
const res = await this.createQueryBuilder(uid)
|
|
219
|
+
.insert(dataToInsert)
|
|
220
|
+
.transacting(trx)
|
|
221
|
+
.execute();
|
|
192
222
|
|
|
193
|
-
|
|
223
|
+
id = res[0].id || res[0];
|
|
194
224
|
|
|
195
|
-
|
|
225
|
+
await this.attachRelations(uid, id, data, { transaction: trx });
|
|
196
226
|
|
|
197
|
-
|
|
227
|
+
await trx.commit();
|
|
228
|
+
} catch (e) {
|
|
229
|
+
await trx.rollback();
|
|
230
|
+
throw e;
|
|
231
|
+
}
|
|
198
232
|
|
|
199
233
|
// TODO: in case there is no select or populate specified return the inserted data ?
|
|
200
234
|
// TODO: do not trigger the findOne lifecycles ?
|
|
@@ -259,13 +293,25 @@ const createEntityManager = (db) => {
|
|
|
259
293
|
|
|
260
294
|
const { id } = entity;
|
|
261
295
|
|
|
262
|
-
const
|
|
296
|
+
const trx = await strapi.db.transaction();
|
|
297
|
+
try {
|
|
298
|
+
const dataToUpdate = processData(metadata, data);
|
|
263
299
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
300
|
+
if (!isEmpty(dataToUpdate)) {
|
|
301
|
+
await this.createQueryBuilder(uid)
|
|
302
|
+
.where({ id })
|
|
303
|
+
.update(dataToUpdate)
|
|
304
|
+
.transacting(trx)
|
|
305
|
+
.execute();
|
|
306
|
+
}
|
|
267
307
|
|
|
268
|
-
|
|
308
|
+
await this.updateRelations(uid, id, data, { transaction: trx });
|
|
309
|
+
|
|
310
|
+
await trx.commit();
|
|
311
|
+
} catch (e) {
|
|
312
|
+
await trx.rollback();
|
|
313
|
+
throw e;
|
|
314
|
+
}
|
|
269
315
|
|
|
270
316
|
// TODO: do not trigger the findOne lifecycles ?
|
|
271
317
|
const result = await this.findOne(uid, {
|
|
@@ -326,9 +372,17 @@ const createEntityManager = (db) => {
|
|
|
326
372
|
|
|
327
373
|
const { id } = entity;
|
|
328
374
|
|
|
329
|
-
|
|
375
|
+
const trx = await strapi.db.transaction();
|
|
376
|
+
try {
|
|
377
|
+
await this.createQueryBuilder(uid).where({ id }).delete().transacting(trx).execute();
|
|
330
378
|
|
|
331
|
-
|
|
379
|
+
await this.deleteRelations(uid, id, { transaction: trx });
|
|
380
|
+
|
|
381
|
+
await trx.commit();
|
|
382
|
+
} catch (e) {
|
|
383
|
+
await trx.rollback();
|
|
384
|
+
throw e;
|
|
385
|
+
}
|
|
332
386
|
|
|
333
387
|
await db.lifecycles.run('afterDelete', uid, { params, result: entity }, states);
|
|
334
388
|
|
|
@@ -358,8 +412,7 @@ const createEntityManager = (db) => {
|
|
|
358
412
|
* @param {ID} id - entity ID
|
|
359
413
|
* @param {object} data - data received for creation
|
|
360
414
|
*/
|
|
361
|
-
|
|
362
|
-
async attachRelations(uid, id, data) {
|
|
415
|
+
async attachRelations(uid, id, data, { transaction: trx }) {
|
|
363
416
|
const { attributes } = db.metadata.get(uid);
|
|
364
417
|
|
|
365
418
|
for (const attributeName of Object.keys(attributes)) {
|
|
@@ -371,6 +424,8 @@ const createEntityManager = (db) => {
|
|
|
371
424
|
continue;
|
|
372
425
|
}
|
|
373
426
|
|
|
427
|
+
const cleanRelationData = toAssocs(data[attributeName]);
|
|
428
|
+
|
|
374
429
|
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
375
430
|
const { target, morphBy } = attribute;
|
|
376
431
|
|
|
@@ -380,9 +435,12 @@ const createEntityManager = (db) => {
|
|
|
380
435
|
// set columns
|
|
381
436
|
const { idColumn, typeColumn } = targetAttribute.morphColumn;
|
|
382
437
|
|
|
438
|
+
const relId = toId(cleanRelationData.set[0]);
|
|
439
|
+
|
|
383
440
|
await this.createQueryBuilder(target)
|
|
384
441
|
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
385
|
-
.where({ id:
|
|
442
|
+
.where({ id: relId })
|
|
443
|
+
.transacting(trx)
|
|
386
444
|
.execute();
|
|
387
445
|
} else if (targetAttribute.relation === 'morphToMany') {
|
|
388
446
|
const { joinTable } = targetAttribute;
|
|
@@ -390,7 +448,11 @@ const createEntityManager = (db) => {
|
|
|
390
448
|
|
|
391
449
|
const { idColumn, typeColumn } = morphColumn;
|
|
392
450
|
|
|
393
|
-
|
|
451
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
452
|
+
continue;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
const rows = cleanRelationData.set.map((data, idx) => {
|
|
394
456
|
return {
|
|
395
457
|
[joinColumn.name]: data.id,
|
|
396
458
|
[idColumn.name]: id,
|
|
@@ -402,11 +464,7 @@ const createEntityManager = (db) => {
|
|
|
402
464
|
};
|
|
403
465
|
});
|
|
404
466
|
|
|
405
|
-
|
|
406
|
-
continue;
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
467
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
410
468
|
}
|
|
411
469
|
|
|
412
470
|
continue;
|
|
@@ -419,40 +477,44 @@ const createEntityManager = (db) => {
|
|
|
419
477
|
|
|
420
478
|
const { idColumn, typeColumn, typeField = '__type' } = morphColumn;
|
|
421
479
|
|
|
422
|
-
|
|
480
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
481
|
+
continue;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
423
485
|
[joinColumn.name]: id,
|
|
424
486
|
[idColumn.name]: data.id,
|
|
425
487
|
[typeColumn.name]: data[typeField],
|
|
426
488
|
...(joinTable.on || {}),
|
|
427
489
|
...(data.__pivot || {}),
|
|
490
|
+
order: idx + 1,
|
|
428
491
|
}));
|
|
429
492
|
|
|
430
|
-
if (isEmpty(rows)) {
|
|
431
|
-
continue;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
493
|
// delete previous relations
|
|
435
494
|
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
436
495
|
uid,
|
|
437
496
|
attributeName,
|
|
438
497
|
joinTable,
|
|
439
498
|
db,
|
|
499
|
+
transaction: trx,
|
|
440
500
|
});
|
|
441
501
|
|
|
442
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
502
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
443
503
|
|
|
444
504
|
continue;
|
|
445
505
|
}
|
|
446
506
|
|
|
447
507
|
if (attribute.joinColumn && attribute.owner) {
|
|
508
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
448
509
|
if (
|
|
449
510
|
attribute.relation === 'oneToOne' &&
|
|
450
511
|
isBidirectional(attribute) &&
|
|
451
|
-
|
|
512
|
+
relIdsToAdd.length
|
|
452
513
|
) {
|
|
453
514
|
await this.createQueryBuilder(uid)
|
|
454
|
-
.where({ [attribute.joinColumn.name]:
|
|
515
|
+
.where({ [attribute.joinColumn.name]: relIdsToAdd, id: { $ne: id } })
|
|
455
516
|
.update({ [attribute.joinColumn.name]: null })
|
|
517
|
+
.transacting(trx)
|
|
456
518
|
.execute();
|
|
457
519
|
}
|
|
458
520
|
|
|
@@ -465,16 +527,19 @@ const createEntityManager = (db) => {
|
|
|
465
527
|
const { target } = attribute;
|
|
466
528
|
|
|
467
529
|
// TODO: check it is an id & the entity exists (will throw due to FKs otherwise so not a big pbl in SQL)
|
|
530
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
468
531
|
|
|
469
532
|
await this.createQueryBuilder(target)
|
|
470
533
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
471
534
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
535
|
+
.transacting(trx)
|
|
472
536
|
.execute();
|
|
473
537
|
|
|
474
538
|
await this.createQueryBuilder(target)
|
|
475
539
|
.update({ [attribute.joinColumn.referencedColumn]: id })
|
|
476
540
|
// NOTE: works if it is an array or a single id
|
|
477
|
-
.where({ id:
|
|
541
|
+
.where({ id: relIdsToAdd })
|
|
542
|
+
.transacting(trx)
|
|
478
543
|
.execute();
|
|
479
544
|
}
|
|
480
545
|
|
|
@@ -484,50 +549,18 @@ const createEntityManager = (db) => {
|
|
|
484
549
|
const { joinTable } = attribute;
|
|
485
550
|
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } =
|
|
486
551
|
joinTable;
|
|
487
|
-
const select = [joinColumn.name];
|
|
488
|
-
if (isAnyToMany(attribute)) {
|
|
489
|
-
select.push(orderColumnName);
|
|
490
|
-
}
|
|
491
552
|
|
|
492
|
-
const
|
|
493
|
-
const relsToAdd = uniqBy('id', cleanRelationData.connect || cleanRelationData);
|
|
553
|
+
const relsToAdd = cleanRelationData.set || cleanRelationData.connect;
|
|
494
554
|
const relIdsToadd = toIds(relsToAdd);
|
|
495
555
|
|
|
496
|
-
// need to delete the previous relations for oneToAny relations
|
|
497
556
|
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
})
|
|
506
|
-
.where(joinTable.on || {})
|
|
507
|
-
.execute();
|
|
508
|
-
|
|
509
|
-
currentRelsToDelete.sort((a, b) => b[orderColumnName] - a[orderColumnName]);
|
|
510
|
-
|
|
511
|
-
for (const relToDelete of currentRelsToDelete) {
|
|
512
|
-
if (relToDelete[orderColumnName] !== null) {
|
|
513
|
-
await this.createQueryBuilder(joinTable.name)
|
|
514
|
-
.decrement(orderColumnName, 1)
|
|
515
|
-
.where({
|
|
516
|
-
[joinColumn.name]: relToDelete[joinColumn.name],
|
|
517
|
-
[orderColumnName]: { $gt: relToDelete[orderColumnName] },
|
|
518
|
-
})
|
|
519
|
-
.where(joinTable.on || {})
|
|
520
|
-
.execute();
|
|
521
|
-
}
|
|
522
|
-
}
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
// delete previous oneToAny relations
|
|
526
|
-
await this.createQueryBuilder(joinTable.name)
|
|
527
|
-
.delete()
|
|
528
|
-
.where({ [inverseJoinColumn.name]: relIdsToadd })
|
|
529
|
-
.where(joinTable.on || {})
|
|
530
|
-
.execute();
|
|
557
|
+
await deletePreviousOneToAnyRelations({
|
|
558
|
+
id,
|
|
559
|
+
attribute,
|
|
560
|
+
relIdsToadd,
|
|
561
|
+
db,
|
|
562
|
+
transaction: trx,
|
|
563
|
+
});
|
|
531
564
|
}
|
|
532
565
|
|
|
533
566
|
// prepare new relations to insert
|
|
@@ -540,30 +573,31 @@ const createEntityManager = (db) => {
|
|
|
540
573
|
};
|
|
541
574
|
});
|
|
542
575
|
|
|
543
|
-
// add order value
|
|
544
|
-
if (
|
|
576
|
+
// add order value
|
|
577
|
+
if (hasOrderColumn(attribute)) {
|
|
545
578
|
insert.forEach((rel, idx) => {
|
|
546
579
|
rel[orderColumnName] = idx + 1;
|
|
547
580
|
});
|
|
548
581
|
}
|
|
549
|
-
// add inv_order value
|
|
550
|
-
if (
|
|
551
|
-
const
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
582
|
+
// add inv_order value
|
|
583
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
584
|
+
const maxResults = await db
|
|
585
|
+
.getConnection()
|
|
586
|
+
.select(inverseJoinColumn.name)
|
|
587
|
+
.max(inverseOrderColumnName, { as: 'max' })
|
|
588
|
+
.whereIn(inverseJoinColumn.name, relIdsToadd)
|
|
589
|
+
.where(joinTable.on || {})
|
|
590
|
+
.groupBy(inverseJoinColumn.name)
|
|
591
|
+
.from(joinTable.name)
|
|
592
|
+
.transacting(trx);
|
|
560
593
|
|
|
561
|
-
|
|
562
|
-
})
|
|
594
|
+
const maxMap = maxResults.reduce(
|
|
595
|
+
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
596
|
+
{}
|
|
563
597
|
);
|
|
564
598
|
|
|
565
599
|
insert.forEach((rel) => {
|
|
566
|
-
rel[inverseOrderColumnName] = maxMap[rel[inverseJoinColumn.name]] + 1;
|
|
600
|
+
rel[inverseOrderColumnName] = (maxMap[rel[inverseJoinColumn.name]] || 0) + 1;
|
|
567
601
|
});
|
|
568
602
|
}
|
|
569
603
|
|
|
@@ -572,7 +606,7 @@ const createEntityManager = (db) => {
|
|
|
572
606
|
}
|
|
573
607
|
|
|
574
608
|
// insert new relations
|
|
575
|
-
await this.createQueryBuilder(joinTable.name).insert(insert).execute();
|
|
609
|
+
await this.createQueryBuilder(joinTable.name).insert(insert).transacting(trx).execute();
|
|
576
610
|
}
|
|
577
611
|
}
|
|
578
612
|
},
|
|
@@ -586,8 +620,7 @@ const createEntityManager = (db) => {
|
|
|
586
620
|
* @param {object} data - data received for creation
|
|
587
621
|
*/
|
|
588
622
|
// TODO: check relation exists (handled by FKs except for polymorphics)
|
|
589
|
-
|
|
590
|
-
async updateRelations(uid, id, data) {
|
|
623
|
+
async updateRelations(uid, id, data, { transaction: trx }) {
|
|
591
624
|
const { attributes } = db.metadata.get(uid);
|
|
592
625
|
|
|
593
626
|
for (const attributeName of Object.keys(attributes)) {
|
|
@@ -596,6 +629,7 @@ const createEntityManager = (db) => {
|
|
|
596
629
|
if (attribute.type !== 'relation' || !has(attributeName, data)) {
|
|
597
630
|
continue;
|
|
598
631
|
}
|
|
632
|
+
const cleanRelationData = toAssocs(data[attributeName]);
|
|
599
633
|
|
|
600
634
|
if (attribute.relation === 'morphOne' || attribute.relation === 'morphMany') {
|
|
601
635
|
const { target, morphBy } = attribute;
|
|
@@ -611,12 +645,15 @@ const createEntityManager = (db) => {
|
|
|
611
645
|
await this.createQueryBuilder(target)
|
|
612
646
|
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
613
647
|
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
648
|
+
.transacting(trx)
|
|
614
649
|
.execute();
|
|
615
650
|
|
|
616
|
-
if (!isNull(
|
|
651
|
+
if (!isNull(cleanRelationData.set)) {
|
|
652
|
+
const relId = toIds(cleanRelationData.set[0]);
|
|
617
653
|
await this.createQueryBuilder(target)
|
|
618
654
|
.update({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
619
|
-
.where({ id:
|
|
655
|
+
.where({ id: relId })
|
|
656
|
+
.transacting(trx)
|
|
620
657
|
.execute();
|
|
621
658
|
}
|
|
622
659
|
} else if (targetAttribute.relation === 'morphToMany') {
|
|
@@ -633,9 +670,14 @@ const createEntityManager = (db) => {
|
|
|
633
670
|
...(joinTable.on || {}),
|
|
634
671
|
field: attributeName,
|
|
635
672
|
})
|
|
673
|
+
.transacting(trx)
|
|
636
674
|
.execute();
|
|
637
675
|
|
|
638
|
-
|
|
676
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
639
681
|
[joinColumn.name]: data.id,
|
|
640
682
|
[idColumn.name]: id,
|
|
641
683
|
[typeColumn.name]: uid,
|
|
@@ -645,11 +687,7 @@ const createEntityManager = (db) => {
|
|
|
645
687
|
field: attributeName,
|
|
646
688
|
}));
|
|
647
689
|
|
|
648
|
-
|
|
649
|
-
continue;
|
|
650
|
-
}
|
|
651
|
-
|
|
652
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
690
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
653
691
|
}
|
|
654
692
|
|
|
655
693
|
continue;
|
|
@@ -672,29 +710,32 @@ const createEntityManager = (db) => {
|
|
|
672
710
|
[joinColumn.name]: id,
|
|
673
711
|
...(joinTable.on || {}),
|
|
674
712
|
})
|
|
713
|
+
.transacting(trx)
|
|
675
714
|
.execute();
|
|
676
715
|
|
|
677
|
-
|
|
716
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
717
|
+
continue;
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
const rows = cleanRelationData.set.map((data, idx) => ({
|
|
678
721
|
[joinColumn.name]: id,
|
|
679
722
|
[idColumn.name]: data.id,
|
|
680
723
|
[typeColumn.name]: data[typeField],
|
|
681
724
|
...(joinTable.on || {}),
|
|
682
725
|
...(data.__pivot || {}),
|
|
726
|
+
order: idx + 1,
|
|
683
727
|
}));
|
|
684
728
|
|
|
685
|
-
if (isEmpty(rows)) {
|
|
686
|
-
continue;
|
|
687
|
-
}
|
|
688
|
-
|
|
689
729
|
// delete previous relations
|
|
690
730
|
await deleteRelatedMorphOneRelationsAfterMorphToManyUpdate(rows, {
|
|
691
731
|
uid,
|
|
692
732
|
attributeName,
|
|
693
733
|
joinTable,
|
|
694
734
|
db,
|
|
735
|
+
transaction: trx,
|
|
695
736
|
});
|
|
696
737
|
|
|
697
|
-
await this.createQueryBuilder(joinTable.name).insert(rows).execute();
|
|
738
|
+
await this.createQueryBuilder(joinTable.name).insert(rows).transacting(trx).execute();
|
|
698
739
|
|
|
699
740
|
continue;
|
|
700
741
|
}
|
|
@@ -713,13 +754,15 @@ const createEntityManager = (db) => {
|
|
|
713
754
|
await this.createQueryBuilder(target)
|
|
714
755
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
715
756
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
757
|
+
.transacting(trx)
|
|
716
758
|
.execute();
|
|
717
759
|
|
|
718
|
-
if (!isNull(
|
|
760
|
+
if (!isNull(cleanRelationData.set)) {
|
|
761
|
+
const relIdsToAdd = toIds(cleanRelationData.set);
|
|
719
762
|
await this.createQueryBuilder(target)
|
|
720
|
-
|
|
721
|
-
.where({ id: data[attributeName] })
|
|
763
|
+
.where({ id: relIdsToAdd })
|
|
722
764
|
.update({ [attribute.joinColumn.referencedColumn]: id })
|
|
765
|
+
.transacting(trx)
|
|
723
766
|
.execute();
|
|
724
767
|
}
|
|
725
768
|
}
|
|
@@ -729,430 +772,224 @@ const createEntityManager = (db) => {
|
|
|
729
772
|
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } =
|
|
730
773
|
joinTable;
|
|
731
774
|
const select = [joinColumn.name, inverseJoinColumn.name];
|
|
732
|
-
if (
|
|
775
|
+
if (hasOrderColumn(attribute)) {
|
|
733
776
|
select.push(orderColumnName);
|
|
734
777
|
}
|
|
735
|
-
if (
|
|
778
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
736
779
|
select.push(inverseOrderColumnName);
|
|
737
780
|
}
|
|
738
781
|
|
|
739
782
|
// only delete relations
|
|
740
|
-
if (isNull(
|
|
741
|
-
|
|
742
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
743
|
-
let lastId = 0;
|
|
744
|
-
let done = false;
|
|
745
|
-
const batchSize = 100;
|
|
746
|
-
while (!done) {
|
|
747
|
-
const relsToDelete = await this.createQueryBuilder(joinTable.name)
|
|
748
|
-
.select(select)
|
|
749
|
-
.where({
|
|
750
|
-
[joinColumn.name]: id,
|
|
751
|
-
id: { $gt: lastId },
|
|
752
|
-
})
|
|
753
|
-
.where(joinTable.on || {})
|
|
754
|
-
.orderBy('id')
|
|
755
|
-
.limit(batchSize)
|
|
756
|
-
.execute();
|
|
757
|
-
// TODO: cannot put pivot here...
|
|
758
|
-
done = relsToDelete.length < batchSize;
|
|
759
|
-
lastId = relsToDelete[relsToDelete.length - 1]?.id;
|
|
760
|
-
|
|
761
|
-
const updateInverseOrderPromises = [];
|
|
762
|
-
for (const relToDelete of relsToDelete) {
|
|
763
|
-
if (relToDelete[inverseOrderColumnName] !== null) {
|
|
764
|
-
const updatePromise = this.createQueryBuilder(joinTable.name)
|
|
765
|
-
.decrement(inverseOrderColumnName, 1)
|
|
766
|
-
.where({
|
|
767
|
-
[inverseJoinColumn.name]: relToDelete[inverseJoinColumn.name],
|
|
768
|
-
[inverseOrderColumnName]: { $gt: relToDelete[inverseOrderColumnName] },
|
|
769
|
-
})
|
|
770
|
-
.where(joinTable.on || {})
|
|
771
|
-
.execute();
|
|
772
|
-
updateInverseOrderPromises.push(updatePromise);
|
|
773
|
-
}
|
|
774
|
-
}
|
|
775
|
-
|
|
776
|
-
await Promise.all(updateInverseOrderPromises);
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
await this.createQueryBuilder(joinTable.name)
|
|
781
|
-
.delete()
|
|
782
|
-
.where({ [joinColumn.name]: id })
|
|
783
|
-
.where(joinTable.on || {})
|
|
784
|
-
.execute();
|
|
783
|
+
if (isNull(cleanRelationData.set)) {
|
|
784
|
+
await deleteRelations({ id, attribute, db, relIdsToDelete: 'all', transaction: trx });
|
|
785
785
|
} else {
|
|
786
|
-
const
|
|
787
|
-
const isPartialUpdate =
|
|
788
|
-
has('connect', cleanRelationData) || has('disconnect', cleanRelationData);
|
|
786
|
+
const isPartialUpdate = !has('set', cleanRelationData);
|
|
789
787
|
let relIdsToaddOrMove;
|
|
790
788
|
|
|
791
789
|
if (isPartialUpdate) {
|
|
792
|
-
// does not support pivot
|
|
793
|
-
let connect = uniqBy('id', cleanRelationData.connect || []);
|
|
794
|
-
let disconnect = uniqBy('id', cleanRelationData.disconnect || []);
|
|
795
790
|
if (isAnyToOne(attribute)) {
|
|
796
|
-
connect = connect.slice(-1);
|
|
797
|
-
|
|
791
|
+
cleanRelationData.connect = cleanRelationData.connect.slice(-1);
|
|
792
|
+
}
|
|
793
|
+
relIdsToaddOrMove = toIds(cleanRelationData.connect);
|
|
794
|
+
const relIdsToDelete = toIds(
|
|
795
|
+
differenceWith(isEqual, cleanRelationData.disconnect, cleanRelationData.connect)
|
|
796
|
+
);
|
|
797
|
+
|
|
798
|
+
if (!isEmpty(relIdsToDelete)) {
|
|
799
|
+
await deleteRelations({ id, attribute, db, relIdsToDelete, transaction: trx });
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
if (isEmpty(cleanRelationData.connect)) {
|
|
803
|
+
continue;
|
|
798
804
|
}
|
|
799
|
-
|
|
800
|
-
//
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
if (
|
|
805
|
-
isAnyToMany(attribute) ||
|
|
806
|
-
(isBidirectional(attribute) && isManyToAny(attribute))
|
|
807
|
-
) {
|
|
808
|
-
const relsToDelete = await this.createQueryBuilder(joinTable.name)
|
|
805
|
+
|
|
806
|
+
// Fetch current relations to handle ordering
|
|
807
|
+
let currentMovingRels;
|
|
808
|
+
if (hasOrderColumn(attribute) || hasInverseOrderColumn(attribute)) {
|
|
809
|
+
currentMovingRels = await this.createQueryBuilder(joinTable.name)
|
|
809
810
|
.select(select)
|
|
810
811
|
.where({
|
|
811
812
|
[joinColumn.name]: id,
|
|
812
|
-
[inverseJoinColumn.name]: { $in:
|
|
813
|
+
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
813
814
|
})
|
|
814
815
|
.where(joinTable.on || {})
|
|
816
|
+
.transacting(trx)
|
|
815
817
|
.execute();
|
|
816
|
-
|
|
817
|
-
// ORDER UPDATE
|
|
818
|
-
if (isAnyToMany(attribute)) {
|
|
819
|
-
// sort by order DESC so that the order updates are done in the correct order
|
|
820
|
-
// avoiding one to interfere with the others
|
|
821
|
-
relsToDelete.sort((a, b) => b[orderColumnName] - a[orderColumnName]);
|
|
822
|
-
|
|
823
|
-
for (const relToDelete of relsToDelete) {
|
|
824
|
-
if (relToDelete[orderColumnName] !== null) {
|
|
825
|
-
await this.createQueryBuilder(joinTable.name)
|
|
826
|
-
.decrement(orderColumnName, 1)
|
|
827
|
-
.where({
|
|
828
|
-
[joinColumn.name]: id,
|
|
829
|
-
[orderColumnName]: { $gt: relToDelete[orderColumnName] },
|
|
830
|
-
})
|
|
831
|
-
.where(joinTable.on || {})
|
|
832
|
-
.execute();
|
|
833
|
-
}
|
|
834
|
-
}
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
// INVERSE ORDER UPDATE
|
|
838
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
839
|
-
const updateInverseOrderPromises = [];
|
|
840
|
-
for (const relToDelete of relsToDelete) {
|
|
841
|
-
if (relToDelete[inverseOrderColumnName] !== null) {
|
|
842
|
-
const updatePromise = this.createQueryBuilder(joinTable.name)
|
|
843
|
-
.decrement(inverseOrderColumnName, 1)
|
|
844
|
-
.where({
|
|
845
|
-
[inverseJoinColumn.name]: relToDelete[inverseJoinColumn.name],
|
|
846
|
-
[inverseOrderColumnName]: { $gt: relToDelete[inverseOrderColumnName] },
|
|
847
|
-
})
|
|
848
|
-
.where(joinTable.on || {})
|
|
849
|
-
.execute();
|
|
850
|
-
updateInverseOrderPromises.push(updatePromise);
|
|
851
|
-
}
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
await Promise.all(updateInverseOrderPromises);
|
|
855
|
-
}
|
|
856
818
|
}
|
|
857
819
|
|
|
858
|
-
//
|
|
859
|
-
|
|
860
|
-
.
|
|
861
|
-
.
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
.where(joinTable.on || {})
|
|
866
|
-
.execute();
|
|
867
|
-
|
|
868
|
-
// add/move
|
|
869
|
-
let max;
|
|
870
|
-
const currentMovingRels = await this.createQueryBuilder(joinTable.name)
|
|
871
|
-
.select(select)
|
|
872
|
-
.where({
|
|
873
|
-
[joinColumn.name]: id,
|
|
874
|
-
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
875
|
-
})
|
|
876
|
-
.where(joinTable.on || {})
|
|
877
|
-
.execute();
|
|
878
|
-
const currentMovingRelsMap = groupBy(inverseJoinColumn.name, currentMovingRels);
|
|
820
|
+
// prepare relations to insert
|
|
821
|
+
const insert = cleanRelationData.connect.map((relToAdd) => ({
|
|
822
|
+
[joinColumn.name]: id,
|
|
823
|
+
[inverseJoinColumn.name]: relToAdd.id,
|
|
824
|
+
...(joinTable.on || {}),
|
|
825
|
+
...(relToAdd.__pivot || {}),
|
|
826
|
+
}));
|
|
879
827
|
|
|
880
|
-
|
|
881
|
-
|
|
828
|
+
// add order value
|
|
829
|
+
if (hasOrderColumn(attribute)) {
|
|
830
|
+
const orderMax = (
|
|
882
831
|
await this.createQueryBuilder(joinTable.name)
|
|
883
832
|
.max(orderColumnName)
|
|
884
833
|
.where({ [joinColumn.name]: id })
|
|
885
834
|
.where(joinTable.on || {})
|
|
886
835
|
.first()
|
|
836
|
+
.transacting(trx)
|
|
887
837
|
.execute()
|
|
888
838
|
).max;
|
|
839
|
+
|
|
840
|
+
insert.forEach((row, idx) => {
|
|
841
|
+
row[orderColumnName] = orderMax + idx + 1;
|
|
842
|
+
});
|
|
889
843
|
}
|
|
890
844
|
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
const
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
845
|
+
// add inv order value
|
|
846
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
847
|
+
const nonExistingRelsIds = difference(
|
|
848
|
+
relIdsToaddOrMove,
|
|
849
|
+
map(inverseJoinColumn.name, currentMovingRels)
|
|
850
|
+
);
|
|
851
|
+
|
|
852
|
+
const maxResults = await db
|
|
853
|
+
.getConnection()
|
|
854
|
+
.select(inverseJoinColumn.name)
|
|
855
|
+
.max(inverseOrderColumnName, { as: 'max' })
|
|
856
|
+
.whereIn(inverseJoinColumn.name, nonExistingRelsIds)
|
|
857
|
+
.where(joinTable.on || {})
|
|
858
|
+
.groupBy(inverseJoinColumn.name)
|
|
859
|
+
.from(joinTable.name)
|
|
860
|
+
.transacting(trx);
|
|
861
|
+
|
|
862
|
+
const maxMap = maxResults.reduce(
|
|
863
|
+
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
864
|
+
{}
|
|
865
|
+
);
|
|
866
|
+
|
|
867
|
+
insert.forEach((row) => {
|
|
868
|
+
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
869
|
+
});
|
|
870
|
+
}
|
|
912
871
|
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
} else if (!currentRel) {
|
|
924
|
-
const insert = {
|
|
925
|
-
[joinColumn.name]: id,
|
|
926
|
-
[inverseJoinColumn.name]: relToAddOrMove.id,
|
|
927
|
-
...(relToAddOrMove.__pivot || {}),
|
|
928
|
-
};
|
|
929
|
-
|
|
930
|
-
if (isAnyToMany(attribute)) {
|
|
931
|
-
insert[orderColumnName] = max + 1;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
935
|
-
const { max: reverseMax } = await this.createQueryBuilder(joinTable.name)
|
|
936
|
-
.max(inverseOrderColumnName)
|
|
937
|
-
.where({ [inverseJoinColumn.name]: relToAddOrMove.id })
|
|
938
|
-
.where(joinTable.on || {})
|
|
939
|
-
.first()
|
|
940
|
-
.execute();
|
|
941
|
-
|
|
942
|
-
insert[inverseOrderColumnName] = reverseMax + 1;
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
await this.createQueryBuilder(joinTable.name).insert(insert).execute();
|
|
946
|
-
max += 1;
|
|
947
|
-
}
|
|
872
|
+
// insert rows
|
|
873
|
+
const query = this.createQueryBuilder(joinTable.name)
|
|
874
|
+
.insert(insert)
|
|
875
|
+
.onConflict(joinTable.pivotColumns)
|
|
876
|
+
.transacting(trx);
|
|
877
|
+
|
|
878
|
+
if (hasOrderColumn(attribute)) {
|
|
879
|
+
query.merge([orderColumnName]);
|
|
880
|
+
} else {
|
|
881
|
+
query.ignore();
|
|
948
882
|
}
|
|
883
|
+
|
|
884
|
+
await query.execute();
|
|
885
|
+
|
|
886
|
+
// remove gap between orders
|
|
887
|
+
await cleanOrderColumns({ attribute, db, id, transaction: trx });
|
|
949
888
|
} else {
|
|
889
|
+
if (isAnyToOne(attribute)) {
|
|
890
|
+
cleanRelationData.set = cleanRelationData.set.slice(-1);
|
|
891
|
+
}
|
|
950
892
|
// overwrite all relations
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
[joinColumn.name]: id,
|
|
964
|
-
[inverseJoinColumn.name]: { $notIn: relIdsToaddOrMove },
|
|
965
|
-
id: { $gt: lastId },
|
|
966
|
-
})
|
|
967
|
-
.where(joinTable.on || {})
|
|
968
|
-
.orderBy('id')
|
|
969
|
-
.limit(batchSize)
|
|
970
|
-
.execute();
|
|
971
|
-
|
|
972
|
-
done = relsToDelete.length < batchSize;
|
|
973
|
-
lastId = relsToDelete[relsToDelete.length - 1]?.id;
|
|
974
|
-
|
|
975
|
-
// ORDER UPDATE
|
|
976
|
-
if (isAnyToMany(attribute)) {
|
|
977
|
-
// sort by order DESC so that the order updates are done in the correct order
|
|
978
|
-
// avoiding one to interfere with the others
|
|
979
|
-
relsToDelete.sort((a, b) => b[orderColumnName] - a[orderColumnName]);
|
|
980
|
-
|
|
981
|
-
for (const relToDelete of relsToDelete) {
|
|
982
|
-
if (relToDelete[orderColumnName] !== null) {
|
|
983
|
-
await this.createQueryBuilder(joinTable.name)
|
|
984
|
-
.decrement(orderColumnName, 1)
|
|
985
|
-
.where({
|
|
986
|
-
[joinColumn.name]: id,
|
|
987
|
-
[orderColumnName]: { $gt: relToDelete[orderColumnName] },
|
|
988
|
-
})
|
|
989
|
-
.where(joinTable.on || {})
|
|
990
|
-
// manque le pivot ici
|
|
991
|
-
.execute();
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
}
|
|
995
|
-
|
|
996
|
-
// INVERSE ORDER UPDATE
|
|
997
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
998
|
-
const updateInverseOrderPromises = [];
|
|
999
|
-
for (const relToDelete of relsToDelete) {
|
|
1000
|
-
if (relToDelete[inverseOrderColumnName] !== null) {
|
|
1001
|
-
const updatePromise = this.createQueryBuilder(joinTable.name)
|
|
1002
|
-
.decrement(inverseOrderColumnName, 1)
|
|
1003
|
-
.where({
|
|
1004
|
-
[inverseJoinColumn.name]: relToDelete[inverseJoinColumn.name],
|
|
1005
|
-
[inverseOrderColumnName]: { $gt: relToDelete[inverseOrderColumnName] },
|
|
1006
|
-
})
|
|
1007
|
-
.where(joinTable.on || {})
|
|
1008
|
-
.execute();
|
|
1009
|
-
updateInverseOrderPromises.push(updatePromise);
|
|
1010
|
-
}
|
|
1011
|
-
}
|
|
1012
|
-
|
|
1013
|
-
await Promise.all(updateInverseOrderPromises);
|
|
1014
|
-
}
|
|
1015
|
-
}
|
|
893
|
+
relIdsToaddOrMove = toIds(cleanRelationData.set);
|
|
894
|
+
await deleteRelations({
|
|
895
|
+
id,
|
|
896
|
+
attribute,
|
|
897
|
+
db,
|
|
898
|
+
relIdsToDelete: 'all',
|
|
899
|
+
relIdsToNotDelete: relIdsToaddOrMove,
|
|
900
|
+
transaction: trx,
|
|
901
|
+
});
|
|
902
|
+
|
|
903
|
+
if (isEmpty(cleanRelationData.set)) {
|
|
904
|
+
continue;
|
|
1016
905
|
}
|
|
1017
906
|
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
.
|
|
1021
|
-
.
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
.where({
|
|
1031
|
-
[joinColumn.name]: id,
|
|
1032
|
-
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
1033
|
-
})
|
|
1034
|
-
.where(joinTable.on || {})
|
|
1035
|
-
.execute();
|
|
1036
|
-
const currentMovingRelsMap = groupBy(inverseJoinColumn.name, currentMovingRels);
|
|
1037
|
-
|
|
1038
|
-
let max = 0;
|
|
1039
|
-
for (const relToAdd of relsToAdd) {
|
|
1040
|
-
const currentRel = currentMovingRelsMap[relToAdd.id]?.[0];
|
|
1041
|
-
|
|
1042
|
-
if (currentRel && isAnyToMany(attribute)) {
|
|
1043
|
-
const update = { [orderColumnName]: max + 1 };
|
|
1044
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1045
|
-
.update(update)
|
|
1046
|
-
.where({
|
|
1047
|
-
[joinColumn.name]: id,
|
|
1048
|
-
[inverseJoinColumn.name]: relToAdd.id,
|
|
1049
|
-
})
|
|
1050
|
-
.where(joinTable.on || {})
|
|
1051
|
-
.execute();
|
|
1052
|
-
} else if (!currentRel) {
|
|
1053
|
-
const insert = {
|
|
1054
|
-
[joinColumn.name]: id,
|
|
1055
|
-
[inverseJoinColumn.name]: relToAdd.id,
|
|
1056
|
-
...(relToAdd.__pivot || {}),
|
|
1057
|
-
};
|
|
1058
|
-
|
|
1059
|
-
if (isAnyToMany(attribute)) {
|
|
1060
|
-
insert[orderColumnName] = max + 1;
|
|
1061
|
-
}
|
|
1062
|
-
// can be optimized in one query
|
|
1063
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
1064
|
-
const { max: reverseMax } = await this.createQueryBuilder(joinTable.name)
|
|
1065
|
-
.max(inverseOrderColumnName)
|
|
1066
|
-
.where({ [inverseJoinColumn.name]: id })
|
|
1067
|
-
.where(joinTable.on || {})
|
|
1068
|
-
.first()
|
|
1069
|
-
.execute();
|
|
1070
|
-
|
|
1071
|
-
insert[inverseOrderColumnName] = reverseMax + 1;
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
await this.createQueryBuilder(joinTable.name).insert(insert).execute();
|
|
1075
|
-
}
|
|
1076
|
-
max += 1;
|
|
907
|
+
const insert = cleanRelationData.set.map((relToAdd) => ({
|
|
908
|
+
[joinColumn.name]: id,
|
|
909
|
+
[inverseJoinColumn.name]: relToAdd.id,
|
|
910
|
+
...(joinTable.on || {}),
|
|
911
|
+
...(relToAdd.__pivot || {}),
|
|
912
|
+
}));
|
|
913
|
+
|
|
914
|
+
// add order value
|
|
915
|
+
if (hasOrderColumn(attribute)) {
|
|
916
|
+
insert.forEach((row, idx) => {
|
|
917
|
+
row[orderColumnName] = idx + 1;
|
|
918
|
+
});
|
|
1077
919
|
}
|
|
1078
|
-
}
|
|
1079
920
|
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
const currentRelsToDelete = await this.createQueryBuilder(joinTable.name)
|
|
1085
|
-
.select(select)
|
|
921
|
+
// add inv order value
|
|
922
|
+
if (hasInverseOrderColumn(attribute)) {
|
|
923
|
+
const existingRels = await this.createQueryBuilder(joinTable.name)
|
|
924
|
+
.select(inverseJoinColumn.name)
|
|
1086
925
|
.where({
|
|
1087
|
-
[
|
|
1088
|
-
[
|
|
926
|
+
[joinColumn.name]: id,
|
|
927
|
+
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
1089
928
|
})
|
|
1090
929
|
.where(joinTable.on || {})
|
|
930
|
+
.transacting(trx)
|
|
1091
931
|
.execute();
|
|
1092
932
|
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
933
|
+
const nonExistingRelsIds = difference(
|
|
934
|
+
relIdsToaddOrMove,
|
|
935
|
+
map(inverseJoinColumn.name, existingRels)
|
|
936
|
+
);
|
|
937
|
+
|
|
938
|
+
const maxResults = await db
|
|
939
|
+
.getConnection()
|
|
940
|
+
.select(inverseJoinColumn.name)
|
|
941
|
+
.max(inverseOrderColumnName, { as: 'max' })
|
|
942
|
+
.whereIn(inverseJoinColumn.name, nonExistingRelsIds)
|
|
943
|
+
.where(joinTable.on || {})
|
|
944
|
+
.groupBy(inverseJoinColumn.name)
|
|
945
|
+
.from(joinTable.name)
|
|
946
|
+
.transacting(trx);
|
|
947
|
+
|
|
948
|
+
const maxMap = maxResults.reduce(
|
|
949
|
+
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
|
950
|
+
{}
|
|
951
|
+
);
|
|
952
|
+
|
|
953
|
+
insert.forEach((row) => {
|
|
954
|
+
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
|
955
|
+
});
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
// insert rows
|
|
959
|
+
const query = this.createQueryBuilder(joinTable.name)
|
|
960
|
+
.insert(insert)
|
|
961
|
+
.onConflict(joinTable.pivotColumns)
|
|
962
|
+
.transacting(trx);
|
|
963
|
+
|
|
964
|
+
if (hasOrderColumn(attribute)) {
|
|
965
|
+
query.merge([orderColumnName]);
|
|
966
|
+
} else {
|
|
967
|
+
query.ignore();
|
|
1107
968
|
}
|
|
1108
969
|
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
970
|
+
await query.execute();
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
// Delete the previous relations for oneToAny relations
|
|
974
|
+
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
975
|
+
await deletePreviousOneToAnyRelations({
|
|
976
|
+
id,
|
|
977
|
+
attribute,
|
|
978
|
+
relIdsToadd: relIdsToaddOrMove,
|
|
979
|
+
db,
|
|
980
|
+
transaction: trx,
|
|
981
|
+
});
|
|
1118
982
|
}
|
|
1119
983
|
|
|
1120
984
|
// Delete the previous relations for anyToOne relations
|
|
1121
985
|
if (isBidirectional(attribute) && isAnyToOne(attribute)) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
})
|
|
1130
|
-
.where(joinTable.on || {})
|
|
1131
|
-
.execute();
|
|
1132
|
-
|
|
1133
|
-
for (const relToDelete of currentRelsToDelete) {
|
|
1134
|
-
if (relToDelete[inverseOrderColumnName] !== null) {
|
|
1135
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1136
|
-
.decrement(inverseOrderColumnName, 1)
|
|
1137
|
-
.where({
|
|
1138
|
-
[inverseJoinColumn.name]: relToDelete[inverseJoinColumn.name],
|
|
1139
|
-
[inverseOrderColumnName]: { $gt: relToDelete[inverseOrderColumnName] },
|
|
1140
|
-
})
|
|
1141
|
-
.where(joinTable.on || {})
|
|
1142
|
-
.execute();
|
|
1143
|
-
}
|
|
1144
|
-
}
|
|
1145
|
-
}
|
|
1146
|
-
|
|
1147
|
-
// delete previous oneToAny relations
|
|
1148
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1149
|
-
.delete()
|
|
1150
|
-
.where({
|
|
1151
|
-
[joinColumn.name]: id,
|
|
1152
|
-
[inverseJoinColumn.name]: { $notIn: relIdsToaddOrMove },
|
|
1153
|
-
})
|
|
1154
|
-
.where(joinTable.on || {})
|
|
1155
|
-
.execute();
|
|
986
|
+
await deletePreviousAnyToOneRelations({
|
|
987
|
+
id,
|
|
988
|
+
attribute,
|
|
989
|
+
relIdToadd: relIdsToaddOrMove[0],
|
|
990
|
+
db,
|
|
991
|
+
transaction: trx,
|
|
992
|
+
});
|
|
1156
993
|
}
|
|
1157
994
|
}
|
|
1158
995
|
}
|
|
@@ -1168,8 +1005,7 @@ const createEntityManager = (db) => {
|
|
|
1168
1005
|
* @param {Metadata} metadata - model metadta
|
|
1169
1006
|
* @param {ID} id - entity ID
|
|
1170
1007
|
*/
|
|
1171
|
-
|
|
1172
|
-
async deleteRelations(uid, id) {
|
|
1008
|
+
async deleteRelations(uid, id, { transaction: trx }) {
|
|
1173
1009
|
const { attributes } = db.metadata.get(uid);
|
|
1174
1010
|
|
|
1175
1011
|
for (const attributeName of Object.keys(attributes)) {
|
|
@@ -1198,6 +1034,7 @@ const createEntityManager = (db) => {
|
|
|
1198
1034
|
await this.createQueryBuilder(target)
|
|
1199
1035
|
.update({ [idColumn.name]: null, [typeColumn.name]: null })
|
|
1200
1036
|
.where({ [idColumn.name]: id, [typeColumn.name]: uid })
|
|
1037
|
+
.transacting(trx)
|
|
1201
1038
|
.execute();
|
|
1202
1039
|
} else if (targetAttribute.relation === 'morphToMany') {
|
|
1203
1040
|
const { joinTable } = targetAttribute;
|
|
@@ -1213,6 +1050,7 @@ const createEntityManager = (db) => {
|
|
|
1213
1050
|
...(joinTable.on || {}),
|
|
1214
1051
|
field: attributeName,
|
|
1215
1052
|
})
|
|
1053
|
+
.transacting(trx)
|
|
1216
1054
|
.execute();
|
|
1217
1055
|
}
|
|
1218
1056
|
|
|
@@ -1241,6 +1079,7 @@ const createEntityManager = (db) => {
|
|
|
1241
1079
|
[joinColumn.name]: id,
|
|
1242
1080
|
...(joinTable.on || {}),
|
|
1243
1081
|
})
|
|
1082
|
+
.transacting(trx)
|
|
1244
1083
|
.execute();
|
|
1245
1084
|
|
|
1246
1085
|
continue;
|
|
@@ -1265,56 +1104,12 @@ const createEntityManager = (db) => {
|
|
|
1265
1104
|
await this.createQueryBuilder(target)
|
|
1266
1105
|
.where({ [attribute.joinColumn.referencedColumn]: id })
|
|
1267
1106
|
.update({ [attribute.joinColumn.referencedColumn]: null })
|
|
1107
|
+
.transacting(trx)
|
|
1268
1108
|
.execute();
|
|
1269
1109
|
}
|
|
1270
1110
|
|
|
1271
1111
|
if (attribute.joinTable) {
|
|
1272
|
-
|
|
1273
|
-
const { joinColumn, inverseJoinColumn, inverseOrderColumnName } = joinTable;
|
|
1274
|
-
|
|
1275
|
-
// INVERSE ORDER UPDATE
|
|
1276
|
-
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
1277
|
-
let lastId = 0;
|
|
1278
|
-
let done = false;
|
|
1279
|
-
const batchSize = 100;
|
|
1280
|
-
while (!done) {
|
|
1281
|
-
const relsToDelete = await this.createQueryBuilder(joinTable.name)
|
|
1282
|
-
.select(inverseJoinColumn.name, inverseOrderColumnName)
|
|
1283
|
-
.where({
|
|
1284
|
-
[joinColumn.name]: id,
|
|
1285
|
-
id: { $gt: lastId },
|
|
1286
|
-
})
|
|
1287
|
-
.where(joinTable.on || {})
|
|
1288
|
-
.orderBy('id')
|
|
1289
|
-
.limit(batchSize)
|
|
1290
|
-
.execute();
|
|
1291
|
-
done = relsToDelete.length < batchSize;
|
|
1292
|
-
lastId = relsToDelete[relsToDelete.length - 1]?.id;
|
|
1293
|
-
|
|
1294
|
-
const updateInverseOrderPromises = [];
|
|
1295
|
-
for (const relToDelete of relsToDelete) {
|
|
1296
|
-
if (relToDelete[inverseOrderColumnName] !== null) {
|
|
1297
|
-
const updatePromise = this.createQueryBuilder(joinTable.name)
|
|
1298
|
-
.decrement(inverseOrderColumnName, 1)
|
|
1299
|
-
.where({
|
|
1300
|
-
[inverseJoinColumn.name]: relToDelete[inverseJoinColumn.name],
|
|
1301
|
-
[inverseOrderColumnName]: { $gt: relToDelete[inverseOrderColumnName] },
|
|
1302
|
-
})
|
|
1303
|
-
.where(joinTable.on || {})
|
|
1304
|
-
.execute();
|
|
1305
|
-
updateInverseOrderPromises.push(updatePromise);
|
|
1306
|
-
}
|
|
1307
|
-
}
|
|
1308
|
-
|
|
1309
|
-
await Promise.all(updateInverseOrderPromises);
|
|
1310
|
-
}
|
|
1311
|
-
}
|
|
1312
|
-
|
|
1313
|
-
await this.createQueryBuilder(joinTable.name)
|
|
1314
|
-
.delete()
|
|
1315
|
-
.where({ [joinColumn.name]: id })
|
|
1316
|
-
.where(joinTable.on || {})
|
|
1317
|
-
.execute();
|
|
1112
|
+
await deleteRelations({ id, attribute, db, relIdsToDelete: 'all', transaction: trx });
|
|
1318
1113
|
}
|
|
1319
1114
|
}
|
|
1320
1115
|
},
|