641 Stimmen

Wie führe ich den SQL-Join in MongoDB aus?

Wie führe ich den SQL-Join in MongoDB aus?

Nehmen wir an, Sie haben zwei Sammlungen (Benutzer und Kommentare), und ich möchte alle Kommentare mit pid=444 zusammen mit den Benutzerinformationen für jeden abrufen.

comments
  { uid:12345, pid:444, comment="blah" }
  { uid:12345, pid:888, comment="asdf" }
  { uid:99999, pid:444, comment="qwer" }

users
  { uid:12345, name:"john" }
  { uid:99999, name:"mia"  }

Gibt es eine Möglichkeit, alle Kommentare mit einem bestimmten Feld (z. B. ...find({pid:444}) ) und die mit jedem Kommentar verknüpften Benutzerinformationen in einem Rutsch zu finden?

Im Moment erhalte ich zuerst die Kommentare, die meinen Kriterien entsprechen, dann finde ich alle uid's in diesem Ergebnissatz heraus, erhalte die Benutzerobjekte und führe sie mit den Ergebnissen des Kommentars zusammen. Es scheint, dass ich das falsch mache.

67 Stimmen

Die letzte Antwort auf diese Frage ist wahrscheinlich die wichtigste, da MongoDB 3.2+ eine Join-Lösung namens $lookup implementiert hat. Ich dachte, ich würde sie hier einfügen, weil vielleicht nicht jeder bis zum Ende liest. stackoverflow.com/a/33511166/2593330

10 Stimmen

Richtig, $Nachschlag wurde in MongoDB 3.2 eingeführt. Einzelheiten finden Sie unter docs.mongodb.org/master/reference/operator/aggregation/lookup/

0 Stimmen

Konvertieren Sie eine beliebige Abfrage in Mongo und prüfen Sie die Antwort: stackoverflow.com/questions/68155715/

405voto

Clayton Gulick Punkte 8917

Ab Mongo 3.2 sind die Antworten auf diese Frage meist nicht mehr korrekt. Der neue $lookup-Operator, der der Aggregationspipeline hinzugefügt wurde, ist im Wesentlichen identisch mit einem Left Outer Join:

https://docs.mongodb.org/master/reference/operator/aggregation/lookup/#pipe._S_lookup

Aus den Unterlagen:

{
   $lookup:
     {
       from: <collection to join>,
       localField: <field from the input documents>,
       foreignField: <field from the documents of the "from" collection>,
       as: <output array field>
     }
}

Natürlich ist Mongo no eine relationale Datenbank, und die Entwickler sind vorsichtig, spezifische Anwendungsfälle für $lookup zu empfehlen, aber zumindest ab 3.2 ist Join jetzt mit MongoDB möglich.

1 Stimmen

@clayton : Wie wäre es mit mehr als zwei Kollektionen?

2 Stimmen

@DipenDedania fügen Sie einfach zusätzliche $lookup-Stufen zur Aggregationspipeline hinzu.

2 Stimmen

Ich kann kein Feld in Array in linke Sammlung mit seiner entsprechenden id in rechten collection.can jemand mir helfen verbinden?

167voto

Orlando Becerra Punkte 1677

Wir können alle Daten innerhalb einer einzigen Sammlung mit einer einfachen Funktion in wenigen Zeilen über die Mongodb-Client-Konsole zusammenführen, und jetzt könnten wir in der Lage sein, die gewünschte Abfrage durchzuführen. Nachfolgend ein vollständiges Beispiel,

Die Autoren:

db.authors.insert([
    {
        _id: 'a1',
        name: { first: 'orlando', last: 'becerra' },
        age: 27
    },
    {
        _id: 'a2',
        name: { first: 'mayra', last: 'sanchez' },
        age: 21
    }
]);

Kategorien:

db.categories.insert([
    {
        _id: 'c1',
        name: 'sci-fi'
    },
    {
        _id: 'c2',
        name: 'romance'
    }
]);

Bücher

db.books.insert([
    {
        _id: 'b1',
        name: 'Groovy Book',
        category: 'c1',
        authors: ['a1']
    },
    {
        _id: 'b2',
        name: 'Java Book',
        category: 'c2',
        authors: ['a1','a2']
    },
]);

Buchverleih

db.lendings.insert([
    {
        _id: 'l1',
        book: 'b1',
        date: new Date('01/01/11'),
        lendingBy: 'jose'
    },
    {
        _id: 'l2',
        book: 'b1',
        date: new Date('02/02/12'),
        lendingBy: 'maria'
    }
]);

Die Magie:

