如何在mongodb中删除N个文档

78

在我的集合中,文档包含状态(status)和时间戳(timestamp)等关键字。当我想要查找最新的十个文档时,我会编写以下查询语句。

db.collectionsname.find().sort({"timestamp"-1}).limit(10)

这个查询给我想要的结果,但是当我想删除最新的十个文件时,我编写了以下查询

db.collectionsname.remove({"status":0},10).sort({"timestamp":-1})

但它显示以下错误 TypeError: Cannot call method 'sort' of undefined 并且我再次编写了以下相同的查询 db.collectionsname.remove({"status":0},10) 它只删除一个文档。那么我该如何编写一个查询,可以删除十个最新的文档,并按时间戳排序?


1
这可能会对你有所帮助:http://docs.mongodb.org/manual/reference/command/findAndModify/ - Mina
10个回答

101

使用removefindAndModify时,您无法设置限制。因此,如果您想精确地限制要删除的文档数量,则需要分两个步骤完成。

db.collectionName.find({}, {_id : 1})
    .limit(100)
    .sort({timestamp:-1})
    .toArray()
    .map(function(doc) { return doc._id; });  // Pull out just the _ids

然后将返回的_id传递给remove方法:

db.collectionName.remove({_id: {$in: removeIdsArray}})

顺便说一下:您无法从已限制大小的集合中删除文档。


如果所有记录都将被删除,那么排序部分由谁来完成? - ULazdins
2
请纠正我,但是 limit(100)sort({timestamp:-1}) 之前或之后会有所区别。在@WiredPrairie的示例中,限制结果为100,并在排序之后进行排序(.sort({timestamp:-1}).limit(100))。在这种情况下,排序语句对返回的记录没有影响。另一方面,如果您编写 .limit(100).sort({timestamp:-1}),它将会产生不同的结果。 - ULazdins
12
无论你在游标上的排序和限制调用顺序如何,Mongo都会在限制结果之前应用排序。 - joeytwiddle
1
如果数字很大怎么办?我收到了错误信息:“从JavaScript转换为BSON失败:对象大小19888938超过了16793600字节的限制。” - JaskeyLam

33

您可以将查找查询映射的输出进行管道处理,使用_id并执行基于$in查询的删除操作,例如:

db.collection.remove({_id: 
    { $in: db.collection.find().sort({timestamp:-1}).limit(100).map(a => a._id) }
})

谢谢,这个在Mongo命令行客户端上也可以工作。 - Clément Renaud
2
在 MongoDB 5.0 上使用新的 mongo shell mongosh,会返回以下错误信息:Error: cyclic dependency detected - barrypicker
3
如果在“.map”之前添加“.toArray()”,循环依赖就会消失。 - Noumenon

25

设N为要删除的记录数。

    db.collectionName.find().limit(N).forEach(doc => 
     { 
       db.collectionName.remove({_id:doc._id})
     }
    )

1
虽然这个方法在技术上可行,但会导致多次调用mongo的删除操作。最好像所选答案一样一次性发送。 - checklist
2
恭喜,现在您有 N + 1 个数据库调用。 - tchelidze
3
这仍然是一个有用的答案。例如:当您在 dev 数据库中拥有大量文档并且只想保留其中的 200 万条而不是 400 万条时,您可以让它在夜间执行清理操作,而不是打印 200 万个 _id 并将它们提供给程序(很可能分批进行)。 - JavaTec

13

要从您的集合myCollection中删除N个文档:

db.getCollection('myCollection').find({}).limit(N).forEach(function(doc){
    db.getCollection('myCollection').remove({_id: doc._id});
})

2
这对我在Robo 3T中非常有效,与被接受的答案相比。 - Kiechlus
我登录了以点赞这个。谢谢。 - Bilaal Abdel Hassan

5

可行的解决方案(受以上答案启发):

(当需要删除大量记录时,$in 操作符有一定限制,因此这是最终解决方案)以删除100万条记录为例:

var ITERATIONS=1000;
var LIMIT=1000;
for(i=0; i<ITERATIONS; i++) {
    arr = db.MyCollection.find({}, {_id : 1}).limit(LIMIT).toArray().map(function(doc) { return doc._id; });
    db.MyCollection.remove({_id: {$in: arr}});
    // sleep(1);  // Optional for not loading mongo in case of huge amount of deletions
}

