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

498

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 die mit jedem Kommentar verknüpften Benutzerinformationen auf einmal abzurufen?

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

Das Unbekannte
quelle
35
Die letzte Antwort auf diese Frage ist wahrscheinlich die relevanteste, da MongoDB 3.2+ eine Join-Lösung namens $ lookup implementiert hat. Ich dachte, ich würde es hier vorantreiben, weil vielleicht nicht jeder nach unten lesen wird. stackoverflow.com/a/33511166/2593330
thefourtheye
6
Richtig, $ lookup wurde in MongoDB 3.2 eingeführt. Details finden Sie unter docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Antworten:

306

Ab Mongo 3.2 sind die Antworten auf diese Frage meist nicht mehr richtig. Der neue $ lookup-Operator, der der Aggregationspipeline hinzugefügt wurde, ist im Wesentlichen identisch mit einem linken äußeren Join:

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 empfehlen sorgfältig, bestimmte Anwendungsfälle für $ lookup zu empfehlen, aber ab Version 3.2 ist das Beitreten jetzt mit MongoDB möglich.

Clayton Gulick
quelle
@clayton: Wie wäre es mit mehr als zwei Sammlungen?
Dipen Dedania
1
@DipenDedania fügt der Aggregationspipeline nur zusätzliche $ Lookup-Stufen hinzu.
Clayton Gulick
Ich kann keinem Feld im Array in der linken Sammlung mit der entsprechenden ID in der rechten Sammlung beitreten. Kann mir jemand helfen?
Prateek Singh
1
Ich bin etwas verwirrt darüber - gibt es eine Möglichkeit anzugeben, dass Sie nur bestimmte Dokumente in der "from" -Sammlung haben möchten, oder werden automatisch alle Dokumente gleichzeitig in der Datenbank verknüpft?
user3413723
Sie fragen sich nur, ob die neueste Spring Data MongoDB 3.2 unterstützt?
gtiwari333
142

Diese Seite auf der offiziellen Mongodb-Website befasst sich genau mit dieser Frage:

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

Wenn wir unsere Liste der Geschichten anzeigen, müssen wir den Namen des Benutzers anzeigen, der die Geschichte gepostet hat. Wenn wir eine relationale Datenbank verwenden, können wir einen Join für Benutzer und Speicher durchführen und alle unsere Objekte in einer einzigen Abfrage abrufen. MongoDB unterstützt jedoch keine Joins und erfordert daher manchmal eine gewisse Denormalisierung. Hier bedeutet dies, dass das Attribut 'Benutzername' zwischengespeichert wird.

Relationale Puristen fühlen sich möglicherweise bereits unwohl, als ob wir gegen ein universelles Gesetz verstoßen würden. Beachten Sie jedoch, dass MongoDB-Sammlungen nicht relationalen Tabellen entsprechen. Jedes dient einem einzigartigen Designziel. Eine normalisierte Tabelle liefert einen atomaren, isolierten Datenblock. Ein Dokument repräsentiert jedoch ein Objekt als Ganzes genauer. Im Fall einer Social-News-Site kann argumentiert werden, dass ein Benutzername für die veröffentlichte Geschichte von wesentlicher Bedeutung ist.

William Stein
quelle
51
@dudelgrincen Es ist ein Paradigmenwechsel von Normalisierungs- und relationalen Datenbanken. Das Ziel eines NoSQL ist es, sehr schnell aus der Datenbank zu lesen und zu schreiben. Mit BigData erhalten Sie eine Vielzahl von Anwendungs- und Front-End-Servern mit niedrigeren Zahlen in DBs. Es wird erwartet, dass Sie Millionen von Transaktionen pro Sekunde ausführen. Laden Sie das schwere Heben aus der Datenbank und stellen Sie es auf Anwendungsebene. Wenn Sie eine gründliche Analyse benötigen, führen Sie einen Integrationsjob aus, bei dem Ihre Daten in eine OLAP-Datenbank gestellt werden. Sie sollten sowieso nicht viele tiefe Anfragen von Ihren OLTP-DBS erhalten.
Snowburnt
18
@dudelgrincen Ich sollte auch sagen, dass es nicht für jedes Projekt oder Design ist. Wenn Sie etwas haben, das in einer SQL-Datenbank funktioniert, warum sollten Sie es ändern? Wenn Sie Ihr Schema nicht massieren können, um mit noSQL zu arbeiten, tun Sie es nicht.
Snowburnt
9
Migrationen und sich ständig weiterentwickelnde Schemata sind auf einem NoSQL-System auch viel einfacher zu verwalten.
Justin
14
Was ist, wenn der Benutzer 3.540 Beiträge auf der Website hat und seinen Benutzernamen im Profil ändert? Sollte jeder Beitrag mit dem neuen Benutzernamen aktualisiert werden?
Ivo Pereira
2
@IvoPereira Ja und genau deshalb sollte man es vermeiden, Daten auf diese Weise zu modellieren. Es gibt einen Artikel, der das gleiche Szenario und seine Konsequenzen erklärt: Warum Sie MongoDB niemals verwenden sollten
Omid
138

