MongoDB Viele-zu-Viele-Vereinigung

143

Wie würden Sie eine Viele-zu-Viele-Verbindung mit MongoDB herstellen?

Beispielsweise; Angenommen, Sie haben eine Benutzertabelle und eine Rollentabelle. Benutzer haben viele Rollen und Rollen haben viele Benutzer. In SQL Land würden Sie eine UserRoles-Tabelle erstellen.

Users:
    Id
    Name

Roles:
    Id
    Name

UserRoles:
    UserId
    RoleId

Wie wird dieselbe Art von Beziehung in MongoDB behandelt?

Josh Close
quelle
Siehe auch Antworten auf diese Frage und diese Frage
Matthew Murdoch

Antworten:

96

Abhängig von Ihren Abfrageanforderungen können Sie alles in das Benutzerdokument einfügen:

{name:"Joe"
,roles:["Admin","User","Engineer"]
}

Um alle Ingenieure zu erhalten, verwenden Sie:

db.things.find( { roles : "Engineer" } );

Wenn Sie die Rollen in separaten Dokumenten verwalten möchten, können Sie die _id des Dokuments anstelle des Namens in das Rollenarray aufnehmen:

{name:"Joe"
,roles:["4b5783300334000000000aa9","5783300334000000000aa943","6c6793300334001000000006"]
}

und richten Sie die Rollen wie folgt ein:

{_id:"6c6793300334001000000006"
,rolename:"Engineer"
}
diederikh
quelle
7
Letzteres wäre besser, da ich eine Liste aller verfügbaren Rollen erhalten muss. Der einzig schlechte Teil ist, dass ich dann beide Enden der Assoziation einrichten muss. Wenn Sie auf SQL-Weise eine UserRole hinzufügen, wird der Benutzer über die Rolle und die Rolle über den Benutzer informiert. Auf diese Weise muss ich die Rolle für den Benutzer und den Benutzer für die Rolle festlegen. Ich denke, das ist aber in Ordnung.
Josh Close
46
Nur weil eine Datenbank SQL nicht unterstützt, bedeutet dies nicht, dass Referenzen keine nützlichen Tools sind. NoSQL! = NoReference siehe diese Erklärung: mongodb.org/display/DOCS/Schema+Design
Tom Gruner
8
Dies scheint keine gute Idee zu sein. Wenn Sie nur sechs Rollen haben, sicher, aber was wäre, wenn Sie 20000 Objekte hätten, die mit 20000 weiteren Objekten verknüpft werden könnten (in einer Viele-Viele-Beziehung)? Sogar die MongoDB-Dokumente weisen darauf hin, dass Sie keine veränderlichen, riesigen Arrays von Referenzen haben sollten. docs.mongodb.org/manual/tutorial/…
CaptSaltyJack
Offensichtlich möchten Sie für viele-zu-viele-Beziehungen mit vielen Objekten eine andere Lösung verwenden (wie das Beispiel für Herausgeber / Bücher in den Dokumenten). In diesem Fall funktioniert es einwandfrei und würde die Sache nur komplizieren, wenn Sie separate Benutzerrollendokumente erstellen.
Diederikh
1
Dies funktioniert für die meisten Systeme, da die Rollen normalerweise klein sind und wir normalerweise einen Benutzer nehmen und dann seine Rollen betrachten. Aber was ist, wenn die Rollen groß sind? oder was ist, wenn ich Sie bitte, mir eine Liste der Benutzer mit der Rolle == "Ingenieur" zu geben? Jetzt müssten Sie Ihre gesamte Benutzersammlung abfragen (alle Benutzer besuchen, die keine Rolle als Ingenieur haben), um nur zwei oder drei Benutzer zu erhalten, die diese Rolle beispielsweise unter Millionen solcher Benutzer haben können. Eine separate Tabelle oder Sammlung ist viel besser.
Programmierer
31

Anstatt zu versuchen, nach unserer jahrelangen Erfahrung mit RDBMS zu modellieren, habe ich es viel einfacher gefunden, Dokument-Repository-Lösungen mit MongoDB, Redis und anderen NoSQL-Datenspeichern zu modellieren, indem ich sie für die Lese-Anwendungsfälle optimiere und dabei die atomaren Aspekte berücksichtige Schreibvorgänge, die von den Schreibanwendungsfällen unterstützt werden müssen.

