删除mongodb $lookup聚合结果

问题描述 投票:0回答:3

如何删除由于此聚合而返回的所有

chunks
文档?

db.getCollection('chunks').aggregate([
    {
      $lookup:
        {
          from: "files",
          localField: "files_id",
          foreignField: "_id",
          as: "file"
        }
   },
   {
     $match:
       {
         "file.uploadDate":
           {
             $lt: ISODate("2017-06-10T00:00:00.000Z")
           }
       }
   }
])

我的架构有一个名为

files
的集合,其中包含文件元数据(名称、上传日期)和块,其中包含实际数据(二进制、files_id)

我知道

db.collection.deleteMany({})
,但它只接受匹配过滤器。

我有 MongoDB 3.2

mongodb aggregation-framework
3个回答
9
投票

循环结果:

var ops = [];

db.getCollection('chunks').aggregate([
    {
      $lookup:
        {
          from: "files",
          localField: "files_id",
          foreignField: "_id",
          as: "file"
        }
   },
   {
     $match:
       {
         "file.uploadDate":
           {
             $lt: ISODate("2017-06-10T00:00:00.000Z")
           }
       }
   }
]).forEach(doc => {
  ops = [
    ...ops,
    { "deleteOne": {
       "filter": { "_id": doc._id }   
    }}
  ];
  if ( ops.length >= 1000 ) {
    db.getCollection('chunks').bulkWrite(ops);
    ops = [];
  }
});

if ( ops.length > 0 ) {
  db.getCollection('chunks').bulkWrite(ops);
  ops = [];
}

或者在没有 ES6 的环境中:

var ops = [];

db.getCollection('chunks').aggregate([
    {
      $lookup:
        {
          from: "files",
          localField: "files_id",
          foreignField: "_id",
          as: "file"
        }
   },
   {
     $match:
       {
         "file.uploadDate":
           {
             $lt: ISODate("2017-06-10T00:00:00.000Z")
           }
       }
   }
]).forEach(function(doc) {

  ops.push({ "deleteOne": { "filter": { "_id": doc._id }  } });

  if ( ops.length >= 1000 ) {
    db.getCollection('chunks').bulkWrite(ops);
    ops = [];
  }
});

if ( ops.length > 0 ) {
  db.getCollection('chunks').bulkWrite(ops);
  ops = [];
}

使用

.bulkWrite()
那么你基本上是以 1000 个为单位“批处理”请求。因此数据库的实际写入和响应仅在此时发生,而不是针对所有条目。

您无法提供聚合管道作为常规

.remove**()
方法的查询参数。所以你要做的就是用这样的动作循环光标。


7
投票

获得聚合结果后,您可以使用

map
函数获取所有
chunk
ids,然后您可以将
db.collection.remove()
$in
运算符一起使用。

var pipeline = [
  {$lookup:{
      from: "files",
      localField: "files_id",
      foreignField: "_id",
      as: "file"
    }
  },
  {$match:{
      "file.uploadDate":
      {
        $lt: ISODate("2017-06-10T00:00:00.000Z")
      }
    }
  }
];

var cursor = db.chunks.aggregate(pipeline);
var chunkIds = cursor.map(function (chunk) { return chunk._id; });
db.chunks.remove({"_id": { "$in": chunkIds }});

6
投票

另一种方法是将计算移至 mongodb,而不是通过 Node.js 应用程序运行列表。然而,这需要对数据库中的数据进行一些临时更改。

简单的逻辑是这样的:

  • 使用聚合将数据库中的项目标记为删除
  • 发送删除查询以删除所有标记的项目

注意:我正在为最新版本的 mongodb 编写代码,根据文档,我所依赖的

$merge
步骤至少可以从 4.2 版本获得,但不能从 4.0 或更早版本获得。 这个答案对原始问题中使用的版本3.2没有帮助,但我认为值得在这里添加它,因为这个问题+答案通常会显示在对该问题的搜索中。

标记要删除的项目

// .toArray() at the end triggers mongodb to execute the merge step - even though it doesn't return anything.
// await makes sure we wait until all items are marked before continuing.
await db.collection("chunks").aggregate([
  // The lookup and matching as before
  { $lookup: { from: "files", localField: "files_id", foreignField: "_id", as: "files" } },
  { $match: { "files.uploadDate": { $lt: ISODate("2017-06-10T00:00:00.000Z") } } },

  // Remove the files field added by the lookup, no longer needed
  { $removeField: "files" },

  // Mark for deletion by setting a field
  { $set: { __markedForDeletion: true } },

  // Overwrite chunks entries with the deletion marking
  { $merge: { into: "chunks", whenMatched: "replace" } },
]).toArray();

删除标记的项目

await db.collection("chunks").deleteMany({ __markedForDeletion: true });

这种方法的优点是不需要 mongodb 序列化并将数据发送到 Node.js 应用程序,只需等待 mongodb 在内部执行命令,这比这里提到的其他答案要快得多且不易出错。

© www.soinside.com 2019 - 2024. All rights reserved.