2022-09-16 18:38:46 +02:00
|
|
|
'use strict';
|
|
|
|
|
2022-09-26 16:22:22 +02:00
|
|
|
const { map, isEmpty } = require('lodash/fp');
|
2022-09-16 18:38:46 +02:00
|
|
|
const {
|
|
|
|
isBidirectional,
|
|
|
|
isOneToAny,
|
|
|
|
isManyToAny,
|
|
|
|
isAnyToOne,
|
|
|
|
isAnyToMany,
|
|
|
|
} = require('../metadata/relations');
|
|
|
|
const { createQueryBuilder } = require('../query');
|
|
|
|
|
|
|
|
const deletePreviousOneToAnyRelations = async ({ id, attribute, joinTable, relIdsToadd, db }) => {
|
2022-09-26 16:22:22 +02:00
|
|
|
const { joinColumn, inverseJoinColumn } = joinTable;
|
2022-09-16 18:38:46 +02:00
|
|
|
|
|
|
|
// need to delete the previous relations for oneToAny relations
|
|
|
|
if (isBidirectional(attribute) && isOneToAny(attribute)) {
|
|
|
|
// delete previous oneToAny relations
|
|
|
|
await createQueryBuilder(joinTable.name, db)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[inverseJoinColumn.name]: relIdsToadd,
|
|
|
|
[joinColumn.name]: { $ne: id },
|
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
2022-09-26 16:22:22 +02:00
|
|
|
|
|
|
|
await cleanOrderColumns({ joinTable, attribute, db, inverseRelIds: relIdsToadd });
|
2022-09-16 18:38:46 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-09-26 16:22:22 +02:00
|
|
|
const deletePreviousAnyToOneRelations = async ({ id, attribute, joinTable, relIdToadd, db }) => {
|
|
|
|
const { joinColumn, inverseJoinColumn } = joinTable;
|
2022-09-16 18:38:46 +02:00
|
|
|
|
|
|
|
// Delete the previous relations for anyToOne relations
|
|
|
|
if (isBidirectional(attribute) && isAnyToOne(attribute)) {
|
|
|
|
// update orders for previous anyToOne relations that will be deleted if it has order (manyToOne)
|
|
|
|
if (isManyToAny(attribute)) {
|
2022-09-26 16:22:22 +02:00
|
|
|
// if the database integrity was not broken relsToDelete is supposed to be of length 1
|
|
|
|
const relsToDelete = await createQueryBuilder(joinTable.name, db)
|
|
|
|
.select(inverseJoinColumn.name)
|
2022-09-16 18:38:46 +02:00
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
2022-09-26 16:22:22 +02:00
|
|
|
[inverseJoinColumn.name]: { $ne: relIdToadd },
|
2022-09-16 18:38:46 +02:00
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
|
2022-09-26 16:22:22 +02:00
|
|
|
const relIdsToDelete = map(inverseJoinColumn.name, relsToDelete);
|
2022-09-16 18:38:46 +02:00
|
|
|
|
2022-09-26 16:22:22 +02:00
|
|
|
// delete previous anyToOne relations
|
|
|
|
await createQueryBuilder(joinTable.name, db)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
[inverseJoinColumn.name]: { $in: relIdsToDelete },
|
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
await cleanOrderColumns({ joinTable, attribute, db, inverseRelIds: relIdsToDelete });
|
|
|
|
} else {
|
|
|
|
// delete previous anyToOne relations
|
|
|
|
await createQueryBuilder(joinTable.name, db)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
[inverseJoinColumn.name]: { $ne: relIdToadd },
|
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
}
|
2022-09-16 18:38:46 +02:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
// INVERSE ORDER UPDATE
|
2022-09-20 11:18:32 +02:00
|
|
|
const deleteRelations = async (
|
|
|
|
{ id, attribute, joinTable, db },
|
2022-09-20 15:53:17 +02:00
|
|
|
{ relIdsToNotDelete = [], relIdsToDelete = [] }
|
2022-09-20 11:18:32 +02:00
|
|
|
) => {
|
2022-09-26 16:22:22 +02:00
|
|
|
const { joinColumn, inverseJoinColumn } = joinTable;
|
2022-09-20 15:53:17 +02:00
|
|
|
const all = relIdsToDelete === 'all';
|
2022-09-16 18:38:46 +02:00
|
|
|
|
|
|
|
if (isAnyToMany(attribute) || (isBidirectional(attribute) && isManyToAny(attribute))) {
|
|
|
|
let lastId = 0;
|
|
|
|
let done = false;
|
|
|
|
const batchSize = 100;
|
|
|
|
while (!done) {
|
2022-09-26 16:22:22 +02:00
|
|
|
const batchToDelete = await createQueryBuilder(joinTable.name, db)
|
|
|
|
.select(inverseJoinColumn.name)
|
2022-09-16 18:38:46 +02:00
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
id: { $gt: lastId },
|
2022-09-20 15:53:17 +02:00
|
|
|
[inverseJoinColumn.name]: { $notIn: relIdsToNotDelete },
|
|
|
|
...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }),
|
2022-09-16 18:38:46 +02:00
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.orderBy('id')
|
|
|
|
.limit(batchSize)
|
|
|
|
.execute();
|
2022-09-26 16:22:22 +02:00
|
|
|
done = batchToDelete.length < batchSize;
|
|
|
|
lastId = batchToDelete[batchToDelete.length - 1]?.id;
|
|
|
|
|
|
|
|
const batchIds = map(inverseJoinColumn.name, batchToDelete);
|
|
|
|
|
|
|
|
await createQueryBuilder(joinTable.name, db)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
[inverseJoinColumn.name]: { $in: batchIds },
|
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
|
|
|
|
await cleanOrderColumns({ joinTable, attribute, db, id, inverseRelIds: batchIds });
|
2022-09-16 18:38:46 +02:00
|
|
|
}
|
2022-09-26 16:22:22 +02:00
|
|
|
} else {
|
|
|
|
await createQueryBuilder(joinTable.name, db)
|
|
|
|
.delete()
|
|
|
|
.where({
|
|
|
|
[joinColumn.name]: id,
|
|
|
|
[inverseJoinColumn.name]: { $notIn: relIdsToNotDelete },
|
|
|
|
...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }),
|
|
|
|
})
|
|
|
|
.where(joinTable.on || {})
|
|
|
|
.execute();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Clean the order columns by ensuring the order value are continuous (ex: 1, 2, 3 and not 1, 5, 10)
|
|
|
|
* @param {Object} params
|
|
|
|
* @param {string} params.joinTable - joinTable of the relation where the clean will be done
|
|
|
|
* @param {string} params.attribute - attribute on which the clean will be done
|
|
|
|
* @param {string} params.db - Database instance
|
|
|
|
* @param {string} params.id - Entity ID for which the clean will be done
|
|
|
|
* @param {string} params.inverseRelIds - Entity ids of the inverse side for which the clean will be done
|
|
|
|
*/
|
|
|
|
const cleanOrderColumns = async ({ joinTable, attribute, db, id, inverseRelIds }) => {
|
|
|
|
if (
|
|
|
|
!(isAnyToMany(attribute) && id) &&
|
|
|
|
!(isBidirectional(attribute) && isManyToAny(attribute) && !isEmpty(inverseRelIds))
|
|
|
|
) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
|
|
|
const knex = db.getConnection();
|
|
|
|
const update = {};
|
|
|
|
const subQuery = knex(joinTable.name).select('id');
|
|
|
|
|
|
|
|
if (isAnyToMany(attribute) && id) {
|
|
|
|
update[orderColumnName] = knex.raw('t.src_order');
|
|
|
|
const on = [joinColumn.name, ...Object.keys(joinTable.on)];
|
|
|
|
subQuery
|
|
|
|
.select(
|
|
|
|
knex.raw(`ROW_NUMBER() OVER (PARTITION BY ${on.join(', ')} ORDER BY ??) AS src_order`, [
|
|
|
|
...on,
|
|
|
|
orderColumnName,
|
|
|
|
])
|
|
|
|
)
|
|
|
|
.where(joinColumn.name, id);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isBidirectional(attribute) && isManyToAny(attribute) && !isEmpty(inverseRelIds)) {
|
|
|
|
update[inverseOrderColumnName] = knex.raw('t.inv_order');
|
|
|
|
const on = [inverseJoinColumn.name, ...Object.keys(joinTable.on)];
|
|
|
|
subQuery
|
|
|
|
.select(
|
|
|
|
knex.raw(`ROW_NUMBER() OVER (PARTITION BY ${on.join(', ')} ORDER BY ??) AS inv_order`, [
|
|
|
|
...on,
|
|
|
|
inverseOrderColumnName,
|
|
|
|
])
|
|
|
|
)
|
|
|
|
.orWhereIn(inverseJoinColumn.name, inverseRelIds);
|
2022-09-16 18:38:46 +02:00
|
|
|
}
|
|
|
|
|
2022-09-26 16:22:22 +02:00
|
|
|
await knex(joinTable.name)
|
|
|
|
.update(update)
|
|
|
|
.from(subQuery)
|
|
|
|
.where('t.id', knex.raw('??.id', joinTable.name));
|
|
|
|
|
|
|
|
/*
|
|
|
|
`UPDATE :joinTable:
|
|
|
|
SET :orderColumn: = t.order, :inverseOrderColumn: = t.inv_order
|
|
|
|
FROM (
|
|
|
|
SELECT
|
|
|
|
id,
|
|
|
|
ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS order,
|
|
|
|
ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order
|
|
|
|
FROM :joinTable:
|
|
|
|
WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds)
|
|
|
|
) AS t
|
|
|
|
WHERE t.id = :joinTable:.id`,
|
|
|
|
*/
|
2022-09-16 18:38:46 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
module.exports = {
|
|
|
|
deletePreviousOneToAnyRelations,
|
|
|
|
deletePreviousAnyToOneRelations,
|
2022-09-20 11:18:32 +02:00
|
|
|
deleteRelations,
|
2022-09-26 16:22:22 +02:00
|
|
|
cleanOrderColumns,
|
2022-09-16 18:38:46 +02:00
|
|
|
};
|