user11459242
user11459242

Reputation:

How to delete documents returned by an aggregation query in mongodb

I am attempting to delete all the documents returned by an aggregation in Mongodb.

The query I have is as follows:

db.getCollection("Collection")
  .aggregate([
    {
      $match: { status: { $in: ["inserted", "done", "duplicated", "error"] } }
    },
    {
      $project: {
        yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }
      }
    },  
    { $match: { yearMonthDay: { $eq: "2019-08-06" } } }
  ])
  .forEach(function(doc) {
    db.getCollection("Collection").remove({});
  });

I tried this query but it removes all the data in the database, any suggestions please?

Upvotes: 17

Views: 30921

Answers (3)

phoenixstudio
phoenixstudio

Reputation: 2598

You can add expires to your document, and update this field in the $merge stage of your aggregation.

// 1) add this to your schema
deleteAt: {
  type: Date,
  expires: 0,
}

// 2) set the value of `deleteAt` for the documents to be deleted

// 3) add this at the end of the aggregation 
$merge: {
  into: "Collections" // or "Collection"
}

Upvotes: 0

turivishal
turivishal

Reputation: 36104

As per your query its not required to filter by aggregation and remove by another methods, you can apply this query filters in remove() method's filters,

  • the $expr Allows the use of aggregation expressions within the query language,
db.getCollection("Collection").remove({ 
  $and: [
    { status: { $in: ["inserted", "done", "duplicated", "error"] } },
    {
      $expr: {
        $eq: [
          { $dateToString: { format: "%Y-%m-%d", date: "$date" } },
          "2019-08-06"
        ]
      }
    }
  ]
});

This can also support in deleteOne and deleteMany methods.

Upvotes: 5

ambianBeing
ambianBeing

Reputation: 3529

Since the remove doesn't have a query condition its going to match with all the documents and delete irrespective of the aggregation result.

Solution (match the ids of the current cursor doc):

db.getCollection("Collection")
  .aggregate([
    {
      $match: { status: { $in: ["inserted", "done", "duplicated", "error"] } }
    },
    {
      $project: {
        yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }
      }
    },
    { $match: { yearMonthDay: { $eq: "2019-08-06" } } }
  ])
  .forEach(function(doc) {
    db.getCollection("Collection").remove({ "_id": doc._id });
  });

Another better solution would be to have single round trip to db while deletion is get a list of ids from the aggregation cursor() via cursor.map()

var idsList = db
  .getCollection("Collection")
  .aggregate([
    {
      $match: { status: { $in: ["inserted", "done", "duplicated", "error"] } }
    },
    {
      $project: {
        yearMonthDay: { $dateToString: { format: "%Y-%m-%d", date: "$date" } }
      }
    },
    { $match: { yearMonthDay: { $eq: "2019-08-06" } } }
  ])
  .map(function(d) {
    return d._id;
  });

//now delete those documents via $in operator
db.getCollection("Collection").remove({ _id: { $in: idsList } });

Upvotes: 32

Related Questions