db.books.find().forEach(
    function (newBook) {
        newBook.category = db.categories.findOne( { "_id": newBook.category } );
        newBook.lendings = db.lendings.find( { "book": newBook._id  } ).toArray();
        newBook.authors = db.authors.find( { "_id": { $in: newBook.authors }  } ).toArray();
        db.booksReloaded.insert(newBook);
    }
);

Abrufen der neuen Erfassungsdaten:

db.booksReloaded.find().pretty()

.- Antwort :)

{
    "_id" : "b1",
    "name" : "Groovy Book",
    "category" : {
        "_id" : "c1",
        "name" : "sci-fi"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        }
    ],
    "lendings" : [
        {
            "_id" : "l1",
            "book" : "b1",
            "date" : ISODate("2011-01-01T00:00:00Z"),
            "lendingBy" : "jose"
        },
        {
            "_id" : "l2",
            "book" : "b1",
            "date" : ISODate("2012-02-02T00:00:00Z"),
            "lendingBy" : "maria"
        }
    ]
}
{
    "_id" : "b2",
    "name" : "Java Book",
    "category" : {
        "_id" : "c2",
        "name" : "romance"
    },
    "authors" : [
        {
            "_id" : "a1",
            "name" : {
                "first" : "orlando",
                "last" : "becerra"
            },
            "age" : 27
        },
        {
            "_id" : "a2",
            "name" : {
                "first" : "mayra",
                "last" : "sanchez"
            },
            "age" : 21
        }
    ],
    "lendings" : [ ]
}

Ich hoffe, diese Zeilen können Ihnen helfen.

2 Stimmen

Ich frage mich, wenn dieser gleiche Code mit Lehre mongodb ausgeführt werden kann?

4 Stimmen

Was passiert, wenn eines der Referenzobjekte eine Aktualisierung erhält? Wird diese Aktualisierung automatisch in das Buchobjekt übernommen? Oder muss die Schleife erneut durchlaufen werden?

25 Stimmen

Das ist in Ordnung, solange Ihre Daten klein sind. Es wird jeden Buchinhalt zu Ihrem Client bringen und dann jede Kategorie, jeden Verleih und jeden Autor einzeln abrufen. In dem Moment, in dem Sie Tausende von Büchern haben, würde dies wirklich sehr langsam gehen. Eine bessere Technik wäre wahrscheinlich die Verwendung einer Aggregationspipeline und die Ausgabe der zusammengeführten Daten in einer separaten Sammlung. Lassen Sie mich noch einmal darauf zurückkommen. Ich werde das als Antwort hinzufügen.

154voto

William Stein Punkte 2399

Diese Seite auf der offiziellen mongodb-Website behandelt genau diese Frage:

https://mongodb-documentation.readthedocs.io/en/latest/ecosystem/tutorial/model-data-for-ruby-on-rails.html

Wenn wir unsere Liste der Beiträge anzeigen, müssen wir den Namen des Nutzers anzeigen, der den Beitrag veröffentlicht hat. Wenn wir eine relationale Datenbank verwenden würden, könnten wir eine Verknüpfung von Benutzern und Geschäften durchführen und alle unsere Objekte in einer einzigen Abfrage abrufen. MongoDB unterstützt jedoch keine Verknüpfungen und erfordert daher manchmal eine kleine Denormalisierung. In diesem Fall bedeutet dies, dass das Attribut "username" zwischengespeichert wird.

Beziehungspuristen fühlen sich vielleicht schon unwohl, als ob wir gegen ein universelles Gesetz verstoßen würden. Wir sollten jedoch bedenken, dass MongoDB-Sammlungen nicht mit relationalen Tabellen gleichzusetzen sind; jede dient einem anderen Zweck. Eine normalisierte Tabelle bietet einen atomaren, isolierten Datenblock. Ein Dokument hingegen repräsentiert eher ein Objekt als Ganzes. Im Falle einer sozialen Nachrichtenseite kann man argumentieren, dass ein Benutzername ein wesentlicher Bestandteil der geposteten Geschichte ist.

59 Stimmen

@dudelgrincen es ist ein Paradigmenwechsel von Normalisierung und relationalen Datenbanken. Das Ziel einer NoSQL-Datenbank ist es, sehr schnell aus der Datenbank zu lesen und zu schreiben. Bei BigData werden Sie Unmengen von Anwendungs- und Front-End-Servern mit geringeren Zahlen an DBs haben. Es wird erwartet, dass Sie Millionen von Transaktionen pro Sekunde durchführen. Verlagern Sie die Schwerstarbeit von der Datenbank auf die Anwendungsebene. Wenn Sie tiefgehende Analysen benötigen, führen Sie einen Integrationsauftrag aus, der Ihre Daten in eine OLAP-Datenbank einspeist. Sie sollten ohnehin nicht viele tiefgehende Abfragen von Ihren OLTP-Datenbanken erhalten.

