如何在Mongo中高效地通过查询删除文档?

18

我有一个查询,用于选择要删除的文档。目前,我是手动删除它们,就像这样(使用python):

for id in mycoll.find(query, fields={}):
  mycoll.remove(id)

这似乎不是非常有效率。有更好的方法吗?

编辑

好的,我忘记提及查询细节并因此道歉,因为这很重要。以下是完整的 Python 代码:

def reduce_duplicates(mydb, max_group_size):
  # 1. Count the group sizes
  res = mydb.static.map_reduce(jstrMeasureGroupMap, jstrMeasureGroupReduce, 'filter_scratch', full_response = True)
  # 2. For each entry from the filter scratch collection having count > max_group_size
  deleteFindArgs = {'fields': {}, 'sort': [('test_date', ASCENDING)]}
  for entry in mydb.filter_scratch.find({'value': {'$gt': max_group_size}}):
    key = entry['_id']
    group_size = int(entry['value'])
    # 2b. query the original collection by the entry key, order it by test_date ascending, limit to the group size minus max_group_size.
    for id in mydb.static.find(key, limit = group_size - max_group_size, **deleteFindArgs):
      mydb.static.remove(id)
  return res['counts']['input']

那么,这个程序是做什么的?它将重复键的数量减少到每个键值最多 max_group_size 个,只保留最新的记录。它的工作原理如下:

  1. 将数据映射成(键, 计数)对。
  2. 迭代所有满足 计数 > max_group_size 的键值对。
  3. 按时间戳升序排序(最早的在前面)并限制结果为最旧的前 count - max_group_size条记录,通过key查询数据。
  4. 删除每个找到的记录。

从中可以看到,这实现了将重复项减少到最多 N 条最新记录的任务。所以,最后两步是foreach-found-remove,这是我问题的重要细节,它改变了一切,我必须更具体地描述它 - 抱歉。

现在,关于集合的删除命令。它确实接受查询,但我的查询包括排序和限制。我能用删除命令实现吗?好吧,我已经试过了:

mydb.static.find(key, limit = group_size - max_group_size, sort=[('test_date', ASCENDING)])

这个尝试惨败了。此外,它似乎会损坏Mongo数据库。观察:

C:\dev\poc\SDR>python FilterOoklaData.py
bad offset:0 accessing file: /data/db/ookla.0 - consider repairing database

毋庸置疑,使用foreach-found-remove方法可以正常工作并产生预期的结果。

现在,我希望我已经提供了足够的上下文,并(希望)恢复了我的失落荣誉。

5个回答

39

你可以使用查询语句来删除所有匹配的文档

var query = {name: 'John'};
db.collection.remove(query);

需要注意的是,如果匹配的文档数量很高,您的数据库可能会变得不那么响应。通常建议以较小的块删除文档。

假设您要从一个集合中删除100k个文档。最好执行100个查询,每个查询删除1k个文档,而不是一个查询删除全部100k个文档。


假设你有100k个文档需要从一个集合中删除。最好执行100次查询,每次删除1k个文档,而不是执行1次查询删除所有的100k个文档。怎样才能达到这个目的? - Ezequiel
1
@Ezequiel:可以获取所有要删除的文档的ID,然后将它们分成1000个一组,并使用$in运算符发送多个删除命令。 - Sergio Tulentsev

12

您可以直接使用MongoDB脚本语言删除它:

db.mycoll.remove({_id:'your_id_here'});

2

1
我建议在记录数量较大时进行分页。
首先,获取要删除的数据数量:
-------------------------- COUNT --------------------------
var query= {"FEILD":"XYZ", 'DATE': {$lt:new ISODate("2019-11-10")}};
db.COL.aggregate([
    {$match:query},
    {$count: "all"}
  ])

第二步:逐块删除:

-------------------------- DELETE --------------------------
var query= {"FEILD":"XYZ", 'date': {$lt:new ISODate("2019-11-10")}};
var cursor = db.COL.aggregate([
    {$match:query},
    { $limit : 5 }
  ])
cursor.forEach(function (doc){
    db.COL.remove({"_id": doc._id});
});

这应该更快:
var query={"FEILD":"XYZ", 'date': {$lt:new ISODate("2019-11-10")}};
var ids = db.COL.find(query, {_id: 1}).limit(5);
db.tags.deleteMany({"_id": { "$in": ids.map(r => r._id)}});

0

cmd 中运行此查询

db.users.remove( {"_id": ObjectId("5a5f1c472ce1070e11fde4af")});

如果您正在使用 node.js,请编写以下代码

User.remove({ _id: req.body.id },, function(err){...});

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