Mit der Mongodb-Client-Konsole können wir alle Daten in nur einer Sammlung mit einer einfachen Funktion in wenigen Zeilen zusammenführen / verbinden. Jetzt können wir die gewünschte Abfrage ausführen. Unten 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.

Orlando Becerra
quelle
2
Ich frage mich, ob der gleiche Code mit Doctrine Mongodb ausgeführt werden kann.
Abbood
4
Was passiert, wenn eines der Referenzobjekte aktualisiert wird? Wird dieses Update automatisch im Buchobjekt angezeigt? Oder muss diese Schleife erneut ausgeführt werden?
Balupton
14
Dies ist in Ordnung, solange Ihre Daten klein sind. Es wird jeden Buchinhalt zu Ihrem Kunden bringen und dann jede Kategorie, Leihgabe und Autoren einzeln abrufen. In dem Moment, in dem Ihre Bücher zu Tausenden sind, würde dies sehr, sehr langsam gehen. Eine bessere Technik wäre wahrscheinlich, die Aggregationspipeline zu verwenden und die zusammengeführten Daten in einer separaten Sammlung auszugeben. Lassen Sie mich noch einmal darauf zurückkommen. Ich werde dem eine Antwort hinzufügen.
Sandeep Giri
Können Sie Ihren Algorithmus an dieses andere Beispiel anpassen? stackoverflow.com/q/32718079/287948
Peter Krauss
1
@ SandeepGiri Wie kann ich die aggregierte Pipeline ausführen, da ich wirklich sehr intensive Daten in einer getrennten Sammlung habe, die verbunden werden müssen?
Yassine Abdul-Rahman
38

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

Otto Allmendinger
quelle
4
Scheint in Bezug auf die Leistung von einem SQL Server-Hintergrund falsch zu sein, aber es ist vielleicht nicht so schlecht mit einer Dokument-Datenbank?
Terjetyl
3
Auch vor dem Hintergrund eines SQL-Servers würde ich es begrüßen, wenn MongoDB eine 'Ergebnismenge' (mit ausgewählten zurückgegebenen Feldern) als Eingabe für eine neue Abfrage auf einmal verwendet, ähnlich wie verschachtelte Abfragen in SQL
Stijn Sanders
1
@terjetyl Du musst es wirklich planen. Welche Felder werden im Frontend angezeigt? Wenn es sich in einer Einzelansicht um eine begrenzte Anzahl handelt, nehmen Sie diese als eingebettete Dokumente. Der Schlüssel ist, keine Joins durchführen zu müssen. Wenn Sie eine gründliche Analyse durchführen möchten, tun Sie dies nachträglich in einer anderen Datenbank. Führen Sie einen Job aus, der die Daten für eine optimale Leistung in einen OLAP-Cube umwandelt.
Snowburnt
4
Ab Mongo 3.2 werden Linksverknüpfungen unterstützt.
Somnath Muluk
18

Wie andere bereits betont haben, versuchen Sie, eine relationale Datenbank aus keiner relationalen Datenbank zu erstellen, was Sie wirklich nicht tun möchten, aber wenn Sie den Fall haben, dass Sie dies hier tun müssen, ist dies eine Lösung, die Sie verwenden können. Wir führen zuerst einen Foreach-Fund für Sammlung A (oder in Ihrem Fall für Benutzer) durch und erhalten dann jedes Element als Objekt. Anschließend verwenden wir die Objekteigenschaft (in Ihrem Fall uid), um in unserer zweiten Sammlung (in Ihren Fallkommentaren) nachzuschlagen, wenn wir dies tun kann es finden, dann haben wir eine Übereinstimmung und wir können drucken oder etwas damit machen. 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
    }
});
grepit
quelle
Wird dies nicht den Gegenstand finden, auf dem wir uns gerade befinden?
Skarlinski
18

