it-swarm.com.de

Wie führe ich das SQL Join-Äquivalent in MongoDB durch?

Wie führe ich das SQL Join-Äquivalent in MongoDB durch?

Angenommen, Sie haben zwei Sammlungen (Benutzer und Kommentare) und ich möchte alle Kommentare mit pid = 444 zusammen mit den Benutzerinformationen für jede Sammlung 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 den mit jedem Kommentar verknüpften Benutzerinformationen auf einmal abzurufen?

Im Moment erhalte ich zuerst die Kommentare, die meinen Kriterien entsprechen, finde dann alle UIDs in dieser Ergebnismenge heraus, erhalte die Benutzerobjekte und füge sie mit den Ergebnissen des Kommentars zusammen. Scheint, als ob ich es falsch mache.

455
The Unknown

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 mit einem Left Outer Join identisch:

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

Aus den Dokumenten:

{
   $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 keine relationale Datenbank, und die Entwickler sind vorsichtig, spezifische Anwendungsfälle für $ lookup zu empfehlen, aber mindestens ab Version 3.2 tun sie join ist jetzt mit MongoDB möglich.

279
Clayton Gulick

Diese Seite auf der offiziellen mongodb Seite adressiert genau diese Frage:

http://docs.mongodb.org/ecosystem/tutorial/model-data-for-Ruby-on-Rails/

Wenn wir unsere Liste mit Storys anzeigen, müssen wir den Namen des Benutzers anzeigen, der die Story gepostet hat. Wenn wir eine relationale Datenbank verwenden, können wir einen Join für Benutzer und Informationsspeicher ausführen und alle unsere Objekte in einer einzigen Abfrage abrufen. MongoDB unterstützt jedoch keine Joins und erfordert daher manchmal eine gewisse Denormalisierung. In diesem Fall muss das Attribut 'Benutzername' zwischengespeichert werden.

Relationale Puristen fühlen sich vielleicht schon unwohl, als ob wir gegen ein universelles Gesetz verstoßen würden. Beachten Sie jedoch, dass MongoDB-Auflistungen nicht den relationalen Tabellen entsprechen. jedes dient einem einzigartigen Entwurfsziel. Eine normalisierte Tabelle stellt einen atomaren, isolierten Datenblock bereit. Ein Dokument repräsentiert jedoch eher ein Objekt als Ganzes. Im Fall einer Social News-Site kann argumentiert werden, dass ein Benutzername für die gepostete Geschichte von wesentlicher Bedeutung ist.

138
William Stein

Mit der Mongodb-Client-Konsole können wir alle Daten in nur einer Sammlung mit einer einfachen Funktion in wenigen Zeilen zusammenführen und die gewünschte Abfrage durchführen. Nachfolgend ein vollständiges Beispiel:

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

.- Holen Sie sich die neuen Sammlungsdaten:

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.

132
Orlando Becerra

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

39

Hier ist ein Beispiel für einen "Join" * Schauspieler und Filme Sammlungen:

https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt

Dabei wird die Methode .mapReduce() verwendet

* join - eine Alternative zum Join in dokumentorientierten Datenbanken

17
antitoxic

Wie andere darauf hingewiesen haben, versuchen Sie, eine relationale Datenbank aus keiner relationalen Datenbank zu erstellen, die Sie wirklich nicht möchten, aber wenn Sie den Fall haben, dass Sie dies hier tun müssen, können Sie eine Lösung verwenden. Wir führen zuerst eine Foreach-Suche in Sammlung A durch (oder in Ihrem Fall Benutzer) und dann bekommen wir jedes Objekt als Objekt. Dann verwenden wir die Objekteigenschaft (in Ihrem Fall UID), um in unserer zweiten Sammlung (in Ihrem Fall Kommentare) nachzuschlagen, wenn wir kann es finden, dann haben wir eine Übereinstimmung und wir können etwas damit drucken oder tun. Hoffe das hilft dir und viel Glück :)

db.users.find().forEach(
function (object) {
    var commonInBoth=db.comments.findOne({ "uid": object.uid} );
    if (commonInBoth != null) {
        printjson(commonInBoth) ;
        printjson(object) ;
    }else {
        // did not match so we don't care in this case
    }
});
16
grepit

Mit der richtigen Kombination von $ lookup , $ project und $ match können Sie mehrere Tabellen mit mehreren Parametern verknüpfen. Dies liegt daran, dass sie mehrfach verkettet werden können.

