mirror of
https://github.com/strapi/strapi.git
synced 2025-11-16 01:57:56 +00:00
use onConflict in updateRelations
This commit is contained in:
parent
3b0d6b6e7f
commit
a9819dc375
@ -16,6 +16,8 @@ const {
|
|||||||
isEqual,
|
isEqual,
|
||||||
differenceWith,
|
differenceWith,
|
||||||
isNumber,
|
isNumber,
|
||||||
|
map,
|
||||||
|
difference,
|
||||||
} = require('lodash/fp');
|
} = require('lodash/fp');
|
||||||
const types = require('../types');
|
const types = require('../types');
|
||||||
const { createField } = require('../fields');
|
const { createField } = require('../fields');
|
||||||
@ -723,7 +725,7 @@ const createEntityManager = (db) => {
|
|||||||
|
|
||||||
// only delete relations
|
// only delete relations
|
||||||
if (isNull(cleanRelationData.set)) {
|
if (isNull(cleanRelationData.set)) {
|
||||||
await deleteRelations({ id, attribute, joinTable, db }, { relsToDelete: 'all' });
|
await deleteRelations({ id, attribute, joinTable, db }, { relIdsToDelete: 'all' });
|
||||||
} else {
|
} else {
|
||||||
const isPartialUpdate = !has('set', cleanRelationData);
|
const isPartialUpdate = !has('set', cleanRelationData);
|
||||||
let relIdsToaddOrMove;
|
let relIdsToaddOrMove;
|
||||||
@ -740,10 +742,7 @@ const createEntityManager = (db) => {
|
|||||||
differenceWith(isEqual, cleanRelationData.disconnect, cleanRelationData.connect)
|
differenceWith(isEqual, cleanRelationData.disconnect, cleanRelationData.connect)
|
||||||
);
|
);
|
||||||
|
|
||||||
await deleteRelations(
|
await deleteRelations({ id, attribute, joinTable, db }, { relIdsToDelete });
|
||||||
{ id, attribute, joinTable, db },
|
|
||||||
{ relsToDelete: relIdsToDelete }
|
|
||||||
);
|
|
||||||
|
|
||||||
// add/move
|
// add/move
|
||||||
let max;
|
let max;
|
||||||
@ -838,63 +837,75 @@ const createEntityManager = (db) => {
|
|||||||
relIdsToaddOrMove = toIds(cleanRelationData.set);
|
relIdsToaddOrMove = toIds(cleanRelationData.set);
|
||||||
await deleteRelations(
|
await deleteRelations(
|
||||||
{ id, attribute, joinTable, db },
|
{ id, attribute, joinTable, db },
|
||||||
{ relsToDelete: 'all', relsToNotDelete: relIdsToaddOrMove }
|
{ relIdsToDelete: 'all', relIdsToNotDelete: relIdsToaddOrMove }
|
||||||
);
|
);
|
||||||
|
|
||||||
const currentMovingRels = await this.createQueryBuilder(joinTable.name)
|
if (isEmpty(cleanRelationData.set)) {
|
||||||
.select(select)
|
continue;
|
||||||
.where({
|
|
||||||
[joinColumn.name]: id,
|
|
||||||
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
|
||||||
})
|
|
||||||
.where(joinTable.on || {})
|
|
||||||
.execute();
|
|
||||||
const currentMovingRelsMap = currentMovingRels.reduce(
|
|
||||||
(acc, rel) => Object.assign(acc, { [rel[inverseJoinColumn.name]]: rel }),
|
|
||||||
{}
|
|
||||||
);
|
|
||||||
|
|
||||||
let index = 0;
|
|
||||||
for (const relToAdd of cleanRelationData.set) {
|
|
||||||
const currentRel = currentMovingRelsMap[relToAdd.id];
|
|
||||||
|
|
||||||
if (currentRel && isAnyToMany(attribute)) {
|
|
||||||
const update = { [orderColumnName]: index + 1 };
|
|
||||||
await this.createQueryBuilder(joinTable.name)
|
|
||||||
.update(update)
|
|
||||||
.where({
|
|
||||||
[joinColumn.name]: id,
|
|
||||||
[inverseJoinColumn.name]: relToAdd.id,
|
|
||||||
})
|
|
||||||
.where(joinTable.on || {})
|
|
||||||
.execute();
|
|
||||||
} else if (!currentRel) {
|
|
||||||
const insert = {
|
|
||||||
[joinColumn.name]: id,
|
|
||||||
[inverseJoinColumn.name]: relToAdd.id,
|
|
||||||
...(joinTable.on || {}),
|
|
||||||
...(relToAdd.__pivot || {}),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (isAnyToMany(attribute)) {
|
|
||||||
insert[orderColumnName] = index + 1;
|
|
||||||
}
|
|
||||||
// can be optimized in one query
|
|
||||||
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
|
||||||
const { max: reverseMax } = await this.createQueryBuilder(joinTable.name)
|
|
||||||
.max(inverseOrderColumnName)
|
|
||||||
.where({ [inverseJoinColumn.name]: id })
|
|
||||||
.where(joinTable.on || {})
|
|
||||||
.first()
|
|
||||||
.execute();
|
|
||||||
|
|
||||||
insert[inverseOrderColumnName] = reverseMax + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
await this.createQueryBuilder(joinTable.name).insert(insert).execute();
|
|
||||||
}
|
|
||||||
index += 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const insert = cleanRelationData.set.map((relToAdd) => ({
|
||||||
|
[joinColumn.name]: id,
|
||||||
|
[inverseJoinColumn.name]: relToAdd.id,
|
||||||
|
...(joinTable.on || {}),
|
||||||
|
...(relToAdd.__pivot || {}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
// add order value
|
||||||
|
if (isAnyToMany(attribute)) {
|
||||||
|
insert.forEach((row, idx) => {
|
||||||
|
row[orderColumnName] = idx + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// add inv order value
|
||||||
|
if (isBidirectional(attribute) && isManyToAny(attribute)) {
|
||||||
|
const existingRels = await this.createQueryBuilder(joinTable.name)
|
||||||
|
.select('id')
|
||||||
|
.where({
|
||||||
|
[joinColumn.name]: id,
|
||||||
|
[inverseJoinColumn.name]: { $in: relIdsToaddOrMove },
|
||||||
|
})
|
||||||
|
.where(joinTable.on || {})
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
const nonExistingRelsIds = difference(relIdsToaddOrMove, map('id', existingRels));
|
||||||
|
|
||||||
|
const maxResults = await db
|
||||||
|
.getConnection()
|
||||||
|
.select(inverseJoinColumn.name)
|
||||||
|
.max(inverseOrderColumnName, { as: 'max' })
|
||||||
|
.whereIn(inverseJoinColumn.name, nonExistingRelsIds)
|
||||||
|
.where(joinTable.on || {})
|
||||||
|
.groupBy(inverseJoinColumn.name)
|
||||||
|
.from(joinTable.name);
|
||||||
|
|
||||||
|
const maxMap = maxResults.reduce(
|
||||||
|
(acc, res) => Object.assign(acc, { [res[inverseJoinColumn.name]]: res.max }),
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
|
insert.forEach((row) => {
|
||||||
|
row[inverseOrderColumnName] = (maxMap[row[inverseJoinColumn.name]] || 0) + 1;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// insert rows
|
||||||
|
const query = this.createQueryBuilder(joinTable.name)
|
||||||
|
.insert(insert)
|
||||||
|
.onConflict([
|
||||||
|
joinColumn.name,
|
||||||
|
inverseJoinColumn.name,
|
||||||
|
...Object.keys(joinTable.on || {}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (isAnyToMany(attribute)) {
|
||||||
|
query.merge([orderColumnName]);
|
||||||
|
} else {
|
||||||
|
query.ignore();
|
||||||
|
}
|
||||||
|
|
||||||
|
await query.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete the previous relations for oneToAny relations
|
// Delete the previous relations for oneToAny relations
|
||||||
@ -1031,7 +1042,7 @@ const createEntityManager = (db) => {
|
|||||||
if (attribute.joinTable) {
|
if (attribute.joinTable) {
|
||||||
const { joinTable } = attribute;
|
const { joinTable } = attribute;
|
||||||
|
|
||||||
await deleteRelations({ id, attribute, joinTable, db }, { relsToDelete: 'all' });
|
await deleteRelations({ id, attribute, joinTable, db }, { relIdsToDelete: 'all' });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@ -112,11 +112,11 @@ const deletePreviousAnyToOneRelations = async ({ id, attribute, joinTable, relId
|
|||||||
// INVERSE ORDER UPDATE
|
// INVERSE ORDER UPDATE
|
||||||
const deleteRelations = async (
|
const deleteRelations = async (
|
||||||
{ id, attribute, joinTable, db },
|
{ id, attribute, joinTable, db },
|
||||||
{ relsToNotDelete = [], relsToDelete = [] }
|
{ relIdsToNotDelete = [], relIdsToDelete = [] }
|
||||||
) => {
|
) => {
|
||||||
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable;
|
||||||
const select = getSelect(joinTable, attribute);
|
const select = getSelect(joinTable, attribute);
|
||||||
const all = relsToDelete === 'all';
|
const all = relIdsToDelete === 'all';
|
||||||
|
|
||||||
if (isAnyToMany(attribute) || (isBidirectional(attribute) && isManyToAny(attribute))) {
|
if (isAnyToMany(attribute) || (isBidirectional(attribute) && isManyToAny(attribute))) {
|
||||||
let lastId = 0;
|
let lastId = 0;
|
||||||
@ -128,8 +128,8 @@ const deleteRelations = async (
|
|||||||
.where({
|
.where({
|
||||||
[joinColumn.name]: id,
|
[joinColumn.name]: id,
|
||||||
id: { $gt: lastId },
|
id: { $gt: lastId },
|
||||||
[inverseJoinColumn.name]: { $notIn: relsToNotDelete },
|
[inverseJoinColumn.name]: { $notIn: relIdsToNotDelete },
|
||||||
...(all ? {} : { [inverseJoinColumn.name]: { $in: relsToDelete } }),
|
...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }),
|
||||||
})
|
})
|
||||||
.where(joinTable.on || {})
|
.where(joinTable.on || {})
|
||||||
.orderBy('id')
|
.orderBy('id')
|
||||||
@ -183,8 +183,8 @@ const deleteRelations = async (
|
|||||||
.delete()
|
.delete()
|
||||||
.where({
|
.where({
|
||||||
[joinColumn.name]: id,
|
[joinColumn.name]: id,
|
||||||
[inverseJoinColumn.name]: { $notIn: relsToNotDelete },
|
[inverseJoinColumn.name]: { $notIn: relIdsToNotDelete },
|
||||||
...(all ? {} : { [inverseJoinColumn.name]: { $in: relsToDelete } }),
|
...(all ? {} : { [inverseJoinColumn.name]: { $in: relIdsToDelete } }),
|
||||||
})
|
})
|
||||||
.where(joinTable.on || {})
|
.where(joinTable.on || {})
|
||||||
.execute();
|
.execute();
|
||||||
|
|||||||
@ -142,6 +142,11 @@ const createCompoLinkModelMeta = (baseModelMeta) => {
|
|||||||
name: `${baseModelMeta.tableName}_entity_fk`,
|
name: `${baseModelMeta.tableName}_entity_fk`,
|
||||||
columns: ['entity_id'],
|
columns: ['entity_id'],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: `${baseModelMeta.tableName}_unique`,
|
||||||
|
columns: ['entity_id', 'component_id', 'field'],
|
||||||
|
type: 'unique',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
foreignKeys: [
|
foreignKeys: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -444,6 +444,11 @@ const createJoinTable = (metadata, { attributeName, attribute, meta }) => {
|
|||||||
name: `${joinTableName}_inv_fk`,
|
name: `${joinTableName}_inv_fk`,
|
||||||
columns: [inverseJoinColumnName],
|
columns: [inverseJoinColumnName],
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: `${joinTableName}_unique`,
|
||||||
|
columns: [joinColumnName, inverseJoinColumnName],
|
||||||
|
type: 'unique',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
foreignKeys: [
|
foreignKeys: [
|
||||||
{
|
{
|
||||||
|
|||||||
@ -23,6 +23,9 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
|
|||||||
offset: null,
|
offset: null,
|
||||||
transaction: null,
|
transaction: null,
|
||||||
forUpdate: false,
|
forUpdate: false,
|
||||||
|
onConflict: null,
|
||||||
|
merge: null,
|
||||||
|
ignore: false,
|
||||||
orderBy: [],
|
orderBy: [],
|
||||||
groupBy: [],
|
groupBy: [],
|
||||||
increments: [],
|
increments: [],
|
||||||
@ -69,6 +72,24 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
|
|||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
onConflict(args) {
|
||||||
|
state.onConflict = args;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
merge(args) {
|
||||||
|
state.merge = args;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
|
ignore() {
|
||||||
|
state.ignore = true;
|
||||||
|
|
||||||
|
return this;
|
||||||
|
},
|
||||||
|
|
||||||
delete() {
|
delete() {
|
||||||
state.type = 'delete';
|
state.type = 'delete';
|
||||||
|
|
||||||
@ -400,6 +421,14 @@ const createQueryBuilder = (uid, db, initialState = {}) => {
|
|||||||
state.decrements.forEach((decr) => qb.decrement(decr.column, decr.amount));
|
state.decrements.forEach((decr) => qb.decrement(decr.column, decr.amount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (state.onConflict) {
|
||||||
|
if (state.merge) {
|
||||||
|
qb.onConflict(state.onConflict).merge(state.merge);
|
||||||
|
} else if (state.ignore) {
|
||||||
|
qb.onConflict(state.onConflict).ignore();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (state.limit) {
|
if (state.limit) {
|
||||||
qb.limit(state.limit);
|
qb.limit(state.limit);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user