MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

MongoDB Duplicate Documents auch nach dem Hinzufügen eines eindeutigen Schlüssels

Herzlichen Glückwunsch, Sie scheinen einen Fehler gefunden zu haben. Dies passiert in meinen Tests nur mit MongoDB 3.0.0 oder ist zumindest bei MongoDB 2.6.6 nicht vorhanden. Fehler jetzt bei SERVER-17599 aufgezeichnet

HINWEIS :Eigentlich kein "Problem", sondern "absichtlich" bestätigt. Die Option für Version 3.0.0 wurde entfernt. Trotzdem in der Dokumentation aufgeführt.

Das Problem besteht darin, dass der Index nicht erstellt wird und Fehler auftreten, wenn Sie versuchen, dies für eine Sammlung mit vorhandenen Duplikaten in den Feldern „Zusammengesetzter Schlüssel“ zu erstellen. Oben sollte die Indexerstellung dies in der Shell ergeben:

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

Wenn keine Duplikate vorhanden sind, können Sie den Index so erstellen, wie Sie es gerade versuchen, und er wird erstellt.

Um dies zu umgehen, entfernen Sie zuerst die Duplikate mit einem Verfahren wie dem folgenden:

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

Dann werden weitere Einfügungen mit doppelten Daten nicht eingefügt und der entsprechende Fehler wird aufgezeichnet.

Der letzte Hinweis hier ist, dass "dropDups" keine sehr elegante Lösung zum Entfernen doppelter Daten ist/war. Sie möchten wirklich etwas mit mehr Kontrolle, wie oben gezeigt.

Verwenden Sie für den zweiten Teil nicht .insert() Verwenden Sie die .update() Methode. Es hat eine "Upsert"-Option

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

So werden die „gefundenen“ Dokumente „geändert“ und die nicht gefundenen Dokumente „eingefügt“. Siehe auch $setOnInsert um bestimmte Daten nur dann zu erstellen, wenn das Dokument tatsächlich eingefügt wird und nicht, wenn es geändert wird.

Für Ihren konkreten Versuch die korrekte Syntax von .update() sind drei Argumente. "Abfrage", "Update" und "Optionen":

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

Keine der Aktualisierungsoperationen darf "auf denselben Pfad zugreifen", der in einer anderen Aktualisierungsoperation in diesem "Aktualisierungs"-Dokumentabschnitt verwendet wird.