Skip to content

Commit

Permalink
feat: use bulkWrite for deleting images, test that operations work
Browse files Browse the repository at this point in the history
  • Loading branch information
lessej committed Jan 2, 2025
1 parent 72d5b3c commit 1c098e8
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 31 deletions.
75 changes: 50 additions & 25 deletions src/api/db/models/Image.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1089,64 +1089,89 @@ export class ImageModel {
static async deleteLabelsFromImages(
input: { labelId: string },
context: Pick<Context, 'user'>,
): Promise<HydratedDocument<ImageSchema>[]> {
): Promise<boolean> {
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();
}


Expand Down
12 changes: 6 additions & 6 deletions src/api/db/models/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -721,19 +721,19 @@ 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 ` +
' automatically deleted. Please contact nathaniel[dot]rindlaub@tnc[dot]org to request that it be manually deleted.';
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);

Expand Down

0 comments on commit 1c098e8

Please # to comment.