Angenommen, wir möchten 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üpfe alle Tabellen

sie können beliebig viele Tabellen nachschlagen.

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

$ unwind - weil die Daten korrekt denormalisiert sind, sonst 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 Bedingungen

$ project : Definieren Sie hier alle bedingten Anweisungen sowie 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 Bedingungen

$ match - Füge alle Bedingungen mit OR oder AND usw. zusammen. Es kann mehrere davon geben.

$ project : Definiert alle Bedingungen nicht

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

                        ])

Auf diese Weise kann so ziemlich jede Kombination von Tabellen, Bedingungen und Verknüpfungen durchgeführt werden.

13
Shaurabh Bharti

Es hängt davon ab, was Sie versuchen.

Sie haben es derzeit als normalisierte Datenbank eingerichtet, was in Ordnung ist, und die Art und Weise, wie Sie es tun, ist angemessen.

Es gibt jedoch auch andere Möglichkeiten.

Sie könnten eine Postsammlung haben, die Kommentare für jeden Post mit Verweisen auf die Benutzer enthält, die Sie iterativ abfragen können, um sie abzurufen. Sie könnten den Namen des Benutzers mit den Kommentaren speichern, Sie könnten sie alle in einem Dokument speichern.

Die Sache mit NoSQL ist, dass es für flexible Schemata und sehr schnelles Lesen und Schreiben ausgelegt ist. In einer typischen Big-Data-Farm ist die Datenbank der größte Engpass. Sie haben weniger Datenbank-Engines als Anwendungs- und Front-End-Server. Sie sind teurer, aber leistungsfähiger. Auch der Festplattenspeicher ist vergleichsweise günstig. Normalisierung beruht auf dem Konzept, Speicherplatz zu sparen, ist jedoch mit Kosten verbunden, da Ihre Datenbanken komplizierte Joins ausführen und die Integrität von Beziehungen sowie Kaskadenoperationen überprüfen müssen. All dies erspart den Entwicklern einige Kopfschmerzen, wenn sie die Datenbank richtig entworfen haben.

Wenn Sie bei NoSQL akzeptieren, dass Redundanz und Speicherplatz aufgrund ihrer Kosten keine Probleme darstellen (sowohl in Bezug auf die für Aktualisierungen erforderliche Prozessorzeit als auch in Bezug auf die Festplattenkosten zum Speichern zusätzlicher Daten), ist Denormalisierung kein Problem (für eingebettete Arrays, die zu einem Problem werden) Hunderttausende von Elementen kann es ein Leistungsproblem sein, aber die meiste Zeit ist das kein Problem). Darüber hinaus stehen für jeden Datenbankcluster mehrere Anwendungs- und Front-End-Server zur Verfügung. Lassen Sie sie die Joins schwer heben und die Datenbankserver beim Lesen und Schreiben bleiben.

TL; DR: Was Sie tun, ist in Ordnung, und es gibt andere Möglichkeiten, es zu tun. In den Datenmodellmustern der Mongodb-Dokumentation finden Sie einige gute Beispiele. http://docs.mongodb.org/manual/data-modeling/

10
Snowburnt

Sie können zwei Sammlungen in Mongo verbinden, indem Sie Lookup verwenden, das in Version 3.2 angeboten wird. In Ihrem Fall wäre die Abfrage

