【问题标题】:MongoDB Duplicate Documents even after adding unique key即使在添加唯一键之后,MongoDB 也会重复文档
【发布时间】:2015-05-18 07:04:34
【问题描述】:

我创建了一个集合并添加了一个这样的唯一键

db.user_services.createIndex({"uid":1 , "sid": 1},{unique:true,dropDups: true})

集合看起来像这样 “用户服务”

{
 "_id" : ObjectId("55068b35f791c7f81000002d"),
 "uid" : 15,
 "sid" : 1,
 "rate" : 5
},
{

 "_id" : ObjectId("55068b35f791c7f81000002f"),
 "uid" : 15,
 "sid" : 1,
 "rate" : 4
}

问题:

我正在使用 php 驱动程序插入具有相同 uid 和 sid 的文档,并且它正在被插入。

我想要什么

  1. 在 Mongo Shell 上:在 uid 和 sid 上添加唯一键,并且没有具有相同 uid 和 sid 的重复文档。
  2. 在 PHP 方面:有类似 mysql "insert (value) on duplicate key update rate=rate+1" 的东西。也就是说,每当我尝试插入一个文档时,如果没有,它应该被插入,否则它应该更新文档的 rate 字段

【问题讨论】:

    标签: php mongodb mongodb-query aggregation-framework


    【解决方案1】:

    恭喜,您似乎发现了一个错误。这仅在我的测试中发生在 MongoDB 3.0.0 中,或者至少在 MongoDB 2.6.6 中不存在。错误现在记录在SERVER-17599

    注意: 实际上不是“问题”,而是“按设计”确认。删除了版本 3.0.0 的选项。不过仍然在documentation 中列出。

    问题在于,当您尝试在“复合键”字段上存在重复项的集合上创建该索引时,未创建索引并且出错。在上面,索引创建应该在 shell 中产生:

    {
        "createdCollectionAutomatically" : false,
        "numIndexesBefore" : 1,
        "errmsg" : "exception: E11000 duplicate key error dup key: { : 15.0, : 1.0 }",
        "code" : 11000,
        "ok" : 0
    }
    

    当不存在重复项时,您可以按照当前尝试创建索引,它将被创建。

    所以要解决这个问题,首先使用以下过程删除重复项:

    db.events.aggregate([
        { "$group": {
            "_id": { "uid": "$uid", "sid": "$sid" },
            "dups": { "$push": "$_id" },
            "count": { "$sum": 1 }
        }},
        { "$match": { "count": { "$gt": 1 } }}
    ]).forEach(function(doc) {
        doc.dups.shift();
        db.events.remove({ "_id": {"$in": doc.dups }});
    });
    
    db.events.createIndex({"uid":1 , "sid": 1},{unique:true})
    

    然后将不再插入包含重复数据的进一步插入,并记录相应的错误。

    最后要注意的是,“dropDups”是/不是删除重复数据的非常优雅的解决方案。如上所示,您确实想要具有更多控制权的东西。

    对于第二部分,不要使用.insert(),而是使用.update() 方法。它有一个"upsert" 选项

    $collection->update(
        array( "uid" => 1, "sid" => 1 ),
        array( '$set' => $someData ),
        array( 'upsert' => true )
    );
    

    因此,“找到”的文档被“修改”,未找到的文档被“插入”。另请参阅$setOnInsert,了解仅在实际插入文档而不是在修改文档时创建某些数据的方法。


    对于您的具体尝试,.update() 的正确语法是三个参数。 “查询”、“更新”和“选项”:

    $collection->update(
        array( "uid" => 1, "sid" => 1 ),
        array(
            '$set' => array( "field" => "this" ),
            '$inc' => array( "counter" => 1 ),
            '$setOnInsert' => array( "newField" => "another" )
       ),
       array( "upsert" => true )
    );
    

    不允许任何更新操作“访问与该“更新”文档部分中另一个更新操作中使用的路径相同的路径。

    【讨论】:

    • dropDups 选项是removed in MongoDB 3.0。另请参阅:SERVER-14710。数据的不确定性删除是索引选项的意外副作用。
    • @Stennie。说得通。出于这个原因,我自己从未使用过它。仍然在documentation 中,但这将是问题的根源。
    • 谢谢,很好地了解文档!我会为它raise a Jira :)
    • 谢谢.. 它有效.. 你能解决我的问题的第二部分吗...我可以在单个查询中更新重复条目
    • @NeilLunn:聚合查询的最后一部分应该有一个小的更正:db.events.remove({"_id" : { "$in": doc.dups }});跨度>
    【解决方案2】:

    我觉得当前最流行的答案对于这样一个基本的 MongoDB 操作来说有点过于本地化和详细 - 通过一个键从 mongo 中删除重复项。

    通过 mongo > 3.0 的键删除重复项很简单。只需运行此查询,替换 yourDuplicateKey 并假设 _id 是您的主键(确保您使用 mongodump 以防万一):

    db.yourCollection.aggregate([
        { "$group": {
            "_id": { "yourDuplicateKey": "$yourDuplicateKey" },
            "dups": { "$push": "$_id" },
            "count": { "$sum": 1 }
        }},
        { "$match": { "count": { "$gt": 1 } }}
    ]).forEach(function(doc) {
        doc.dups.shift();
        db.yourCollection.remove({ "_id": {"$in": doc.dups }});
    });
    

    【讨论】:

    • 可靠的答案!并且也让我对如何思考 MongoDB 查询有所了解。 Mongo 受到了很多抨击,但在 shell 中编写这样的代码的能力令人难以置信。
    【解决方案3】:

    另一种避免使用多个值重复记录的简单方法

    示例: 使用以下代码可以避免字段“学生姓名”和“家长姓名”的重复值

        $DataForDB = array( "AdmissionNo" => $admissionNo, 
        "StudentName" => $StudentName, "ParentName" => $ParentName);
        if(empty($Coll->findOne(array("StudenName" => $StudentName, "ParentName" => $ParentName)))){
        $Coll->insertOne($DataForDB);
        }
    

    在这种情况下,我们正在检查具有以下字段的文档是否存在,如果存在,则数据未输入到数据库中,如果不存在,则输入数据。

    【讨论】:

      猜你喜欢
      • 2018-11-25
      • 1970-01-01
      • 2014-04-22
      • 1970-01-01
      • 1970-01-01
      • 2019-06-19
      • 2016-10-21
      • 1970-01-01
      • 2012-08-17
      相关资源
      最近更新 更多