How to delete all documents containing property and replacing all of them with one document?

I have a document (product) with subdocuments (reports).

I am scraping and adding reports. Then I have a cron job and when the announcedRepair date ‘arrives’ I want to do the following simultaneously:

  • Delete all the reports with certain announcedRepair date
  • then right after add a custom report saying it’s fixed.
  • if there are multiple reports with the same announcedRepair, don’t add multiple custom reports, but only one while adding, I’m trying to check that

Nothing I’ve tried so far works. either I got it to work so I delete and add custom reports, but then I can’t manage to only 1 custom fixed report, or when I add the condition, it’s not working at all (how I have it now, I’m adding the whole condition as a string to

db ('"$cond":[
                    { "$eq": ["$isBroke", true]},...')

How can I do it? Thanks!!


const productSchema = new mongoose.Schema({
    name: { 
        type: String
    },
    reports: [ReportSchema]
...
})

mongoose.model('Product', productSchema);

const reportSchema = new mongoose.Schema({   
    title: String,
    category: String,
    announcedRepair: Date,
    isBroke: Boolean
    ...
});
        const isFixedreport = {
            _id: id,
            name,
            category,
            announcedRepair: null,
            isBroke: false,
            ...
        };

await Product.findOneAndUpdate({
        "reports.announcedRepair": announcedRepair
    },
    {
        "$pull": {
            "reports": {
                "announcedRepair": announcedRepair
            }
        }
    },  {new: true})


await Product.findOneAndUpdate({
        "name": name,
        "category": category
    },
    {
        "$push": {
            "reports":  {
                "$cond":[
                    { "$eq": ["$isBroke", true]},
                    , isFixedreport,
                    "$$REMOVE"
                ]
            }
        }
    },  {new: true})

Hi @Anna_N_A and welcome to the MongoDB community forum!!

If I understand the question correctly, from the above sample schema shared, you need to remove the subdocument report for a specific date and wish to insert the isFixed dummy document in place for all the removed subdocuments.
Please correct me know if my understanding is wrong here.

Can you also help with the below details which would help me replicate the same in my local environment.

  1. A sample document and how does the expected output looks like.
  2. The MongoDB version you are on.

Best Regards
Aasawari

Hi, thank you for your reply! Yes.
1.

{
    "_id" : ObjectId(), //1st report
    "name" : "Product1",
    "reports" : [
        {
            "_id" : ObjectId(),
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-05-16T12:00:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : null
        },
        {
       "_id" : ObjectId(),  //2nd report
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-03-16T12:30:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : ISODate("2023-04-28T00:00:00.000+0000")
        },
        {
       "_id" : ObjectId(),  // 3rd report
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-03-16T12:28:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : ISODate("2023-04-28T00:00:00.000+0000")
        },
   {
       "_id" : ObjectId(), //4th report
            "category" : "abc",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-03-16T10:00:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : ISODate("2023-07-18T00:00:00.000+0000")
        },
    ]
}

here if the announcedRepair date is 2023-04-28T00:00:00.000+0000, I would remove the 2nd and 3rd report at same time as it has same said announcedRepair. Now, If the removed reports have same category, I want them all to be replaced by one isFixed document. For the ones with different category, I add each time 1 isFixed document.

 const isFixedreport = {
            _id: ObjectId(),
            name: Product1,
            category: "xxx",
            dateTime: new Date(),
            announcedRepair:  null,
            isBroke: false
        };

so after it should look like this:

{
    "_id" : ObjectId(),
    "name" : "Product1",
    "reports" : [
        {
            "_id" : ObjectId(),
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-05-16T12:00:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : null
        },
        {
       "_id" : ObjectId(),
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : false,
            "dateTime" : ISODate("2023-03-17T10:38:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : null
        },
   {
       "_id" : ObjectId(),
            "category" : "xxx",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-03-16T10:00:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : ISODate("2023-07-18T00:00:00.000+0000")
        },
    ]
}
  1. Version: v4.4.13

I also want to mention, that when I add reports and schedule a cron job for the deletion based on the announcedRepair time, I am iterating, so I every time i iterate over a report, I check for the announcedRepair and schedule the deletion. This is also related to the isFixedreport being added twice instead of once. Because of that I am trying to find a way to check - is there already a isFixedreport for same category/where isBroke is false ? if yes, dont add another one.

Thank you so so much for your help!!!

Hi @Anna_N_A and thank you for sharing the sample documents with other details.

Looking at the sample document shared I feel the schema could be redesigned in a more efficient way.

The recommendation here is to make the reports schema a separate collection which would make the query meeting your requirements simpler and more readable.
The sample document from the reports collection would look like:

{
       "_id" : ObjectId(),
            "category" : "abc",
            "name" : "Product1",
            "isBroke" : true,
            "dateTime" : ISODate("2023-03-16T10:00:00.000+0000"),
            "offset" : NumberInt(-60),
            "announcedRepair" : ISODate("2023-07-18T00:00:00.000+0000")
        }

Let us know if you can consider the above recommendation which would further help us to form an efficient query response.

Best Regards
Aasawari

Thank you for the reply. I made the reports only a subdocument, because usually, because when I query to find all the reports for a product, its just 1 query where I find the product and return all the reports. Otherwise I would query for the product to get its content as well as another query for each report. Would that be not less efficient? Even though, yes, when I add reports, it would probably make more sense to have them in a seperate collection.

Hi @Anna_N_A

Yes, and after the change recommended in the above post, this is how approximately how the query will look like:

db.reports.updateMany( { "announcedRepair": ISODate("2023-04-28T00:00:00.000Z")}, 
          { $set: { 
                   "dateTime": new Date(), 
                   "announcedRepair": null, 
                   "isBroke": false}})
{
  acknowledged: true,
  insertedId: null,
  matchedCount: 2,
  modifiedCount: 2,
  upsertedCount: 0
}

Thanks
Aasawari

So your code is for deleting documents based on the announcedRepair. I believe since its an own collection, I dont need to update anything but I can add an expiration date to the document based on announcedRepair and schedule when to add my custom fixedReport?! Also, If its own collection, how can I add an array of documents (array of reports) all at once but based on the condition that there is no document with same dateTime property and same name?

Also, using your code I will still replace every document with same announcedRepair date but that is not what I want. I still want to replace ALL documents with same announced date AND same category but only once!

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.