diff --git a/src/api/db/models/Image.ts b/src/api/db/models/Image.ts index 53af8c83..20f18db7 100644 --- a/src/api/db/models/Image.ts +++ b/src/api/db/models/Image.ts @@ -1089,64 +1089,89 @@ export class ImageModel { static async deleteLabelsFromImages( input: { labelId: string }, context: Pick, - ): Promise[]> { + ): Promise { const allImagesWithLabel = await Image.find({ 'projectId': context.user['curr_project']!, 'objects.labels.labelId': input.labelId, }); const operations = allImagesWithLabel.reduce((operations: any[], img) => { - const { removable, unlockable } = img.objects.reduce((acc, obj) => { + const { removable, unlockable, rest } = img.objects.reduce((acc, obj) => { const firstValidated = obj.labels.find((lbl) => lbl.validation && lbl.validation.validated); if (obj.labels.length === 1 && obj.labels[0].labelId === input.labelId) { acc.removable.push(obj._id); } else if (obj.labels.length > 1 && obj.locked === true && firstValidated !== undefined && firstValidated.labelId === input.labelId) { acc.unlockable.push(obj); + } else { + acc.rest.push(obj._id); } return acc - }, { removable: [] as mongoose.Types.ObjectId[], unlockable: [] as ObjectSchema[] }); - + }, + { + removable: [] as mongoose.Types.ObjectId[], + unlockable: [] as ObjectSchema[], + rest: [] as mongoose.Types.ObjectId[] + }); - const removeOperation = { - updateOne: { - filter: { _id: img._id }, - update: { - $pullAll: { 'objects._id': [removable] } - } - } - }; + const removeOperation = removable.length > 0 + ? [{ + updateOne: { + filter: { _id: img._id }, + update: { + $pull: { 'objects': { + _id: { + $in: removable + } + }} + } + } + }] + : []; const unlockOperations = unlockable.map((obj) => { return { - updateone: { + updateOne: { filter: { _id: img._id }, update: { - $set: { 'objects.$[obj].locked': false } + $set: { 'objects.$[obj].locked': false }, + $pull: { + 'objects.$[obj].labels': { + labelId: input.labelId + } + } }, - arrayfilters: [ + arrayFilters: [ { 'obj._id': new ObjectId(obj._id) } ] } } }); - const standardOperation = { - updateOne: { - filter: { _id: img._id }, - update: { - $pull: { 'objects.labels._id': input.labelId } + const standardOperations = rest.map((objId) => { + return { + updateOne: { + filter: { _id: img._id }, + update: { + $pull: { + 'objects.$[obj].labels': { + labelId: input.labelId + } + } + }, + arrayFilters: [ + { 'obj._id': objId } + ] } } - }; - - return [...operations, ...[removeOperation], ...unlockOperations, ...[standardOperation]]; + }); + return [...operations, ...removeOperation, ...unlockOperations, ...standardOperations]; }, []); - await Image.bulkWrite(operations); + const res = await Image.bulkWrite(operations); - return [] + return res.isOk(); } diff --git a/src/api/db/models/Project.ts b/src/api/db/models/Project.ts index 7894f81a..ed7ab34e 100644 --- a/src/api/db/models/Project.ts +++ b/src/api/db/models/Project.ts @@ -721,6 +721,8 @@ export class ProjectModel { const count = await ImageModel.countImagesByLabel([input._id], context); + // TODO + // Need to benchmark so that we can up the limit if bulk works faster if (count > MAX_LABEL_DELETE) { const msg = `This label is already in extensive use (>${MAX_LABEL_DELETE} images) and cannot be ` + @@ -728,12 +730,10 @@ export class ProjectModel { throw new DeleteLabelError(msg); } - await ImageModel.deleteAnyLabels( - { - labelId: input._id, - }, - context, - ); + let isBulkDeleteOk = await ImageModel.deleteLabelsFromImages({ labelId: input._id }, context); + if (!isBulkDeleteOk) { + return { isOk: false }; + } project.labels.splice(project.labels.indexOf(label), 1);