如何删除由于此聚合而返回的所有
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
循环结果:
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**()
方法的查询参数。所以你要做的就是用这样的动作循环光标。
获得聚合结果后,您可以使用
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 }});
另一种方法是将计算移至 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 在内部执行命令,这比这里提到的其他答案要快得多且不易出错。