Wie man ein Dokumentschema in MongoDB entwerft

Der Autor wählte den Open Internet/Free Speech Fund aus, um eine Spende im Rahmen des Write for DOnations-Programms zu erhalten.

Einführung

Wenn du viel Erfahrung mit relationalen Datenbanken hast, kann es schwierig sein, die Prinzipien des relationalen Modells hinter dir zu lassen, wie das Denken in Tabellen und Beziehungen. Dokumentenorientierte Datenbanken wie MongoDB ermöglichen es, die Starrheit und Einschränkungen des relationalen Modells zu überwinden. Allerdings kann die Flexibilität und Freiheit, die mit der Speicherung von selbstbeschreibenden Dokumenten in der Datenbank einhergeht, zu anderen Fallstricken und Schwierigkeiten führen.

Dieser konzeptionelle Artikel skizziert fünf allgemeine Richtlinien im Zusammenhang mit dem Schemadesign in einer dokumentenorientierten Datenbank und hebt verschiedene Überlegungen hervor, die man bei der Modellierung von Beziehungen zwischen Daten berücksichtigen sollte. Er wird auch mehrere Strategien durchgehen, die man anwenden kann, um solche Beziehungen zu modellieren, einschließlich des Einbettens von Dokumenten in Arrays und der Verwendung von Kind- und Elternreferenzen, sowie wann diese Strategien am besten einzusetzen sind.

Richtlinie 1 — Zusammenlagern, was gemeinsam abgerufen werden muss

In einer typischen relationalen Datenbank werden Daten in Tabellen gespeichert, und jede Tabelle ist mit einer festen Liste von Spalten konstruiert, die verschiedene Attribute darstellen, die eine Entität, ein Objekt oder ein Ereignis ausmachen. Zum Beispiel könnten in einer Tabelle, die Studenten einer Universität repräsentiert, Spalten enthalten sein, die den Vornamen, den Nachnamen, das Geburtsdatum und eine eindeutige Identifikationsnummer jedes Studenten enthalten.

Typischerweise repräsentiert jede Tabelle ein einziges Thema. Wenn man Informationen über die aktuellen Studien, Stipendien oder die vorherige Bildung eines Studenten speichern möchte, könnte es sinnvoll sein, diese Daten in einer separaten Tabelle von derjenigen zu halten, die ihre persönlichen Informationen enthält. Man könnte diese Tabellen dann verbinden, um zu zeigen, dass es eine Beziehung zwischen den Daten in jeder Tabelle gibt, was darauf hinweist, dass die enthaltenen Informationen eine sinnvolle Verbindung haben.

Zum Beispiel könnte eine Tabelle, die den Stipendienstatus jedes Studenten beschreibt, auf die Studenten über ihre Matrikelnummer verweisen, aber den Namen oder die Adresse des Studenten nicht direkt speichern, um Datenduplizierung zu vermeiden. In einem solchen Fall müsste eine Abfrage, um Informationen über einen Studenten einschließlich aller Daten zu den sozialen Medienkonten, der vorherigen Bildung und den Stipendien zu erhalten, mehr als eine Tabelle gleichzeitig abrufen und dann die Ergebnisse aus verschiedenen Tabellen zusammenführen.

Diese Methode, Beziehungen durch Verweise zu beschreiben, wird als normalisiertes Datenmodell bezeichnet. Das Speichern von Daten auf diese Weise – mit mehreren getrennten, präzisen Objekten, die sich gegenseitig beziehen – ist auch in dokumentenorientierten Datenbanken möglich. Die Flexibilität des Dokumentenmodells und die Freiheit, die es bietet, um eingebettete Dokumente und Arrays innerhalb eines einzelnen Dokuments zu speichern, bedeutet jedoch, dass Sie Daten anders modellieren können, als dies in einer relationalen Datenbank der Fall wäre.