Mit der richtigen Kombination aus $ 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üpfen Sie alle Tabellen

Sie können so viele Tabellen nachschlagen, wie Sie möchten.

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

$ unwind - weil Daten korrekt denormalisiert sind, sonst in Arrays eingeschlossen

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 - Verbinde alle Bedingungen mit ODER oder UND usw. Es kann ein Vielfaches davon geben.

$ project : Undefiniere alle Bedingungen

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.

sbharti
quelle
17

Hier ist ein Beispiel für eine "Join" * -Sammlung von Schauspielern und Filmen :

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

Es bedient sich der .mapReduce()Methode

* join - eine Alternative zum Beitritt zu dokumentorientierten Datenbanken

antitoxisch
quelle
19
-1, Dies verbindet NICHT Daten aus zwei Sammlungen. Es werden Daten aus einer einzelnen Sammlung (Akteuren) verwendet, um die Daten herumgeschwenkt werden. So dass Dinge, die Schlüssel waren, jetzt Werte sind und Werte jetzt Schlüssel sind ... ganz anders als ein JOIN.
Evan Teran
12
Genau das müssen Sie tun. MongoDB ist nicht relational, sondern dokumentenorientiert. MapReduce ermöglicht das Spielen mit Daten mit hoher Leistung (Sie können Cluster usw. verwenden ...), aber selbst in einfachen Fällen ist es sehr nützlich!
Thomas Decaux
14

Sie können zwei Sammlungen in Mongo verbinden, indem Sie die Suche verwenden, die in der 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"
    }
})

Es funktioniert genauso wie die Verknüpfung von links und rechts in SQL.

jarry jafery
quelle
11

Es hängt davon ab, was Sie versuchen zu tun.

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, in die Kommentare für jeden Post mit Verweisen auf die Benutzer eingebettet sind, die Sie iterativ abfragen können, um sie abzurufen. Sie können den Namen des Benutzers mit den Kommentaren speichern, Sie können 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 sehr billig. Die Normalisierung beruht auf dem Konzept, Speicherplatz zu sparen, ist jedoch mit Kosten verbunden, wenn Ihre Datenbanken komplizierte Verknüpfungen ausführen und die Integrität von Beziehungen überprüfen sowie Kaskadenvorgänge ausführen. All dies erspart den Entwicklern einige Kopfschmerzen, wenn sie die Datenbank richtig entworfen haben.

Wenn Sie mit NoSQL akzeptieren, dass Redundanz und Speicherplatz aufgrund ihrer Kosten keine Probleme darstellen (sowohl hinsichtlich der für Aktualisierungen erforderlichen Prozessorzeit als auch der Festplattenkosten für die Speicherung zusätzlicher Daten), ist die Denormalisierung kein Problem (für eingebettete Arrays, die zu Problemen werden) Hunderttausende von Elementen kann ein Leistungsproblem sein, aber meistens ist das kein Problem. Zusätzlich haben Sie mehrere Anwendungs- und Front-End-Server für jeden Datenbankcluster. Lassen Sie sie die Joins schwer anheben und die Datenbankserver beim Lesen und Schreiben bleiben.

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

Schnee verbrannt
quelle
8
"Normalisierung entsteht durch das Konzept, Platz zu sparen", frage ich mich. Die IMHO-Normalisierung beruht auf dem Konzept der Vermeidung von Redundanz. Angenommen, Sie speichern den Namen eines Benutzers zusammen mit einem Blogpost. Was ist, wenn sie heiratet? In einem nicht normalisierten Modell müssen Sie alle Beiträge durchblättern und den Namen ändern. In einem normalisierten Modell ändern Sie normalerweise EINEN Datensatz.
Daniel Khan
@DanielKhan Redundanz verhindern und Platz sparen sind ähnliche Konzepte, aber bei erneuter Analyse stimme ich zu, Redundanz ist die Hauptursache für dieses Design. Ich werde umformulieren. Danke für den Hinweis.
Snowburnt
11

Es gibt eine Spezifikation, die viele Treiber unterstützen und die DBRef heißt.

DBRef ist eine formellere Spezifikation zum Erstellen von Referenzen zwischen Dokumenten. DBRefs enthalten (im Allgemeinen) einen Sammlungsnamen sowie eine Objekt-ID. Die meisten Entwickler verwenden DBRefs nur, wenn die Sammlung von einem Dokument zum nächsten geändert werden kann. Wenn Ihre referenzierte Sammlung immer dieselbe ist, sind die oben beschriebenen manuellen Referenzen effizienter.