Beispielsweise folgen die Verwendungen einer Domäne "Benutzer in Rollen":

  1. Rolle - Erstellen, Lesen, Aktualisieren, Löschen, Auflisten von Benutzern, Hinzufügen von Benutzern, Entfernen von Benutzern, Löschen aller Benutzer, Benutzerindex oder Ähnliches zur Unterstützung von "Ist Benutzer in Rolle" (Vorgänge wie ein Container + eigene Metadaten).
  2. Benutzer - Erstellen, Lesen, Aktualisieren, Löschen (CRUD-Operationen wie eine freistehende Entität)

Dies kann als die folgenden Dokumentvorlagen modelliert werden:

User: { _id: UniqueId, name: string, roles: string[] }
    Indexes: unique: [ name ]
Role: { _id: UniqueId, name: string, users: string[] }
    Indexes: unique: [ name ]

Um die Hochfrequenzverwendungen zu unterstützen, z. B. rollenbezogene Funktionen der Benutzerentität, wird User.Roles absichtlich denormalisiert, auf dem Benutzer gespeichert und Role.Users mit doppeltem Speicher.

Wenn dies im Text nicht ohne weiteres ersichtlich ist, ist dies jedoch die Art des Denkens, die bei der Verwendung von Dokumentrepositorys empfohlen wird.

Ich hoffe, dass dies dazu beiträgt, die Lücke in Bezug auf die Leseseite der Operationen zu schließen.

Für die Schreibseite wird empfohlen, nach atomaren Schreibvorgängen zu modellieren. Wenn für die Dokumentstrukturen beispielsweise eine Sperre erworben, ein Dokument, ein anderes und möglicherweise mehrere Dokumente aktualisiert und die Sperre aufgehoben werden muss, ist das Modell wahrscheinlich fehlgeschlagen. Nur weil wir verteilte Sperren erstellen können, heißt das nicht, dass wir sie verwenden sollen.

Für den Fall des Benutzers in Rollen-Modells fügen die Operationen, die unsere atomare Schreibvermeidung von Sperren erweitern, einen Benutzer zu einer Rolle hinzu oder entfernen ihn. In beiden Fällen führt ein erfolgreicher Vorgang dazu, dass sowohl ein einzelner Benutzer als auch ein einzelnes Rollendokument aktualisiert werden. Wenn etwas fehlschlägt, ist es einfach, eine Bereinigung durchzuführen. Dies ist der einzige Grund, warum das Muster "Arbeitseinheit" häufig verwendet wird, wenn Dokumentrepositorys verwendet werden.

Die Operation, die unsere atomare Schreibvermeidung von Sperren wirklich erweitert, löscht eine Rolle, was zu vielen Benutzeraktualisierungen führen würde, um den Role.name aus dem User.roles-Array zu entfernen. Diese Operation von clear wird dann im Allgemeinen nicht empfohlen, kann jedoch bei Bedarf durch Anordnen der Operationen implementiert werden:

  1. Rufen Sie die Liste der Benutzernamen von Role.users ab.
  2. Iterieren Sie die Benutzernamen aus Schritt 1 und entfernen Sie den Rollennamen aus User.roles.
  3. Löschen Sie die Role.users.

Im Fall eines Problems, das am wahrscheinlichsten in Schritt 2 auftritt, ist ein Rollback einfach, da derselbe Satz von Benutzernamen aus Schritt 1 zum Wiederherstellen oder Fortfahren verwendet werden kann.

paegun
quelle
15

Ich bin gerade auf diese Frage gestoßen, und obwohl es eine alte ist, dachte ich, es wäre nützlich, ein paar Möglichkeiten hinzuzufügen, die in den gegebenen Antworten nicht erwähnt werden. Außerdem haben sich die Dinge in den letzten Jahren etwas weiterentwickelt, sodass hervorzuheben ist, dass SQL und NoSQL näher zusammenrücken.

Einer der Kommentatoren brachte die kluge warnende Haltung vor, dass „wenn Daten relational sind, verwenden Sie relational“. Dieser Kommentar ist jedoch nur in der relationalen Welt sinnvoll, in der Schemata immer vor der Anwendung stehen.

RELATIONAL WORLD: Strukturdaten> Anwendung schreiben, um sie zu erhalten
NOSQL WORLD: Designanwendung > Strukturdaten entsprechend

