我创建了一个集合并添加了一个像这样的唯一键
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 的文档,并且它正在插入。
我想要什么
- 在 Mongo Shell 上:在 uid 和 sid 上添加唯一键,没有具有相同 uid 和 sid 的重复文档。 在
- PHP方面:具有类似mysql"在重复键上插入(值)更新速率=速率+ 1"的东西。也就是说,每当我尝试插入文档时,如果没有,则应插入它,否则它应该更新文档的费率字段
恭喜,您似乎发现了一个错误。在我的测试中,这只发生在MongoDB 3.0.0上,或者至少在MongoDB 2.6.6中不存在。错误现在记录在 SERVER-17599
注: 实际上不是"问题",而是"设计"确认。删除了版本 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 )
);
不允许任何更新操作"访问与该"更新"文档部分中的另一个更新操作中使用的相同路径。
我觉得目前最流行的答案对于这样一个基本的 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 }});
});
另一种避免使用多个值重复记录的简单方法
例:使用以下代码可以避免字段"学生姓名"和"家长姓名"的重复值
$DataForDB = array( "AdmissionNo" => $admissionNo,
"StudentName" => $StudentName, "ParentName" => $ParentName);
if(empty($Coll->findOne(array("StudenName" => $StudentName, "ParentName" => $ParentName)))){
$Coll->insertOne($DataForDB);
}
在这种情况下,我们正在检查具有以下字段的文档是否存在,如果存在,则数据未输入数据库,如果不存在,则输入数据。
在我的情况下,相当大的集合,我添加了{allowDiskUse:true }来使其工作。
db.yourCollection.aggregate([
{ "$group": {
"_id": { "yourDuplicateKey": "$yourDuplicateKey" },
"dups": { "$push": "$_id" },
"count": { "$sum": 1 }
}},
{ "$match": { "count": { "$gt": 1 } }}
], { allowDiskUse: true } )
.forEach(function(doc) {
doc.dups.shift();
db.yourCollection.remove({ "_id": {"$in": doc.dups }});
});