Entnommen aus der MongoDB-Dokumentation: Datenmodelle> Datenmodellreferenz> Datenbankreferenzen

Pickels
quelle
11

$ lookup (Aggregation)

Führt eine linke äußere Verknüpfung zu einer nicht gesicherten Sammlung in derselben Datenbank durch, um Dokumente aus der "verbundenen" Sammlung zur Verarbeitung zu filtern. Zu jedem Eingabedokument fügt die $ lookup-Phase ein neues Array-Feld hinzu, dessen Elemente die übereinstimmenden Dokumente aus der "verbundenen" Sammlung sind. Die $ lookup-Phase übergibt diese umgeformten Dokumente an die nächste Phase. Die $ lookup-Phase hat die folgenden Syntaxen:

Gleichstellungsübereinstimmung

Um eine Gleichheitsübereinstimmung zwischen einem Feld aus den Eingabedokumenten und einem Feld aus den Dokumenten der "verbundenen" Sammlung durchzuführen, hat die $ lookup-Phase 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

GoutamS
quelle
Unterabfrage ist völlig anders als Join. Wenn Ihre linke Nebentabelle sehr groß ist, bedeutet Unterabfrage, dass jede Zeile selbst eine Abfrage durchführen muss. es wird sehr langsam werden. Join ist in SQL sehr schnell.
yww325
8

Vor 3.2.6 unterstützt Mongodb keine Join-Abfrage wie MySQL. unten Lösung, die für Sie funktioniert.

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

MongoDB erlaubt keine Joins, aber Sie können Plugins verwenden, um dies 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 so installieren npm install mongo-join. Sie können die vollständige Dokumentation mit Beispielen überprüfen .

(++) wirklich hilfreiches Tool, wenn wir (N) Sammlungen beitreten 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;


            });

    });
Amine_Dev
quelle
2

Sie können dies mithilfe der Aggregationspipeline tun, aber es ist schwierig, es selbst zu schreiben.

Sie können mongo-join-querydie Aggregationspipeline automatisch aus Ihrer Abfrage 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 würde das Benutzerobjekt im uidFeld haben und Sie können so viele Ebenen verknüpfen, wie Sie möchten. Sie können den Verweis auf den Benutzer ausfüllen, der auf ein Team verweist, auf etwas anderes verweist usw.

Haftungsausschluss : Ich habe geschrieben mongo-join-query, um genau dieses Problem anzugehen.

Marcelo Lazaroni
quelle
0

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

Dean Hiller
quelle
-2

Nein, es scheint nicht so, als ob du es falsch machst. MongoDB-Joins sind "clientseitig". So ziemlich wie du gesagt hast:

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

1) Select from the collection you're interested in.
2) From that collection pull out ID's you need
3) Select from other collections
4) Decorate your original results.

Es ist kein "echter" Join, aber es ist tatsächlich viel nützlicher als ein SQL-Join, da Sie sich nicht mit doppelten Zeilen für "viele" einseitige Joins befassen müssen, sondern stattdessen den ursprünglich ausgewählten Satz dekorieren müssen.

Auf dieser Seite gibt es viel Unsinn und FUD. Es stellt sich heraus, dass MongoDB 5 Jahre später immer noch eine Sache ist.

Michael Cole
quelle
"Sie müssen sich nicht mit doppelten Zeilen für" viele "seitige Verknüpfungen befassen" - keine Ahnung, was Sie damit meinen. Könntest Du das erläutern?
Mark Amery
1
@ MarkAmery, sicher. In SQL gibt eine nn-Beziehung doppelte Zeilen zurück. ZB Freunde. Wenn Bob mit Mary und Jane befreundet ist, erhalten Sie zwei Zeilen für Bob: Bob, Mary und Bob, Jane. 2 Bobs ist eine Lüge, es gibt nur einen Bob. Mit clientseitigen Joins können Sie mit Bob beginnen und dekorieren, wie Sie möchten: Bob, "Mary and Jane". Mit SQL können Sie dies mit Unterabfragen tun, aber das erledigt die Arbeit auf dem Datenbankserver, die auf dem Client ausgeführt werden könnte.
Michael Cole
-3

Ich denke, wenn Sie normalisierte Datentabellen benötigen, müssen Sie einige andere Datenbanklösungen ausprobieren.

Aber ich habe diese Lösung für MOngo auf Git gefunden. Übrigens, in Code einfügen - es hat den Namen des Films, aber die ID des Noi-Films .

