From f6246c145de44f68eca7e67b5400ed4d19006fc6 Mon Sep 17 00:00:00 2001 From: Marc-Roig Date: Tue, 11 Jul 2023 10:17:06 +0200 Subject: [PATCH] fix: relations are not returned in the correct order When removing or adding a relation in an entity, other entities from the same Content Type were seeing that same relation reordered. Cause: We were updating the order of the other entities when we should only be updating the inverse order. --- .../lib/entity-manager/regular-relations.js | 75 +++++++++++++------ 1 file changed, 52 insertions(+), 23 deletions(-) diff --git a/packages/core/database/lib/entity-manager/regular-relations.js b/packages/core/database/lib/entity-manager/regular-relations.js index f5564253ef..af4b83ad1c 100644 --- a/packages/core/database/lib/entity-manager/regular-relations.js +++ b/packages/core/database/lib/entity-manager/regular-relations.js @@ -206,29 +206,40 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction const { joinTable } = attribute; const { joinColumn, inverseJoinColumn, orderColumnName, inverseOrderColumnName } = joinTable; + + // Build the update query based on the relation type + // See query below for more details const update = []; const updateBinding = []; - const select = ['??']; - const selectBinding = ['id']; - const where = []; - const whereBinding = []; + const selectEntityRelations = db.connection(joinTable.name).select('id'); + const selectInverseEntityRelations = db.connection(joinTable.name).select('id'); if (hasOrderColumn(attribute) && id) { + // Update order column of join table update.push('?? = b.src_order'); updateBinding.push(orderColumnName); - select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS src_order'); - selectBinding.push(joinColumn.name, orderColumnName); - where.push('?? = ?'); - whereBinding.push(joinColumn.name, id); + + // Calculate order column of entity relations + selectEntityRelations + .rowNumber('src_order', joinColumn.name, orderColumnName) + .where(joinColumn.name, id); + + // Do not update the order column of entities that were also using the same relations + selectInverseEntityRelations.select({ src_order: orderColumnName }); } if (hasInverseOrderColumn(attribute) && !isEmpty(inverseRelIds)) { + // Update inverse order column of join table update.push('?? = b.inv_order'); updateBinding.push(inverseOrderColumnName); - select.push('ROW_NUMBER() OVER (PARTITION BY ?? ORDER BY ??) AS inv_order'); - selectBinding.push(inverseJoinColumn.name, inverseOrderColumnName); - where.push(`?? IN (${inverseRelIds.map(() => '?').join(', ')})`); - whereBinding.push(inverseJoinColumn.name, ...inverseRelIds); + + // Calculate inv order column of inverse side for the entity relations + selectEntityRelations.rowNumber('inv_order', inverseJoinColumn.name, inverseOrderColumnName); + + // Calculate inv order column of entities that were also using the same relations + selectInverseEntityRelations + .rowNumber('inv_order', inverseJoinColumn.name, inverseOrderColumnName) + .where(inverseJoinColumn.name, 'in', inverseRelIds); } switch (strapi.db.dialect.client) { @@ -237,16 +248,20 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction await db .getConnection() .raw( - `UPDATE - ?? as a, + `UPDATE ?? as a, ( - SELECT ${select.join(', ')} - FROM ?? - WHERE ${where.join(' OR ')} + ${selectEntityRelations.toSQL().sql} + UNION + ${selectInverseEntityRelations.toSQL().sql} ) AS b SET ${update.join(', ')} WHERE b.id = a.id`, - [joinTable.name, ...selectBinding, joinTable.name, ...whereBinding, ...updateBinding] + [ + joinTable.name, + ...selectEntityRelations.toSQL().bindings, + ...selectInverseEntityRelations.toSQL().bindings, + ...updateBinding, + ] ) .transacting(trx); break; @@ -254,12 +269,21 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction UPDATE :joinTable: as a, ( + -- Update the updated entity order columns SELECT id, ROW_NUMBER() OVER ( PARTITION BY :joinColumn: ORDER BY :orderColumn:) AS src_order, ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order FROM :joinTable: - WHERE :joinColumn: = :id OR :inverseJoinColumn: IN (:inverseRelIds) + WHERE :joinColumn: = :id + UNION + -- Update the inverse side order columns of entities that relate to the same inverse side + SELECT + id, + :orderColumn: AS src_order, + ROW_NUMBER() OVER ( PARTITION BY :inverseJoinColumn: ORDER BY :inverseOrderColumn:) AS inv_order + FROM :joinTable: + WHERE :inverseJoinColumn: IN (:inverseRelIds) ) AS b SET :orderColumn: = b.src_order, :inverseOrderColumn: = b.inv_order WHERE b.id = a.id; @@ -274,12 +298,17 @@ const cleanOrderColumns = async ({ id, attribute, db, inverseRelIds, transaction `UPDATE ?? as a SET ${update.join(', ')} FROM ( - SELECT ${select.join(', ')} - FROM ?? - WHERE ${where.join(' OR ')} + ${selectEntityRelations.toSQL().sql} + UNION + ${selectInverseEntityRelations.toSQL().sql} ) AS b WHERE b.id = a.id`, - [joinTableName, ...updateBinding, ...selectBinding, joinTableName, ...whereBinding] + [ + joinTableName, + ...updateBinding, + ...selectEntityRelations.toSQL().bindings, + ...selectInverseEntityRelations.toSQL().bindings, + ] ) .transacting(trx);