Das grundlegende Konzept für die Modellierung von Daten in einer dokumentenorientierten Datenbank ist, „zusammen zu speichern, was zusammen abgerufen wird“.“ Wenn wir weiter in das Beispiel des Studenten eintauchen, nehmen wir an, dass die meisten Studenten an dieser Schule mehr als eine E-Mail-Adresse haben. Aufgrund dessen möchte die Universität die Möglichkeit haben, mehrere E-Mail-Adressen mit den Kontaktdaten jedes Studenten zu speichern.

In einem solchen Fall könnte ein Beispieldokument eine Struktur wie die folgende haben:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ]
}

Beachten Sie, dass dieses Beispieldokument eine eingebettete Liste von E-Mail-Adressen enthält.

Die Darstellung mehr als eines einzelnen Themas innerhalb eines einzelnen Dokuments charakterisiert ein denormalisiertes Datenmodell. Es ermöglicht Anwendungen, alle relevanten Daten für ein bestimmtes Objekt (hier, einen Schüler) auf einmal abzurufen und zu manipulieren, ohne auf mehrere separate Objekte und Sammlungen zugreifen zu müssen. Dies gewährleistet auch die Atomarität von Operationen auf einem solchen Dokument, ohne Transaktionen über mehrere Dokumente einsetzen zu müssen, um die Integrität zu gewährleisten.

Das Zusammenlagern von Daten, die zusammen abgerufen werden müssen, mithilfe von eingebetteten Dokumenten ist oft der optimale Weg, um Daten in einer dokumentenorientierten Datenbank darzustellen. In den folgenden Richtlinien erfahren Sie, wie verschiedene Beziehungen zwischen Objekten, wie z.B. Eins-zu-Eins- oder Eins-zu-Viele-Beziehungen, am besten in einer dokumentenorientierten Datenbank modelliert werden können.

Richtlinie 2 — Modellierung von Eins-zu-Eins-Beziehungen mit eingebetteten Dokumenten

Eine Eins-zu-Eins-Beziehung stellt eine Verbindung zwischen zwei unterschiedlichen Objekten dar, bei der ein Objekt genau einem anderen Objekt desselben Typs zugeordnet ist.

Fortsetzung des Schülerbeispiels aus dem vorherigen Abschnitt: Jeder Schüler hat zu jedem Zeitpunkt nur eine gültige Schülerausweiskarte. Eine Karte gehört nie mehreren Schülern, und kein Schüler kann mehrere Ausweiskarten haben. Wenn man all diese Daten in einer relationalen Datenbank speichern wollte, würde es wahrscheinlich Sinn machen, die Beziehung zwischen Schülern und ihren Ausweiskarten zu modellieren, indem man die Schülerdatensätze und die Ausweiskartendatensätze in getrennten Tabellen speichert, die durch Referenzen miteinander verbunden sind.

Eine häufige Methode zur Darstellung solcher Beziehungen in einer Dokumentendatenbank ist die Verwendung von eingebetteten Dokumenten. Als Beispiel beschreibt das folgende Dokument einen Schüler namens Sammy und seine Schülerausweiskarte:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "id_card": {
        "number": "123-1234-123",
        "issued_on": ISODate("2020-01-23"),
        "expires_on": ISODate("2020-01-23")
    }
}

Beachten Sie, dass anstelle eines einzelnen Werts das Feld id_card dieses Beispiel-Dokuments ein eingebettetes Dokument enthält, das die Schülerausweiskarte beschreibt, angegeben durch eine ID-Nummer, das Ausgabedatum der Karte und das Ablaufdatum der Karte. Der Ausweis wird im Wesentlichen zu einem Teil des Dokuments, das den Schüler Sammy beschreibt, obwohl er in der Realität ein separates Objekt ist. Normalerweise ist es eine gute Wahl, das Dokumentschema so zu strukturieren, dass man alle zugehörigen Informationen mit einer einzigen Abfrage abrufen kann.

