web-dev-qa-db-ja.com

一意のキーを追加した後でも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ドライバーを使用して同じidとsidのドキュメントを挿入していますが、挿入されています。

欲しいもの

  1. Mongo Shellの場合:uidとsidに重複するドキュメントがなく、uidとsidに一意のキーを追加します。
  2. PHP Side:mysql "insert(value)on duplicate key update rate = rate + 1"のようなものがある。それは私がドキュメントを挿入しようとするときはいつでも、それがそこになければ挿入されるべきであり、それはドキュメントのレートフィールドを更新するべきです
18
Raj Sharma

おめでとうございます。バグを見つけたようです。これは、私のテストではMongoDB 3.0.0でのみ発生するか、少なくともMongoDB 2.6.6では発生しません。 SERVER-17599 にバグが記録されました

[〜#〜]注[〜#〜]:実際には「問題」ではなく、「設計により」確認されています。バージョン3.0.0のオプションを削除しました。 ドキュメント にもリストされています。

問題は、「複合キー」フィールドに既存の重複があるコレクションでこれを作成しようとすると、インデックスが作成されず、エラーが発生することです。上記では、インデックスを作成するとシェルでこれが生成されます。

_{
    "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」は重複データを削除するための非常に洗練されたソリューションではないということです。上で示したように、より詳細な制御が必要です。

2番目の部分では、.insert()を使用するのではなく、.update()メソッドを使用します。 "upsert" オプションがあります

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

したがって、「見つかった」ドキュメントは「変更」され、見つからなかったドキュメントは「挿入」されます。ドキュメントが実際に挿入されたときにのみ特定のデータを作成し、変更時には作成しない方法については、 _$setOnInsert_ も参照してください。


特定の試みのために、.update()の正しい構文は3つの引数です。 「クエリ」、「更新」、「オプション」:

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

「更新」ドキュメントセクションの別の更新操作で使用される「同じパスにアクセス」する更新操作は許可されていません。

37
Neil Lunn

現在の最も人気のある答えは、このような基本的な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 }});
});
18
chakeda

複数の値を使用して重複レコードを回避するもう1つの簡単な方法

例:次のコードを使用すると、フィールド「学生名」と「親の名前」の重複値を回避できます

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

この場合、次のフィールドを持つドキュメントが存在するかどうかを確認します。存在する場合、データが存在せず、DBにデータが入力されません。

0
Tech guy