mongodb $lookup 聚合的删除结果

Bab*_*yan 4 mongodb aggregation-framework

如何删除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")
           }
       }
   }
])
Run Code Online (Sandbox Code Playgroud)

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

但是我知道db.collection.deleteMany({})它只接受匹配过滤器。

我有 MongoDB 3.2

Nei*_*unn 7

循环结果:

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 = [];
}
Run Code Online (Sandbox Code Playgroud)

或者在没有 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 = [];
}
Run Code Online (Sandbox Code Playgroud)

使用.bulkWrite()然后你基本上是“批处理”1000 个请求。所以来自数据库的实际写入和响应只发生在那个时候,而不是所有条目。

您不能提供聚合管道作为通用.remove**()方法的查询参数。所以你要做的是用这样的动作循环光标。


Sha*_*Roy 5

获得聚合结果后,您可以使用map函数来获取所有chunk ID,然后您可以使用db.collection.remove()with$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 }});
Run Code Online (Sandbox Code Playgroud)

  • 这里的问题是发送到 `$in` 的大量参数,请求很容易打破 BSON 限制。实际上,参数的数量不应超过 1000,否则可能会出现问题。在这个网站上甚至提出了许多关于人们试图做同样事情的问题。这就是我们有批量操作的原因。 (2认同)
  • 我的真正观点是,您实际上并没有在这里“获得”任何东西。您“仍在”循环结果,因为这就是 `.map()` 所做的,并且可能因为您正在构建数组而填满 RAM。小或大,当您有游标时,然后使用它。当您查看我演示的过程时,如果只有 50 个结果,那么仍然只有“一个”请求,而且非常有效。所以实际上,构建一个可以超过 1000 个项目的数组实际上是要花费的。这是需要考虑的。我做到了。 (2认同)

Nie*_*ard 5

另一种方法有点 hacky,即将计算转移到 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();
Run Code Online (Sandbox Code Playgroud)

删除标记的项目

await db.collection("chunks").deleteMany({ __markedForDeletion: true });
Run Code Online (Sandbox Code Playgroud)

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