Die Dinge werden weniger einfach, wenn man Beziehungen trifft, die ein Objekt eines Typs mit vielen Objekten eines anderen Typs verbinden, wie zum Beispiel die E-Mail-Adressen eines Schülers, die Kurse, die er besucht, oder die Nachrichten, die er auf dem Schülerratenschwarmschild postet. In den nächsten Richtlinien werden Sie diese Datenbeispiele verwenden, um verschiedene Ansätze für die Arbeit mit Eins-zu-Viele- und Viele-zu-Viele-Beziehungen zu lernen.

Richtlinie 3 — Modellierung von Eins-zu-Wenigen-Beziehungen mit eingebetteten Dokumenten

Wenn ein Objekt eines Typs mit mehreren Objekten eines anderen Typs in Beziehung steht, kann dies als eine Eins-zu-Viele-Beziehung beschrieben werden. Ein Student kann mehrere E-Mail-Adressen haben, ein Auto kann zahlreiche Teile besitzen oder ein Einkaufsauftrag kann aus mehreren Artikeln bestehen. Jedes dieser Beispiele repräsentiert eine Eins-zu-Viele-Beziehung.

Während die üblichste Methode, eine Eins-zu-Eins-Beziehung in einer Dokumentendatenbank darzustellen, über ein eingebettetes Dokument erfolgt, gibt es mehrere Möglichkeiten, Eins-zu-Viele-Beziehungen in einem Dokumentschema zu modellieren. Bei der Auswahl der besten Modellierung sollten jedoch drei Eigenschaften der gegebenen Beziehung berücksichtigt werden:

  • Kardinalität: Kardinalität ist ein Maß für die Anzahl der einzelnen Elemente in einer gegebenen Menge. Wenn eine Klasse zum Beispiel 30 Studenten hat, könnte man sagen, dass die Klasse eine Kardinalität von 30 hat. In einer Eins-zu-Viele-Beziehung kann die Kardinalität in jedem Fall unterschiedlich sein. Ein Student könnte eine oder mehrere E-Mail-Adressen haben. Er könnte für nur wenige Kurse registriert sein oder einen vollständig gefüllten Stundenplan haben. In einer Eins-zu-Viele-Beziehung wird die Größe von „Viele“ beeinflussen, wie Sie die Daten modellieren könnten.
  • Unabhängiger Zugriff: Einige zugehörige Daten werden selten, wenn überhaupt, unabhängig vom Hauptobjekt abgerufen. Beispielsweise könnte es ungewöhnlich sein, die E-Mail-Adresse eines einzelnen Studenten ohne weitere Studentendetails abzurufen. Andererseits könnten die Kurse einer Universität unabhängig von den Studenten, die sich dafür eingetragen haben, einzeln abgerufen und aktualisiert werden müssen. Ob Sie jemals ein verwandtes Dokument alleine abrufen werden, beeinflusst auch, wie Sie die Daten modellieren könnten.
  • Ob die Beziehung zwischen Daten streng eine Eins-zu-Viele-Beziehung ist: Betrachten Sie die Kurse, die ein Beispielstudent an einer Universität besucht. Aus Sicht des Studenten kann er an mehreren Kursen teilnehmen. Auf den ersten Blick mag dies wie eine Eins-zu-Viele-Beziehung erscheinen. Universitätskurse werden jedoch selten von einem einzelnen Studenten besucht; meist besuchen mehrere Studenten denselben Kurs. In solchen Fällen ist die fragliche Beziehung nicht wirklich eine Eins-zu-Viele-Beziehung, sondern eine Viele-zu-Viele-Beziehung, und daher würden Sie einen anderen Ansatz wählen, um diese Beziehung zu modellieren, als eine Eins-zu-Viele-Beziehung.

Stellen Sie sich vor, Sie entscheiden, wie Sie die E-Mail-Adressen von Studenten speichern sollen. Jeder Student kann mehrere E-Mail-Adressen haben, z. B. eine für die Arbeit, eine für den persönlichen Gebrauch und eine, die von der Universität bereitgestellt wird. Ein Dokument, das eine einzelne E-Mail-Adresse repräsentiert, könnte folgendermaßen aussehen:

{
    "email": "[email protected]",
    "type": "work"
}

In Bezug auf die Größe gibt es nur wenige E-Mail-Adressen pro Student, da es unwahrscheinlich ist, dass ein Student hunderte – letztens tausende – E-Mail-Adressen hat. Daher kann diese Beziehung als eine eine-zu-wenigen Beziehung charakterisiert werden, was eine gute rationale ist, die E-Mail-Adressen direkt ins Dokument des Studienents zu integrieren und sie zusammen zu speichern. Es ist kein Risiko, dass die Liste der E-Mail-Adressen unbegrenzt wachsen wird, was das Dokument groß und ineffizient machen würde.

Hinweis: Beachten Sie, dass es einige Hindernisse mit sich bringt, wenn Sie Daten in Arrays speichern. So darfst du ein einziges MongoDB-Dokument nicht größer als 16 MB sein. Während es möglich und üblich ist, mehr als einen Objekt zu einem Feld einzubetteln, können lange Listen von Objekten schnell diese Größenlimit erreichen. Zudem haben Anfragen zur Performance einen großen Einfluss, wenn Sie viele Dokumente innerhalb eines Array-Felds speichern.

Embedding mehrere Dokumente in einem Arrayfeld ist in vielen Situationen möglich und normal, aber es ist auch nicht immer die beste Lösung.

Bezüglich der unabhängigen Zugriffe werden E-Mail-Adressen wahrscheinlich nicht separat vom Student ausgelesen. Daher ist es kein offensichtliches Motiv, sie als separate Dokumente in einer separaten Sammlung zu speichern. Dies ist eine weitere gute rationale, sie direkt ins Dokument des Studienents zu integrieren.

Das letzte zu berücksichtigende Element ist, ob diese Beziehung wirklich eine Eins-zu-Viele-Beziehung anstelle einer Viele-zu-Viele-Beziehung ist. Da eine E-Mail-Adresse zu einer einzelnen Person gehört, ist es sinnvoll, diese Beziehung als Eins-zu-Viele-Beziehung (oder, möglicherweise genauer, eine Eins-zu-Wenige-Beziehung) anstelle einer Viele-zu-Viele-Beziehung zu beschreiben.

Diese drei Annahmen deuten darauf hin, dass das Einbetten der verschiedenen E-Mail-Adressen der Studenten in denselben Dokumenten, die die Studenten selbst beschreiben, eine gute Wahl für die Speicherung dieser Art von Daten wäre. Ein Beispieldokument eines Studenten mit eingebetteten E-Mail-Adressen könnte so aussehen:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ]
}

Mit dieser Struktur rufen Sie jedes Mal, wenn Sie ein Studentendokument abrufen, auch die eingebetteten E-Mail-Adressen in derselben Leseoperation ab.

Wenn Sie eine Eins-zu-Wenige-Beziehung modellieren, bei der die verwandten Dokumente nicht unabhängig zugegriffen werden müssen, ist das direkte Einbetten von Dokumenten in der Regel wünschenswert, da dies die Komplexität des Schemas reduzieren kann.

Wie zuvor erwähnt, ist das Einbetten von Dokumenten jedoch nicht immer die optimale Lösung. Im nächsten Abschnitt werden weitere Details dazu geliefert, warum dies in einigen Szenarien der Fall sein könnte, und es wird skizziert, wie untergeordnete Referenzen als alternative Methode zur Darstellung von Beziehungen in einer Dokumentendatenbank verwendet werden können.

Richtlinie 4 — Modellierung von Eins-zu-Viele- und Viele-zu-Viele-Beziehungen mit Kindreferenzen