Auch wenn Daten relational sind, ist NoSQL immer noch eine Option. Zum Beispiel sind Eins-zu-Viele-Beziehungen überhaupt kein Problem und werden in MongoDB-Dokumenten ausführlich behandelt

Eine 2015 Lösung für ein 2010 Problem

Seit diese Frage gestellt wurde, gab es ernsthafte Versuche, noSQL näher an SQL heranzuführen. Das Team um Yannis Papakonstantinou von der University of California (San Diego) hat an FORWARD gearbeitet , einer Implementierung von SQL ++, die bald die Lösung für anhaltende Probleme wie das hier veröffentlichte sein könnte.

Auf einer praktischeren Ebene hat die Veröffentlichung von Couchbase 4.0 dazu geführt, dass Sie zum ersten Mal native JOINs in NoSQL ausführen können. Sie verwenden ihre eigene N1QL. Dies ist ein Beispiel JOINaus ihren Tutorials :

SELECT usr.personal_details, orders 
        FROM users_with_orders usr 
            USE KEYS "Elinor_33313792" 
                JOIN orders_with_users orders 
                    ON KEYS ARRAY s.order_id FOR s IN usr.shipped_order_history END

N1QL ermöglicht die meisten, wenn nicht alle SQL-Operationen, einschließlich Aggregration, Filterung usw.

DIE NICHT SO NEUE HYBRID-LÖSUNG

Wenn MongoDB immer noch die einzige Option ist, möchte ich auf meinen Punkt zurückkommen, dass die Anwendung Vorrang vor der Datenstruktur haben sollte. Keine der Antworten erwähnt die hybride Einbettung, wobei die meisten abgefragten Daten in das Dokument / Objekt eingebettet werden und Referenzen für eine Minderheit von Fällen aufbewahrt werden.

Beispiel: Können Informationen (außer Rollennamen) warten? Könnte das Bootstrapping der Anwendung schneller sein, wenn nichts angefordert wird, was der Benutzer noch nicht benötigt?

Dies kann der Fall sein, wenn sich der Benutzer anmeldet und alle Optionen für alle Rollen anzeigen muss, zu denen er gehört. Der Benutzer ist jedoch ein "Ingenieur", und Optionen für diese Rolle werden selten verwendet. Dies bedeutet, dass die Anwendung nur die Optionen für einen Ingenieur anzeigen muss, wenn er darauf klicken möchte.

Dies kann mit einem Dokument erreicht werden, das der Anwendung zu Beginn mitteilt (1), zu welchen Rollen der Benutzer gehört und (2) wo Informationen zu einem Ereignis abgerufen werden können, das mit einer bestimmten Rolle verknüpft ist.

   {_id: ObjectID(),
    roles: [[“Engineer”, ObjectId()”],
            [“Administrator”, ObjectId()”]]
   }

Oder, noch besser, indizieren Sie das Feld role.name in der Rollensammlung, und Sie müssen möglicherweise auch ObjectID () nicht einbetten.

Ein weiteres Beispiel: Werden ständig Informationen zu ALLEN Rollen angefordert?

Es kann auch vorkommen, dass sich der Benutzer beim Dashboard anmeldet und in 90% der Fälle Aufgaben ausführt, die mit der Rolle "Ingenieur" verknüpft sind. Die hybride Einbettung könnte für diese bestimmte Rolle vollständig durchgeführt werden und Referenzen nur für den Rest behalten.

{_id: ObjectID(),
  roles: [{name: Engineer”, 
           property1: value1,
           property2: value2
          },   
          [“Administrator”, ObjectId()”]
         ]
}

Schemalos zu sein ist nicht nur ein Merkmal von NoSQL, es könnte in diesem Fall von Vorteil sein. Es ist absolut gültig, verschiedene Objekttypen in der Eigenschaft "Rollen" eines Benutzerobjekts zu verschachteln.

Cortopy
quelle
5

Wenn Mitarbeiter und Unternehmen ein Entitätsobjekt sind, versuchen Sie, das folgende Schema zu verwenden:

employee{
   //put your contract to employee
   contracts:{ item1, item2, item3,...}
}

company{
   //and duplicate it in company
   contracts:{ item1, item2, item3,...}
}
Andrei Andrushkevich
quelle