Problem

Sie haben eine Sammlung von Schauspielern mit einer Reihe von Filmen, die sie gemacht haben.

Sie möchten eine Sammlung von Filmen mit jeweils einer Reihe von Akteuren erstellen.

Einige Beispieldaten

 db.actors.insert( { actor: "Richard Gere", movies: ['Pretty Woman', 'Runaway Bride', 'Chicago'] });
 db.actors.insert( { actor: "Julia Roberts", movies: ['Pretty Woman', 'Runaway Bride', 'Erin Brockovich'] });

Lösung

Wir müssen jeden Film im Actor-Dokument durchlaufen und jeden Film einzeln ausgeben.

Der Haken liegt hier in der Reduktionsphase. Wir können kein Array aus der Reduktionsphase ausgeben, daher müssen wir ein Actors-Array innerhalb des zurückgegebenen "Wert" -Dokuments erstellen.

Der Code
map = function() {
  for(var i in this.movies){
    key = { movie: this.movies[i] };
    value = { actors: [ this.actor ] };
    emit(key, value);
  }
}

reduce = function(key, values) {
  actor_list = { actors: [] };
  for(var i in values) {
    actor_list.actors = values[i].actors.concat(actor_list.actors);
  }
  return actor_list;
}

Beachten Sie, dass actor_list tatsächlich ein Javascript-Objekt ist, das ein Array enthält. Beachten Sie auch, dass die Karte dieselbe Struktur ausgibt.

Führen Sie die folgenden Schritte aus, um die Map / Reduce auszuführen, geben Sie sie in die "Pivot" -Sammlung aus und drucken Sie das Ergebnis:

printjson (db.actors.mapReduce (map, redu, "pivot")); db.pivot.find (). forEach (printjson);

Hier ist die Beispielausgabe. Beachten Sie, dass "Pretty Woman" und "Runaway Bride" sowohl "Richard Gere" als auch "Julia Roberts" haben.

{ "_id" : { "movie" : "Chicago" }, "value" : { "actors" : [ "Richard Gere" ] } }
{ "_id" : { "movie" : "Erin Brockovich" }, "value" : { "actors" : [ "Julia Roberts" ] } }
{ "_id" : { "movie" : "Pretty Woman" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }
{ "_id" : { "movie" : "Runaway Bride" }, "value" : { "actors" : [ "Richard Gere", "Julia Roberts" ] } }

Max Sherbakov
quelle
Beachten Sie, dass der größte Teil des Inhalts dieser Antwort (dh das Bit, das in verständlichem Englisch vorliegt) aus dem MongoDB-Kochbuch unter dem vom Antwortenden bereitgestellten GitHub-Link kopiert wird.
Mark Amery
-4

Wir können zwei Sammlungen mithilfe der mongoDB-Unterabfrage zusammenführen. Hier ist ein Beispiel, Commentss--

`db.commentss.insert([
  { uid:12345, pid:444, comment:"blah" },
  { uid:12345, pid:888, comment:"asdf" },
  { uid:99999, pid:444, comment:"qwer" }])`

Benutzer--

db.userss.insert([
  { uid:12345, name:"john" },
  { uid:99999, name:"mia"  }])

MongoDB-Unterabfrage für JOIN--

`db.commentss.find().forEach(
    function (newComments) {
        newComments.userss = db.userss.find( { "uid": newComments.uid } ).toArray();
        db.newCommentUsers.insert(newComments);
    }
);`

Ergebnis aus neu generierter Sammlung abrufen--

db.newCommentUsers.find().pretty()

Ergebnis--

`{
    "_id" : ObjectId("5511236e29709afa03f226ef"),
    "uid" : 12345,
    "pid" : 444,
    "comment" : "blah",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f0"),
    "uid" : 12345,
    "pid" : 888,
    "comment" : "asdf",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f2"),
            "uid" : 12345,
            "name" : "john"
        }
    ]
}
{
    "_id" : ObjectId("5511236e29709afa03f226f1"),
    "uid" : 99999,
    "pid" : 444,
    "comment" : "qwer",
    "userss" : [
        {
            "_id" : ObjectId("5511238129709afa03f226f3"),
            "uid" : 99999,
            "name" : "mia"
        }
    ]
}`

Hoffe, das wird helfen.

Krishna
quelle
7
Warum haben Sie diese fast identische, einjährige Antwort im Grunde genommen kopiert? stackoverflow.com/a/22739813/4186945
Hackel