sql >> Database >  >> NoSQL >> MongoDB

MongoDB dubbele documenten, zelfs na het toevoegen van een unieke sleutel

Gefeliciteerd, het lijkt erop dat je een bug hebt gevonden. Dit gebeurt alleen met MongoDB 3.0.0 in mijn testen, of is in ieder geval niet aanwezig bij MongoDB 2.6.6. Bug nu geregistreerd op SERVER-17599

OPMERKING :Niet echt een "probleem" maar "by design" bevestigd. De optie voor versie 3.0.0 laten vallen. Staat echter nog steeds in de documentatie.

Het probleem is dat de index niet wordt gemaakt en dat er fouten optreden wanneer u deze probeert te maken op een verzameling met bestaande duplicaten op de "samengestelde sleutel"-velden. Wat het bovenstaande betreft, zou het maken van een index dit in de shell moeten opleveren:

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

Als er geen duplicaten aanwezig zijn, kunt u de index maken zoals u momenteel probeert en deze zal worden gemaakt.

Dus om dit te omzeilen, verwijdert u eerst de duplicaten met een procedure als deze:

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

Dan worden verdere invoegingen met dubbele gegevens niet ingevoegd en wordt de juiste fout geregistreerd.

De laatste opmerking hier is dat "dropDups" geen erg elegante oplossing is/was voor het verwijderen van dubbele gegevens. Je wilt echt iets met meer controle, zoals hierboven aangetoond.

Gebruik voor het tweede deel in plaats van .insert() gebruik de .update() methode. Het heeft een "upsert"-optie

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

Dus de "gevonden" documenten worden "aangepast" en de niet gevonden documenten worden "ingevoegd". Zie ook $setOnInsert voor een manier om alleen bepaalde gegevens aan te maken wanneer het document daadwerkelijk is ingevoegd en niet wanneer het wordt gewijzigd.

Voor uw specifieke poging, de juiste syntaxis van .update() zijn drie argumenten. "query", "update" en "opties":

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

Geen van de updatebewerkingen mag "toegang krijgen tot hetzelfde pad" als gebruikt in een andere updatebewerking in die "update" documentsectie.



  1. Lombok - java.lang.StackOverflowError:null op toString methode

  2. MongoDB - Maak een back-up

  3. Flask by example - Een Redis-taakwachtrij implementeren

  4. Een query schrijven om meerdere waarden toe te voegen aan een sleutel in REDIS Hashes?