From 2589e6be07c2208535480dee70177477238ae03a Mon Sep 17 00:00:00 2001 From: zkao Date: Fri, 19 May 2023 13:25:29 -0600 Subject: [PATCH] updateSortingOrder supports re-sorting in place (#293) --- src/model/MutableAreaDataSource.ts | 26 +++++++++++++++++++++----- src/model/__tests__/updateAreas.ts | 17 +++++++++++++++-- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/model/MutableAreaDataSource.ts b/src/model/MutableAreaDataSource.ts index f7eab147..765f9e7d 100644 --- a/src/model/MutableAreaDataSource.ts +++ b/src/model/MutableAreaDataSource.ts @@ -368,9 +368,25 @@ export default class MutableAreaDataSource extends AreaDataSource { const doUpdate = async (session: ClientSession, user: MUUID, input: UpdateSortingOrderType[]): Promise => { const opType = OperationType.orderAreas const change = await changelogDataSource.create(session, user, opType) + const updates: any[] = [] + let expectedOpCount = input.length + + // Clear existing indices so we can re-order without running into duplicate key errors. + if (input.some(i => i.leftRightIndex >= 0)) { + updates.push({ + updateMany: { + filter: { 'metadata.area_id': { $in: input.map(i => muuid.from(i.areaId)) } }, + update: { + $set: { 'metadata.leftRightIndex': -1 } + // Don't record change since this is an intermediate step. + } + } + }) + expectedOpCount = expectedOpCount * 2 + } - const bulkData = input.map(({ areaId, leftRightIndex }, index) => ( - { + input.forEach(({ areaId, leftRightIndex }, index) => { + updates.push({ updateOne: { filter: { 'metadata.area_id': muuid.from(areaId) }, update: { @@ -387,11 +403,11 @@ export default class MutableAreaDataSource extends AreaDataSource { } } }) - ) + }) - const rs = (await this.areaModel.bulkWrite(bulkData, { session })).toJSON() + const rs = (await this.areaModel.bulkWrite(updates, { session })).toJSON() - if (rs.ok === 1 && rs.nMatched === rs.nModified && rs.nMatched === input.length) { + if (rs.ok === 1 && rs.nMatched === rs.nModified && rs.nMatched === expectedOpCount) { return input.map(item => item.areaId) } else { throw new Error(`Expect to update ${input.length} areas but found ${rs.nMatched}.`) diff --git a/src/model/__tests__/updateAreas.ts b/src/model/__tests__/updateAreas.ts index 4f3bd7ce..5e2496a9 100644 --- a/src/model/__tests__/updateAreas.ts +++ b/src/model/__tests__/updateAreas.ts @@ -277,13 +277,26 @@ describe('Areas', () => { }) })) + // Able to overwrite existing leftRightIndices without duplicate key errors + const change3: UpdateSortingOrderType = { + areaId: a1.metadata.area_id.toUUID().toString(), + leftRightIndex: 9 + } + const change4: UpdateSortingOrderType = { + areaId: a2.metadata.area_id.toUUID().toString(), + leftRightIndex: 10 + } + + await expect(areas.updateSortingOrder(testUser, [change3, change4])).resolves.toStrictEqual( + [a1.metadata.area_id.toUUID().toString(), a2.metadata.area_id.toUUID().toString()]) + // Make sure we can't have duplicate leftToRight indices >= 0 await expect( - areas.updateSortingOrder(testUser, [{ ...change1, leftRightIndex: change2.leftRightIndex }])) + areas.updateSortingOrder(testUser, [{ ...change3, leftRightIndex: change4.leftRightIndex }])) .rejects.toThrowError(/E11000/) // But we can have duplicate indices < 0 to indicate unsorted await areas.updateSortingOrder(testUser, - [{ ...change1, leftRightIndex: -1 }, { ...change2, leftRightIndex: -1 }]) + [{ ...change3, leftRightIndex: -1 }, { ...change4, leftRightIndex: -1 }]) }) })