删除mongodb中的重复值

Posted

技术标签:

【中文标题】删除mongodb中的重复值【英文标题】:Remove duplicate values in mongodb 【发布时间】:2016-03-28 15:12:40 【问题描述】:

我正在使用 python 和 tornado 学习 mongodb。我有一个 mongodb 集合,当我这样做时

db.cal.find()

     
    "Pid" : "5652f92761be0b14889d9854",
    "Registration" : "TN 56 HD 6766",
    "Vid" : "56543ed261be0b0a60a896c9",
    "Period" : "10-2015",
    "AOs": [
        "14-10-2015",
        "15-10-2015",
        "18-10-2015",
        "14-10-2015",
        "15-10-2015",
        "18-10-2015"
    ],
    "Booked": [
        "5-10-2015",
        "7-10-2015",
        "8-10-2015",
        "5-10-2015",
        "7-10-2015",
        "8-10-2015"
    ],
    "NA": [
        "1-10-2015",
        "2-10-2015",
        "3-10-2015",
        "4-10-2015",
        "1-10-2015",
        "2-10-2015",
        "3-10-2015",
        "4-10-2015"
    ],

    "AOr": [
        "23-10-2015",
        "27-10-2015",
        "23-10-2015",
        "27-10-2015"
    ]

我需要一个操作来删除Booked,NA,AOs,AOr 中的重复值。最后应该是


     "Pid" : "5652f92761be0b14889d9854",
      "Registration" : "TN 56 HD 6766",
      "Vid" : "56543ed261be0b0a60a896c9",
      "AOs": [
        "14-10-2015",
        "15-10-2015",
        "18-10-2015",

      ],
      "Booked": [
        "5-10-2015",
        "7-10-2015",
        "8-10-2015",

      ],

      "NA": [
        "1-10-2015",
        "2-10-2015",
        "3-10-2015",
        "4-10-2015",

      ],

      "AOr": [
        "23-10-2015",
        "27-10-2015",

      ]

如何在 mongodb 中实现这一点?

【问题讨论】:

【参考方案1】:

您不能首先在此处使用“dropDups”语法,因为它已从 MongoDB 2.6 开始“弃用”并在 MongoDB 3.0 中删除,甚至无法使用。

要从每个列表中删除重复项,您需要在 python 中使用 set 类。

import pymongo


fields = ['Booked', 'NA', 'AOs', 'AOr']
client = pymongo.MongoClient()
db = client.test
collection = db.cal
bulk = colllection.initialize_ordered_op()
count = 0
for document in collection.find():
    update = dict(zip(fields, [list(set(document[field])) for field in fields])) 
    bulk.find('_id': document['_id']).update_one('$set': update)
    count = count + 1
    if count % 200 == 0:
        bulk.execute()
        bulk = colllection.initialize_ordered_op()

if count > 0:
    bulk.execute()

MongoDB 3.2 deprecates Bulk() 及其关联方法并提供.bulkWrite() 方法。此方法可从 Pymongo 3.2 以bulk_write() 获得。使用此方法要做的第一件事是导入UpdateOne 类。

from pymongo import UpdateOne


requests = [] # list of write operations
for document in collection.find():
    update = dict(zip(fields, [list(set(document[field])) for field in fields])) 
    requests.append(UpdateOne('_id': document['_id'], '$set': update))
collection.bulk_write(requests)

这两个查询给出了相同的预期结果:

'AOr': ['27-10-2015', '23-10-2015'],
 'AOs': ['15-10-2015', '14-10-2015', '18-10-2015'],
 'Booked': ['7-10-2015', '5-10-2015', '8-10-2015'],
 'NA': ['1-10-2015', '4-10-2015', '3-10-2015', '2-10-2015'],
 'Period': '10-2015',
 'Pid': '5652f92761be0b14889d9854',
 'Registration': 'TN 56 HD 6766',
 'Vid': '56543ed261be0b0a60a896c9',
 '_id': ObjectId('567f808fc6e11b467e59330f')

【讨论】:

【参考方案2】:

工作解决方案

我创建了一个基于 javascript 的工作解决方案,可在 mongo shell 上使用:

var codes = ["AOs", "Booked", "NA", "AOr"]

// Use bulk operations for efficiency
var bulk = db.dupes.initializeUnorderedBulkOp()