Die Art der Beziehung zwischen Studenten und ihren E-Mail-Adressen bestimmt, wie diese Beziehung am besten in einer Dokumentendatenbank modelliert werden kann. Es gibt einige Unterschiede zwischen dieser und der Beziehung zwischen Studenten und den Kursen, die sie besuchen, daher wird sich auch die Modellierung der Beziehungen zwischen Studenten und ihren Kursen unterscheiden.

Ein Dokument, das einen einzelnen Kurs beschreibt, den ein Student besucht, könnte eine Struktur wie diese haben:

{
    "name": "Physics 101",
    "department": "Department of Physics",
    "points": 7
}

Angenommen, Sie haben sich entschieden, eingebettete Dokumente zu verwenden, um Informationen über die Kurse jedes Studenten zu speichern, wie in diesem Beispiel:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ],
    "courses": [
        {
            "name": "Physics 101",
            "department": "Department of Physics",
            "points": 7
        },
        {
            "name": "Introduction to Cloud Computing",
            "department": "Department of Computer Science",
            "points": 4
        }
    ]
}

Dies wäre ein vollkommen gültiges MongoDB-Dokument und könnte den Zweck erfüllen, aber berücksichtigen Sie die drei Beziehungseigenschaften, die Sie im vorherigen Leitfaden gelernt haben.

Die erste ist die Kardinalität. Ein Student wird wahrscheinlich nur einige E-Mail-Adressen pflegen, aber er kann mehrere Kurse während seines Studiums besuchen. Nach mehreren Jahren des Besuchs könnten es Dutzende von Kursen sein, an denen der Student teilnahm. Darüber hinaus würden sie diese Kurse zusammen mit vielen anderen Studenten besuchen, die ebenfalls ihre eigenen Kursreihen während ihrer Studienjahre besuchen.

Wenn du dich entschieden hast, jeder Kurs wie im vorhergehenden Beispiel einzubinden, würde das Dokument des Studenten schnell unhandlich werden. Bei einer höheren Kardinalität ist der eingebettete Dokumentenansatz weniger verlockend.

Die zweite Überlegung betrifft den unabhängigen Zugriff. Im Gegensatz zu E-Mail-Adressen kann man davon ausgehen, dass es Fälle gibt, in denen Informationen über Universitätskurse eigenständig abgerufen werden müssten. Zum Beispiel müsste man Informationen über verfügbare Kurse zusammentragen, um eine Marketingbroschüre vorzubereiten. Darüber hinaus werden Kurse wahrscheinlich mit der Zeit aktualisiert werden müssen: Der lehrende Professor könnte sich ändern, ihr Zeitplan könnte Schwankungen aufweisen oder ihre Voraussetzungen müssten aktualisiert werden.

Wenn du die Kurse als Dokumente eingebettet innerhalb der Studentendokumente speicherst, würde es problematisch werden, die Liste aller von der Universität angebotenen Kurse abzurufen. Außerdem müsste man bei jeder Aktualisierung eines Kurses durchgehen und die Kursinformation überall aktualisieren. Beide sind gute Gründe, um Kurse separat zu speichern und sie nicht vollständig einzubinden.

Die dritte Überlegung betrifft, ob die Beziehung zwischen Student und Universitätskurs tatsächlich eins-zu-mehreren oder stattdessen vielen-zu-mehreren ist. In diesem Fall ist es letzteres, da mehr als ein Student an jedem Kurs teilnimmt. Die Kardinalität dieser Beziehung und die unabhängige Zugriffseigenschaften sprechen gegen das Einbetten jedes Kursdokuments, vor allem aus praktischen Gründen wie der einfacheren Zugriff und Aktualisierung. Angesichts der vielen-zu-mehreren Natur der Beziehung zwischen Kursen und Studenten könnte es sinnvoll sein, Kursdokumente in einer separaten Sammlung mit eigenen eindeutigen Kennzeichnern aufzubewahren.

Die Dokumente, die Klassen in dieser separaten Sammlung repräsentieren, könnten eine Struktur haben, wie in diesen Beispielen:

