40 Stimmen

MongoDB: Eindeutiger Index für die Eigenschaft eines Array-Elements

Ich habe eine ähnliche Struktur wie diese:

class Cat {
  int id;
  List<Kitten> kittens;
}

class Kitten {
  int id;
}

Ich möchte verhindern, dass Benutzer eine Katze mit mehr als einem Kätzchen mit der gleichen Kennung anlegen können. Ich habe versucht, einen Index wie folgt zu erstellen:

db.Cats.ensureIndex({'id': 1, 'kittens.id': 1}, {unique:true})

Aber wenn ich versuche, eine schlecht formatierte Katze einzufügen, akzeptiert Mongo sie.

Übersehe ich etwas? Kann das überhaupt gemacht werden?

48voto

Chris Fulstow Punkte 39665

Soweit ich weiß, erzwingen eindeutige Indizes nur die Einzigartigkeit über verschiedene Dokumente hinweg, so dass dies zu einem Fehler durch doppelte Schlüssel führen würde:

db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )
db.cats.insert( { id: 123, kittens: [ { id: 456 } ] } )

Aber das ist erlaubt:

db.cats.insert( { id: 123, kittens: [ { id: 456 }, { id: 456 } ] } )

Ich bin nicht sicher, ob es eine Möglichkeit gibt, die Einschränkung zu erzwingen, die Sie auf der Mongo-Ebene benötigen, vielleicht ist es etwas, das Sie in der Anwendungslogik überprüfen könnten, wenn Sie einfügen oder aktualisieren?

40voto

doublehelix Punkte 2142

Sicherstellung der Eindeutigkeit der einzelnen Werte in einem Array-Feld

Zusätzlich zum obigen Beispiel gibt es in MongoDB eine Funktion, die sicherstellt, dass beim Hinzufügen eines neuen Objekts/Werts zu einem Array-Feld die Aktualisierung nur dann durchgeführt wird, wenn der Wert/das Objekt noch nicht existiert.

Wenn Sie also ein Dokument haben, das wie folgt aussieht:

{ _id: 123, kittens: [456] }

Dies wäre zulässig:

db.cats.update({_id:123}, {$push: {kittens:456}})

was zu

{ _id: 123, kittens: [456, 456] }

jedoch unter Verwendung der $addToSet Funktion (im Gegensatz zu $push ) würde prüfen, ob der Wert bereits existiert, bevor er hinzugefügt wird. Also, beginnend mit:

{ _id: 123, kittens: [456] }

und dann ausgeführt werden:

db.cats.update({_id:123}, {$addToSet: {kittens:456}})

Das würde keine Auswirkungen haben.

Kurz gesagt, eindeutige Beschränkungen überprüfen nicht die Eindeutigkeit innerhalb der Wertelemente eines Array-Feldes, sondern nur, dass zwei Dokumente keine identischen Werte in den indizierten Feldern haben können.

4voto

timchunght Punkte 292

Es gibt ein Äquivalent von insert mit uniquness in array attribute. Der folgende Befehl führt im Wesentlichen insert aus, während er die Einzigartigkeit der Kätzchen sicherstellt (upsert erstellt es für Sie, wenn das Objekt mit 123 nicht bereits existiert).

db.cats.update(
  { id: 123 },
  { $addToSet: {kittens: { $each: [ 456, 456] }}, $set: {'otherfields': 'extraval', "field2": "value2"}},
  { upsert: true}
)

Der resultierende Wert des Objekts ist

{
    "id": 123,
    "kittens": [456],
    "otherfields": "extraval",
    "field2": "value2"
}

3voto

Nun, was hier wichtig schien, ist die Sicherstellung, dass nicht mehr als ein Element in einem Mongodb-Objekt-Array existieren sollte, mit der gleichen ID oder einige andere Felder, die erforderlich ist, um eindeutig behandelt werden. Dann wird eine einfache Abfrage wie diese für die Aktualisierung ausreichen, mit $addToSet .

Verzeihen Sie mir, ich bin kein Mongo-Shell-Experte und verwende Java Mongo Driver Version 4.0.3

collection = database.getCollection("cat", Cat.class);
UpdateResult result = collection.updateOne(and(eq("Id", 1), nin("kittens.id", newKittenId)), addToSet("kittens", new Kitten("newKittenId")));

Die hier verwendete Abfrage fügte der Abgleichsabfrage eine zusätzliche Bedingung hinzu, die wie folgt lautet: where cat.id is 1 and the newKittenId is not yet owned by any of the kittens that had previously been added. Wenn also die Id für die Katze gefunden wird und kein Kätzchen die neue KittenId übernommen hat, geht die Abfrage weiter und aktualisiert die Kätzchen der Katze durch Hinzufügen eines neuen. Wurde die newKittenId jedoch von einem der Kätzchen übernommen, wird einfach updateresult ohne Zählung und ohne geändertes Feld zurückgegeben (es passiert nichts).

Nota : Dies gewährleistet keine eindeutigen Einschränkungen für die kitten.id, Mongo DB unterstützt keine Eindeutigkeit bei Objekt-Arrays in einem Dokument, und addToSet kann nicht wirklich mit doppelten Elementen in einem Objekt-Array umgehen, außer das Objekt ist eine 100%ige Kopie dessen, was in der Datenbankprüfung steht. hier für weitere Erklärungen über addToSet .

2voto

Amit Wagner Punkte 2871

Gibt es eine Umgehung, die Sie mit der Funktion Dokumentenvalidator .

Hier ein Beispiel für einen Validator, bei dem "a" ein Array ist und innerhalb des Feldes "a" des Unterdokuments der Wert "b" eindeutig sein muss. Dabei wird davon ausgegangen, dass die Sammlung entweder leer ist oder der Regel bereits entspricht:

> db.runCommand({collMod:"coll", validator: {$expr:{$eq:[{$size:"$a.b"},{$size:{$setUnion:"$a.b"}}]}}})
/* test it */
> db.coll.insert({a:[{b:1}]}) /* success */
> db.coll.update({},{ '$push' : { 'a':{b:1}}})
WriteResult({
    "nMatched" : 0,
    "nUpserted" : 0,
    "nModified" : 0,
    "writeError" : {
        "code" : 121,
        "errmsg" : "Document failed validation"
    }
})

Weitere Informationen über diese Lösung finden Sie auf der Originalpfosten

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X