db.comments.aggregate({
    $lookup:{
        from:"users",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

oder Sie können auch in Bezug auf Benutzer beitreten, dann wird es eine kleine Änderung geben, wie unten angegeben.

db.users.aggregate({
    $lookup:{
        from:"comments",
        localField:"uid",
        foreignField:"uid",
        as:"users_comments"
    }
})

Dies funktioniert genauso wie der linke und rechte Join in SQL.

10
jarry jafery

Es gibt eine Spezifikation, die von vielen Treibern unterstützt wird und DBRef heißt.

DBRef ist eine formalere Spezifikation zum Erstellen von Referenzen zwischen Dokumenten. DBRefs enthalten (im Allgemeinen) einen Auflistungsnamen sowie eine Objekt-ID. Die meisten Entwickler verwenden DBRefs nur, wenn die Auflistung von einem Dokument zum nächsten wechseln kann. Wenn Ihre referenzierte Sammlung immer dieselbe ist, sind die oben aufgeführten manuellen Referenzen effizienter.

Aus der MongoDB-Dokumentation entnommen: Datenmodelle> Datenmodellreferenz> Datenbankreferenzen

9
Pickels

Vor .2.6 unterstützt Mongodb keine Join-Abfrage wie bei mysql. unten Lösung, die für Sie arbeitet.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])
6
Anish Agarwal

$ lookup (Aggregation)

Führt einen Left Outer Join mit einer nicht gesicherten Sammlung in derselben Datenbank durch, um Dokumente aus der "verbundenen" Sammlung zur Verarbeitung einzufiltern. Zu jedem Eingabedokument fügt die $ lookup-Stufe ein neues Array-Feld hinzu, dessen Elemente die übereinstimmenden Dokumente aus der "verbundenen" Sammlung sind. Die $ lookup-Stufe übergibt diese umgeformten Dokumente an die nächste Stufe. Die $ lookup-Stufe hat folgende Syntax:

Gleichstellungs-Match

Um eine Gleichheitsübereinstimmung zwischen einem Feld aus den Eingabedokumenten und einem Feld aus den Dokumenten der verknüpften Auflistung durchzuführen, hat die $ lookup-Stufe die folgende Syntax:

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

Die Operation würde der folgenden Pseudo-SQL-Anweisung entsprechen:

SELECT *, <output array field>
FROM collection
WHERE <output array field> IN (SELECT <documents as determined from the pipeline>
                               FROM <collection to join>
                               WHERE <pipeline> );

Mongo URL

6
GoutamS

Sie können SQL-Abfragen einschließlich Join auf MongoDB mit mongo_fdw von Postgres ausführen.

3
metdos

MongoDB erlaubt keine Joins, aber Sie können Plugins verwenden, um das zu handhaben. Überprüfen Sie das Mongo-Join-Plugin. Es ist das Beste und ich habe es bereits benutzt. Sie können es mit npm direkt wie folgt installieren npm install mongo-join. Sie können die vollständige Dokumentation mit Beispielen .

(++) wirklich hilfreiches Tool, wenn wir (N) Sammlungen verbinden müssen

(-) Wir können Bedingungen nur auf der obersten Ebene der Abfrage anwenden

Beispiel

var Join = require('mongo-join').Join, mongodb = require('mongodb'), Db = mongodb.Db, Server = mongodb.Server;
db.open(function (err, Database) {
    Database.collection('Appoint', function (err, Appoints) {

        /* we can put conditions just on the top level */
        Appoints.find({_id_Doctor: id_doctor ,full_date :{ $gte: start_date },
            full_date :{ $lte: end_date }}, function (err, cursor) {
            var join = new Join(Database).on({
                field: '_id_Doctor', // <- field in Appoints document
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            }).on({
                field: '_id_Patient', // <- field in Appoints doc
                to: '_id',         // <- field in User doc. treated as ObjectID automatically.
                from: 'User'  // <- collection name for User doc
            })
            join.toArray(cursor, function (err, joinedDocs) {

                /* do what ever you want here */
                /* you can fetch the table and apply your own conditions */
                .....
                .....
                .....


                resp.status(200);
                resp.json({
                    "status": 200,
                    "message": "success",
                    "Appoints_Range": joinedDocs,


                });
                return resp;


            });

    });
2
Amine_Dev

Sie können es mit der Aggregationspipeline tun, aber es ist mühsam, es selbst zu schreiben.

Sie können mongo-join-query verwenden, um die Aggregationspipeline automatisch aus Ihrer Abfrage zu erstellen.

So würde Ihre Anfrage aussehen:

_const mongoose = require("mongoose");
const joinQuery = require("mongo-join-query");

joinQuery(
    mongoose.models.Comment,
    {
        find: { pid:444 },
        populate: ["uid"]
    },
    (err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);
_

Ihr Ergebnis hätte das Benutzerobjekt im Feld uid und Sie können so viele Ebenen verknüpfen, wie Sie möchten. Sie können den Verweis auf den Benutzer einfügen, der auf ein Team verweist, der auf etwas anderes verweist usw.

Haftungsausschluss : Ich habe _mongo-join-query_ geschrieben, um genau dieses Problem anzugehen.

1

playORM kann dies für Sie mit S-SQL (Scalable SQL) tun, das lediglich die Partitionierung hinzufügt, sodass Sie Joins innerhalb von Partitionen erstellen können.

0
Dean Hiller