db.dupes.find().forEach(
  function(doc) 

    // Needed to prevent unnecessary operatations
    changed = false
    codes.forEach(
      function(code) 
        var values = doc[code]
        var uniq = []

        for (var i = 0; i < values.length; i++) 
          // If the current value can not be found, it is unique
          // in the "uniq" array after insertion
          if (uniq.indexOf(values[i]) == -1 )
            uniq.push(values[i])
          
        

        doc[code] = uniq

        if (uniq.length < values.length) 
          changed = true
        

      
    )

    // Update the document only if something was changed
    if (changed) 
      bulk.find("_id":doc._id).updateOne(doc)
    
  
)

// Apply all changes
bulk.execute()

带有示例输入的结果文档:

replset:PRIMARY> db.dupes.find().pretty()

  "_id" : ObjectId("567931aefefcd72d0523777b"),
  "Pid" : "5652f92761be0b14889d9854",
  "Registration" : "TN 56 HD 6766",
  "Vid" : "56543ed261be0b0a60a896c9",
  "Period" : "10-2015",
  "AOs" : [
    "14-10-2015",
    "15-10-2015",
    "18-10-2015"
  ],
  "Booked" : [
    "5-10-2015",
    "7-10-2015",
    "8-10-2015"
  ],
  "NA" : [
    "1-10-2015",
    "2-10-2015",
    "3-10-2015",
    "4-10-2015"
  ],
  "AOr" : [
    "23-10-2015",
    "27-10-2015"
  ]

dropDups 中使用索引

这根本行不通。首先,根据 3.0 版,此选项不再存在。既然我们已经发布了 3.2,我们应该找到一种可移植的方式。

其次,即使使用 dropDups,文档也明确指出:

dropDups boolean : MongoDB 仅索引第一次出现的键,并从包含该键后续出现的集合中删除所有 文档 .

因此,如果另一个文档的其中一个帐单代码中的值与前一个相同,则整个文档将被删除。

【讨论】:

您可以使用Remove Duplicates from JavaScript Array 中显示的方法之一从这些数组中删除重复项,然后使用带有批量操作的$set 运算符来更新文档。另请注意,MongoDB 3.2 弃用 Bulk() 及其相关方法。 shell 上既没有 Jquery 也没有 ecma 6,对吧? ;) 我看不出识别唯一性的劣势在哪里。但是 3.2 很好,我也会添加解决方案。【参考方案3】:

假设您想从集合中删除重复的日期,因此您可以使用 dropDups: true 选项添加唯一索引:

db.bill_codes.ensureIndex("fieldName":1, unique: true, dropDups: true) 

更多参考: db.collection.ensureIndex() - MongoDB Manual 3.0

注意:首先备份您的数据库,以防它不完全符合您的预期。

【讨论】:

这只会删除其中一个字段具有完全相同值的其他文档。 我得到错误: "createdCollectionAutomatically" : false, "numIndexesBefore" : 1, "errmsg" : "exception: bad index key pattern Registration: \"TN 56 HD 6766\", Pid : \"5652f92761be0b14889d9854\" : 未知索引插件 'TN 56 HD 676'", "code" : 67, "ok" : 0 您必须提及您的集合键索引,而不是名称和节点进入标准。 这不仅过时,而且显然是错误的,如果没有备份的建议,这将是完全危险的。已弃用的 dropDups 删除了所有 documents,这些文件恰好在索引中具有相同的键值,而不是重复值。【参考方案4】:

你试过“Distinct()”吗?

链接:https://docs.mongodb.org/v3.0/reference/method/db.collection.distinct/

使用 distinct 指定查询

以下示例从 dept 等于“A”的文档中返回嵌入在 item 字段中的字段 sku 的不同值:

db.inventory.distinct( "item.sku",  dept: "A"  )

该方法返回以下不同 sku 值的数组:

[ "111", "333" ]

【讨论】:

不会减少保存的数据。

以上是关于删除mongodb中的重复值的主要内容,如果未能解决你的问题,请参考以下文章

如何删除 MongoDb 中的重复项?

MongoDb:删除另一个数组中的数组索引[重复]

MongoDB Scala - 删除集合中的重复文档

在mongodb中删除数组中的一个项目[重复]

如何从js中的数组中删除元素[元素来自mongodb] [重复]

如何从js中的数组中删除元素[元素来自mongodb] [重复]