{
    "_id": ObjectId("61741c9cbc9ec583c836170a"),
    "name": "Physics 101",
    "department": "Department of Physics",
    "points": 7
},
{
    "_id": ObjectId("61741c9cbc9ec583c836170b"),
    "name": "Introduction to Cloud Computing",
    "department": "Department of Computer Science",
    "points": 4
}

Wenn Sie sich entscheiden, Kursinformationen auf diese Weise zu speichern, müssen Sie einen Weg finden, um Schüler mit diesen Kursen zu verbinden, damit Sie wissen, welche Schüler welchen Kursen folgen. In Fällen wie diesem, wo die Anzahl der verwandten Objekte nicht übermäßig groß ist, insbesondere bei Viele-zu-Viele-Beziehungen, besteht ein üblicher Weg, dies zu tun, darin, Kind-Referenzen zu verwenden.

Mit Kind-Referenzen referenziert ein Schülerdokument die Objekt-IDs der Kurse, die der Schüler besucht, in einem eingebetteten Array, wie in diesem Beispiel:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ],
    "courses": [
        ObjectId("61741c9cbc9ec583c836170a"),
        ObjectId("61741c9cbc9ec583c836170b")
    ]
}

Beachten Sie, dass dieses Beispiel-Dokument immer noch ein Feld courses hat, das ebenfalls ein Array ist, aber anstatt vollständige Kursdokumente wie im früheren Beispiel einzubetten, sind nur die IDs eingebettet, die die Kursdokumente in der separaten Sammlung referenzieren. Jetzt, wenn ein Schülerdokument abgerufen wird, sind die Kurse nicht sofort verfügbar und müssen separat abgefragt werden. Andererseits ist sofort bekannt, welche Kurse abgerufen werden müssen. Auch wenn die Details eines Kurses aktualisiert werden müssen, muss nur das Kursdokument selbst geändert werden. Alle Referenzen zwischen Schülern und ihren Kursen bleiben gültig.

Hinweis: Es gibt keine feste Regel dafür, wann die Kardinalität einer Beziehung zu groß ist, um Kind-Referenzen auf diese Weise einzubetten. Sie könnten einen anderen Ansatz bei entweder niedrigerer oder höherer Kardinalität wählen, wenn dies am besten zu der fraglichen Anwendung passt. Schließlich möchten Sie immer Ihre Daten so strukturieren, dass sie der Art und Weise entsprechen, wie Ihre Anwendung sie abfragt und aktualisiert.

Wenn Sie eine Eins-zu-viele-Beziehung modellieren, bei der die Anzahl der zugehörigen Dokumente in vernünftigen Grenzen liegt und diese Dokumente unabhängig abgerufen werden müssen, bevorzugen Sie die getrennte Speicherung der zugehörigen Dokumente und die Einbettung von Kindreferenzen, um eine Verbindung zu ihnen herzustellen.

Nachdem Sie gelernt haben, wie man Kindreferenzen verwendet, um Beziehungen zwischen verschiedenen Datentypen anzuzeigen, wird diese Anleitung ein umgekehrtes Konzept erläutern: Elternreferenzen.

Richtlinie 5 — Modellierung unbegrenzter Eins-zu-viele-Beziehungen mit Elternreferenzen

Die Verwendung von Kindreferenzen funktioniert gut, wenn es zu viele zugehörige Objekte sind, um sie direkt in das übergeordnete Dokument einzubetten, aber die Anzahl liegt immer noch in bekannten Grenzen. Es gibt jedoch Fälle, in denen die Anzahl der zugehörigen Dokumente unbegrenzt sein könnte und sich im Laufe der Zeit weiter vergrößern wird.

Als Beispiel stellen Sie sich vor, dass der Studentenrat der Universität ein Nachrichtenbrett hat, auf dem jeder Student beliebige Nachrichten posten kann, einschließlich Fragen zu Kursen, Reisegeschichten, Stellenangebote, Lernmaterialien oder einfach nur eine freie Diskussion. Eine Beispielnachricht in diesem Beispiel besteht aus einem Betreff und einem Nachrichtentext:

{
    "_id": ObjectId("61741c9cbc9ec583c836174c"),
    "subject": "Books on kinematics and dynamics",
    "message": "Hello! Could you recommend good introductory books covering the topics of kinematics and dynamics? Thanks!",
    "posted_on": ISODate("2021-07-23T16:03:21Z")
}

Sie könnten entweder den zuvor diskutierten Ansätzen der Einbettung oder der Kindreferenzen verwenden, um diese Beziehung zu modellieren. Wenn Sie sich für die Einbettung entscheiden würden, könnte das Dokument des Studenten ungefähr so aussehen:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ],
    "courses": [
        ObjectId("61741c9cbc9ec583c836170a"),
        ObjectId("61741c9cbc9ec583c836170b")
    ],
    "message_board_messages": [
        {
            "subject": "Books on kinematics and dynamics",
            "message": "Hello! Could you recommend good introductory books covering the topics of kinematics and dynamics? Thanks!",
            "posted_on": ISODate("2021-07-23T16:03:21Z")
        },
        . . .
    ]
}

Jedoch kann ein Dokument eines Studenten schnell sehr lang werden und leicht die 16MB Grenze überschreiten, wenn dieser viele Nachrichten schreibt. Daher deutet die Kardinalität dieser Beziehung eher gegen das Einbetten. Zudem könnten die Nachrichten unabhängig vom Studenten abgerufen werden müssen, was z.B. der Fall sein könnte, wenn die Nachrichten-Board-Seite die neuesten von Studenten geposteten Nachrichten anzeigen soll. Dies lässt ebenfalls darauf schließen, dass Einbetten in diesem Szenario nicht die beste Wahl ist.

Hinweis: Es sollte auch berücksichtigt werden, ob die Nachrichten des Nachrichten-Boards häufig abgerufen werden, wenn das Dokument des Studenten abgerufen wird. Wenn nicht, würde das Einbetten all dieser Nachrichten in das Dokument eine Leistungseinbuße beim Abrufen und Bearbeiten dieses Dokuments verursachen, selbst wenn die Nachrichtenliste nicht oft benötigt wird. Der seltener Zugriff auf verwandte Daten ist oft ein weiterer Hinweis darauf, dass Dokumente nicht eingebettet werden sollten.

Betrachten Sie nun stattdessen die Verwendung von Kind-Referenzen anstelle des Einbettens ganzer Dokumente wie im vorherigen Beispiel. Die einzelnen Nachrichten würden in einer separaten Sammlung gespeichert und das Dokument des Studenten könnte dann folgende Struktur haben:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ],
    "courses": [
        ObjectId("61741c9cbc9ec583c836170a"),
        ObjectId("61741c9cbc9ec583c836170b")
    ],
    "message_board_messages": [
        ObjectId("61741c9cbc9ec583c836174c"),
        . . .
    ]
}

In diesem Beispiel speichert das Feld message_board_messages jetzt die Kindreferenzen auf alle von Sammy geschriebenen Nachrichten. Das Ändern des Ansatzes löst jedoch nur eines der zuvor genannten Probleme, da es nun möglich wäre, auf die Nachrichten unabhängig zuzugreifen. Obwohl die Dokumentgröße des Studenten langsamer mit dem Ansatz der Kindreferenzen wachsen würde, könnte die Sammlung von Objektidentifikatoren auch unhandlich werden, angesichts der unbegrenzten Kardinalität dieser Beziehung. Schließlich könnte ein Student leicht Tausende von Nachrichten während ihrer vierjährigen Studienzeit schreiben.