20 Stimmen

@dudelgrincen Ich sollte auch sagen, dass es nicht für jedes Projekt oder Design geeignet ist. Wenn Sie etwas haben, das in einer Datenbank vom Typ SQL funktioniert, warum sollten Sie es ändern? Wenn Sie Ihr Schema nicht so anpassen können, dass es mit noSQL funktioniert, dann lassen Sie es.

9 Stimmen

Migrationen und sich ständig weiterentwickelnde Schemata sind in einem NoSQL-System ebenfalls viel einfacher zu verwalten.

44voto

sbharti Punkte 801

Mit der richtigen Kombination aus $Nachschlag , $Projekt y $match können Sie mehrere Tabellen über mehrere Parameter verbinden. Dies liegt daran, dass sie mehrfach verkettet werden können.

Angenommen, wir wollen folgendes tun ( Referenz )

SELECT S.* FROM LeftTable S
LEFT JOIN RightTable R ON S.ID = R.ID AND S.MID = R.MID  
WHERE R.TIM > 0 AND S.MOB IS NOT NULL

Schritt 1: Verknüpfung aller Tabellen

können Sie mit $lookup so viele Tabellen durchsuchen, wie Sie wollen.

$Nachschlag - eine für jede Tabelle in der Abfrage

$unwind - Daten korrekt denormalisiert, sonst wären sie in Arrays verpackt

Python-Code.

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"}

                        ])

Schritt 2: Definieren Sie alle Konditionalitäten

$Projekt : Definieren Sie hier alle bedingten Anweisungen und alle Variablen, die Sie auswählen möchten.

Python Code..

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }}
                        ])

Schritt 3: Verbinden Sie alle Konditionalitäten

$match - alle Bedingungen mit OR oder AND usw. verbinden. Es kann mehrere dieser Bedingungen geben.

$Projekt : alle Bedingungen aufheben

Vollständiger Python-Code.

db.LeftTable.aggregate([
                        # connect all tables

                        {"$lookup": {
                          "from": "RightTable",
                          "localField": "ID",
                          "foreignField": "ID",
                          "as": "R"
                        }},
                        {"$unwind": "$R"},

                        # define conditionals + variables

                        {"$project": {
                          "midEq": {"$eq": ["$MID", "$R.MID"]},
                          "ID": 1, "MOB": 1, "MID": 1
                        }},

                        # join all conditionals

                        {"$match": {
                          "$and": [
                            {"R.TIM": {"$gt": 0}}, 
                            {"MOB": {"$exists": True}},
                            {"midEq": {"$eq": True}}
                        ]}},

                        # undefine conditionals

                        {"$project": {
                          "midEq": 0
                        }}

                        ])

So gut wie jede Kombination von Tabellen, Konditionalen und Joins kann auf diese Weise durchgeführt werden.

7 Stimmen

Danke, ich mag das Format Ihrer Antwort.

4 Stimmen

Perfekte Antwort, bei mir gab es einen Fehler für {"$unwind ":"R"} wenn sie geändert wird in {"$unwind":"$R"} Es funktioniert perfekt!

38voto

Otto Allmendinger Punkte 26430

Sie müssen es so machen, wie Sie es beschrieben haben. MongoDB ist eine nicht-relationale Datenbank und unterstützt keine Joins.

4 Stimmen

Scheint falsch Leistung weise kommen von einem SQL-Server-Hintergrund, aber seine vielleicht nicht so schlecht mit einem Dokument db?

3 Stimmen

Da ich auch aus einem SQL-Server-Hintergrund komme, würde ich es begrüßen, wenn MongoDB eine "Ergebnismenge" (mit ausgewählten zurückgegebenen Feldern) als Eingabe für eine neue Abfrage in einem Durchgang nehmen würde, ähnlich wie bei verschachtelten Abfragen in SQL

1 Stimmen

@terjetyl Man muss es wirklich einplanen. Wenn es sich um eine begrenzte Anzahl von Feldern in einer einzelnen Ansicht handelt, nehmen Sie diese als eingebettete Dokumente. Der Schlüssel ist, dass Sie keine Joins durchführen müssen. Wenn Sie tiefgreifende Analysen durchführen wollen, tun Sie dies nachträglich in einer anderen Datenbank. Führen Sie einen Auftrag aus, der die Daten in einen OLAP-Würfel umwandelt, um eine optimale Leistung zu erzielen.

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