你可以在Mongo Shell或Robomongo 3T中运行此命令


0

我发现的方法是:

        let uid = "your_user";

        let a = await db
          .collection(`notifications`)
          .find({
            owner: uid,
          })
          .sort({ _id: -1 })
          .skip(1000)
          .limit(1)
          .toArray();

        if (a.length > 0) {
          let _id = new ObjectId(a[0]._id);
          db.collection(
            `notifications`
          ).deleteMany({
            owner: uid,
            _id: { $lt: _id },
          });
        }

说明:'a'变量获取你想要删除旧条目的最后一个条目的_id。您可以使用'skip'指定n值。例如:您有5000个条目,但是您只想保留400个新条目,只需将skip设置为“ 400”。在此示例中,我们按_id排序,但您可以使用任何您想要使用的方式。您可以使用find过滤结果以完全匹配所需内容。

算法的第二部分执行删除。使用与第一个查找匹配的相同过滤器(owner:uid),并删除少于“_id”的许多内容。

在此代码块之前还可以使用'count' if语句检查是否有必要进行检查,以避免服务器成本。


0

这里有另一种方法:

步骤1:在临时集合中对文档进行排序、限制和$out操作

db.my_coll.aggregate([
  { $sort: { _id: - 1} },
  { $limit: 10000 },
  { $out: 'my_coll_temp' }
])

步骤2:从原始集合中删除所有文档(不删除,以保留索引)。此步骤可能使这种方法变得不太灵活,因为删除可能并非总是可行。

db.my_coll.remove({})

步骤3:将临时集合中的文档移回到原始集合

db.my_coll_temp.aggregate([ { $out: 'my_coll' } ])

步骤4:删除临时集合

db.my_coll_temp.drop()

-1

另一种方法是编写Python脚本。

from pymongo import MongoClient

def main():
    local_client = MongoClient()
    collection = local_client.database.collection
    cursor = collection.find()
    total_number_of_records = 10000

    for document in cursor:
        id = document.get("_id")

        if total_number_of_records == 100:
            break

        delete_query = {"_id": id}
        collection.delete_one(delete_query)

        total_number_of_records -= 1

if __name__ == "__main__":
    # execute only if run as a script
    main()

-8

查询 SQL 是什么

db.order.find({"业务员姓名" : "吊炸天"},{"业务员编号":0}).sort({ "订单时间" : -1 })

结果是

{
"_id" : ObjectId("5c9c875fdadfd961b4d847e7"),
"推送ID" : "248437",
"订单时间" : ISODate("2019-03-28T08:35:52Z"),
"订单状态" : "1",
"订单编号" : "20190328163552306694",
"业务员姓名" : "吊炸天"
}
{
"_id" : ObjectId("5c9c875fdadfd961b4d847e8"),
"推送ID" : "248438",
"订单时间" : ISODate("2019-03-28T08:35:52Z"),
"订单状态" : "1",
"订单编号" : "20190328163552178132",
"业务员姓名" : "吊炸天"
}
{
"_id" : ObjectId("5c9c875fdadfd961b4d847e5"),
"推送ID" : "248435",
"订单时间" : ISODate("2019-03-28T08:35:51Z"),
"订单状态" : "1",
"订单编号" : "20190328163551711074",
"业务员姓名" : "吊炸天"
}
{
"_id" : ObjectId("5c9c875fdadfd961b4d847e6"),
"推送ID" : "248436",
"订单时间" : ISODate("2019-03-28T08:35:51Z"),
"订单状态" : "1",
"订单编号" : "20190328163551758179",
"业务员姓名" : "吊炸天"
}

现在删除3和4的数据

var name = ["吊炸天"]
var idArray = db.order.find({"业务员姓名" : {$in:name}},{"订单编号":1,})
                .sort({ "订单时间" : -1 })
                .skip(2)
                .map(function(doc){return doc.订单编号})

db.order.deleteMany({"订单编号":{$in:idArray}})

返回结果为

{
"acknowledged" : true,
"deletedCount" : 2
}

-8
以下查询将从集合中查找并删除最新的10个文档:
db.collectionsname.findAndModify({
    query: { 'status':0 },
    sort: { 'timestamp': -1 },
    limit: 10,
    remove: true
});

5
这个方法不支持"limit"字段,实际上如果你尝试使用它,你会发现它只删除了一个元素(因为这是默认逻辑)。 - tweak2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接