In solchen Szenarien ist eine gängige Methode, ein Objekt mit einem anderen zu verbinden, durch Elternreferenzen. Im Gegensatz zu den zuvor beschriebenen Kindreferenzen verweist jetzt nicht mehr das Studentendokument auf einzelne Nachrichten, sondern eine Referenz im Nachrichtendokument zeigt auf den Studenten, der sie geschrieben hat.

Um Elternreferenzen zu verwenden, müssten Sie das Nachrichtendokumentenschema so ändern, dass es eine Referenz auf den Studenten enthält, der die Nachricht verfasst hat:

{
    "_id": ObjectId("61741c9cbc9ec583c836174c"),
    "subject": "Books on kinematics and dynamics",
    "message": "Hello! Could you recommend a good introductory books covering the topics of kinematics and dynamics? Thanks!",
    "posted_on": ISODate("2021-07-23T16:03:21Z"),
    "posted_by": ObjectId("612d1e835ebee16872a109a4")
}

Beachten Sie, dass das neue Feld posted_by die Objektidentifikatoren des Studentendokuments enthält. Jetzt enthält das Studentendokument keine Informationen über die von ihm geposteten Nachrichten mehr:

{
    "_id": ObjectId("612d1e835ebee16872a109a4"),
    "first_name": "Sammy",
    "last_name": "Shark",
    "emails": [
        {
            "email": "[email protected]",
            "type": "work"
        },
        {
            "email": "[email protected]",
            "type": "home"
        }
    ],
    "courses": [
        ObjectId("61741c9cbc9ec583c836170a"),
        ObjectId("61741c9cbc9ec583c836170b")
    ]
}

Um die Liste der von einem Studenten geschriebenen Nachrichten abzurufen, würden Sie eine Abfrage auf der Nachrichtensammlung durchführen und gegen das Feld posted_by filtern. Das Vorhalten in einer separaten Sammlung sorgt dafür, dass die Liste der Nachrichten sicher wachsen kann, ohne die Dokumente des Studenten zu beeinflussen.

Hinweis: Bei der Verwendung von Elternreferenzen kann die Erstellung eines Index auf dem Feld, das auf das Elterndokument verweist, die Abfrageleistung bei jeder Filterung gegen die Elterndokument-Kennung erheblich steigern.

Wenn Sie eine Eins-zu-Viele-Beziehung modellieren, bei der die Anzahl der zugehörigen Dokumente unbegrenzt ist, unabhängig davon, ob die Dokumente unabhängig zugegriffen werden müssen, wird im Allgemeinen empfohlen, die zugehörigen Dokumente separat zu speichern und mithilfe von Elternreferenzen mit dem Elterndokument zu verbinden.

Schlussfolgerung

Dank der Flexibilität von dokumentenorientierten Datenbanken ist die Bestimmung des besten Wegs, Beziehungen in einer Dokumentdatenbank zu modellieren, weniger eine strenge Wissenschaft als in einer relationalen Datenbank. Durch das Lesen dieses Artikels haben Sie sich mit dem Einbetten von Dokumenten und der Verwendung von Kind- und Elternreferenzen zur Speicherung zugehöriger Daten vertraut gemacht. Sie haben gelernt, die Kardinalität der Beziehung zu berücksichtigen und unbegrenzte Arrays zu vermeiden, sowie zu berücksichtigen, ob das Dokument separat oder häufig zugegriffen wird.

Dies sind nur einige Richtlinien, die Ihnen helfen können, typische Beziehungen in MongoDB zu modellieren, aber das Modellieren von Datenbankschemata ist kein „One Size Fits All“. Berücksichtigen Sie immer Ihre Anwendung und wie sie die Daten verwendet und aktualisiert, wenn Sie das Schema entwerfen.

Um mehr über das Schemadesign und gängige Muster zum Speichern verschiedener Datentypen in MongoDB zu erfahren, empfehlen wir Ihnen, die offizielle MongoDB-Dokumentation zu diesem Thema zu konsultieren.

Source:
https://www.digitalocean.com/community/tutorials/how-to-design-a-document-schema-in-mongodb