MongoDB重复文档,即使在添加唯一键后也是如此

我已经创建了一个集合并添加了一个唯一键,如下所示

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

该系列看起来像这个“user_services”

{
 "_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的东西“在重复的密钥更新速率=rate+ 1上插入(值)”。也就是说,每当我尝试插入文档时,如果不存在,则应插入该文档的速率字段

答案 1

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

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

问题是,当您尝试在“复合键”字段上具有现有重复项的集合上创建索引时,索引未创建,并且会出现错误。在上面,索引创建应该在 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()

$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 )
);

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


答案 2

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

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

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 }});
});

推荐