Jak wykonać odpowiednik SQL Join w MongoDB?

498

Jak wykonać odpowiednik SQL Join w MongoDB?

Powiedzmy na przykład, że masz dwie kolekcje (użytkownicy i komentarze), a ja chcę pobrać wszystkie komentarze za pomocą pid = 444 wraz z informacjami dla każdego z nich.

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

Czy istnieje sposób na wyciągnięcie wszystkich komentarzy z określonym polem (np. ... find ({pid: 444})) i informacji o użytkowniku związanych z każdym komentarzem za jednym razem?

W tej chwili najpierw otrzymuję komentarze, które pasują do moich kryteriów, a następnie ustalam wszystkie numery UID w tym zestawie wyników, pobieram obiekty użytkownika i łączę je z wynikami komentarza. Wygląda na to, że robię to źle.

Nieznane
źródło
35
Ostatnia odpowiedź na to pytanie jest prawdopodobnie najbardziej odpowiednia, ponieważ MongoDB 3.2+ wdrożył rozwiązanie łączenia o nazwie $ lookup. Pomyślałem, że popchnę to tutaj, bo może nie wszyscy będą czytać na dole. stackoverflow.com/a/33511166/2593330
thefourtheye
6
Prawidłowo, wyszukiwanie $ zostało wprowadzone w MongoDB 3.2. Szczegóły można znaleźć na docs.mongodb.org/master/reference/operator/aggregation/lookup/...
NDB

Odpowiedzi:

306

Począwszy od wersji Mongo 3.2 odpowiedzi na to pytanie są w większości niepoprawne. Nowy operator wyszukiwania $ dodany do potoku agregacji jest zasadniczo identyczny jak lewe połączenie zewnętrzne:

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

Z dokumentów:

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

Oczywiście Mongo nie jest relacyjną bazą danych, a deweloperzy ostrożnie zalecają konkretne przypadki użycia dla wyszukiwania $, ale przynajmniej od 3.2 robienie łączenia jest teraz możliwe z MongoDB.

Clayton Gulick
źródło
@clayton: Co powiesz na więcej niż dwie kolekcje?
Dipen Dedania
1
@DipenDedania po prostu dodaj dodatkowe etapy wyszukiwania $ do potoku agregacji.
Clayton Gulick
Nie mogę dołączyć do żadnego pola w tablicy w lewej kolekcji z odpowiednim identyfikatorem w prawej kolekcji. Czy ktoś może mi pomóc?
Prateek Singh
1
Jestem trochę zdezorientowany - czy jest jakiś sposób, aby określić, że chcesz tylko niektóre dokumenty z kolekcji „z”, czy też automatycznie łączy wszystkie w bazie danych jednocześnie?
user3413723,
Zastanawiasz się tylko, czy najnowsza wersja Spring Data MongoDB obsługuje 3.2?
gtiwari333
142

Ta strona na oficjalnej stronie mongodb zawiera dokładnie to pytanie:

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

Kiedy wyświetlamy naszą listę historii, musimy podać nazwę użytkownika, który opublikował historię. Gdybyśmy korzystali z relacyjnej bazy danych, moglibyśmy wykonać sprzężenie na użytkownikach i sklepach i uzyskać wszystkie nasze obiekty w jednym zapytaniu. Ale MongoDB nie obsługuje złączeń, więc czasami wymaga nieco denormalizacji. Oznacza to buforowanie atrybutu „nazwa użytkownika”.

Relacyjni puriści mogą już czuć się nieswojo, jakbyśmy naruszali jakieś uniwersalne prawo. Pamiętajmy jednak, że kolekcje MongoDB nie są równoważne tabelom relacyjnym; każdy służy unikatowemu celowi projektowemu. Znormalizowana tabela zapewnia atomowy, izolowany fragment danych. Dokument bardziej jednak reprezentuje obiekt jako całość. W przypadku serwisów społecznościowych można argumentować, że nazwa użytkownika jest nieodłączną częścią publikowanej historii.

William Stein
źródło
51
@dudelgrincen to zmiana paradygmatu od normalizacji i relacyjnych baz danych. Celem NoSQL jest bardzo szybkie odczytywanie i zapisywanie z bazy danych. Dzięki BigData będziesz mieć serwery aplikacji i serwerów frontendowych o niższej liczbie na DB. Oczekuje się, że wykonasz miliony transakcji na sekundę. Odciąż ciężkie podnoszenie z bazy danych i umieść je na poziomie aplikacji. Jeśli potrzebujesz dogłębnej analizy, uruchom zadanie integracyjne, które umieszcza dane w bazie danych OLAP. Zresztą nie powinieneś otrzymywać wielu głębokich zapytań ze swojego OLTP DBS.
Snowburnt
18
@dudelgrincen Powinienem również powiedzieć, że nie dotyczy to każdego projektu lub projektu. Jeśli masz coś, co działa w bazie danych typu SQL, po co to zmieniać? Jeśli nie możesz masować swojego schematu do pracy z noSQL, nie rób tego.
Snowburnt
9
Migracje i stale ewoluujące schematy są również o wiele łatwiejsze do zarządzania w systemie NoSQL.
justin
14
Co się stanie, jeśli użytkownik ma 3,540 postów w witrynie i zmienia swoją nazwę użytkownika w profilu? Czy każdy post powinien być aktualizowany o nową nazwę użytkownika?
Ivo Pereira,
2
@IvoPereira Tak i właśnie dlatego należy unikać modelowania danych w ten sposób. Jest artykuł, który wyjaśnia ten sam scenariusz i jego konsekwencje: Dlaczego nigdy nie powinieneś używać MongoDB
Omid
138

Możemy scalić / połączyć wszystkie dane tylko w jednym zbiorze za pomocą prostej funkcji w kilku wierszach za pomocą konsoli klienta mongodb, a teraz możemy wykonać żądane zapytanie. Poniżej pełnego przykładu

.- Autorzy:

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

.- Kategorie:

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

.- Książki

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

.- Wypożyczanie książek

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'
    }
]);

.- Magia:

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

.- Pobierz nowe dane kolekcji:

db.booksReloaded.find().pretty()

.- Odpowiedź :)

{
    "_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" : [ ]
}

Mam nadzieję, że te linie mogą ci pomóc.

Orlando Becerra
źródło
2
zastanawiam się, czy ten sam kod można uruchomić za pomocą doktryny mongodb?
abbood
4
Co się stanie, gdy jeden z obiektów odniesień zostanie zaktualizowany? Czy ta aktualizacja automatycznie odzwierciedla obiekt książki? A może ta pętla musi zostać uruchomiona ponownie?
balupton
14
Jest to w porządku, o ile twoje dane są małe. Ma przynieść zawartość każdej książki do twojego klienta, a następnie pobrać każdą kategorię, wypożyczanie i autorów jeden po drugim. W chwili, gdy twoje książki będą w tysiącach, będzie to bardzo wolno. Lepszą techniką byłoby prawdopodobnie użycie potoku agregacji i wysłanie scalonych danych do osobnego zbioru. Wrócę do tego ponownie. Dodaję odpowiedź.
Sandeep Giri
Czy potrafisz dostosować swój algorytm do tego innego przykładu? stackoverflow.com/q/32718079/287948
Peter Krauss
1
@ SandeepGiri jak mogę wykonać potok agregacyjny, ponieważ mam naprawdę bardzo intensywne dane w oddzielnej kolekcji, trzeba dołączyć?
Yassine Abdul-Rahman
38

Musisz to zrobić tak, jak to opisałeś. MongoDB jest nierelacyjną bazą danych i nie obsługuje połączeń.

Otto Allmendinger
źródło
4
Wydaje się, że wydajność nie jest mądra pochodząca z tła serwera SQL, ale może nie jest aż tak źle z dbaniem o dokument?
terjetyl
3
z tła serwera SQL również doceniłbym MongoDB biorąc „zestaw wyników” (z wybranymi zwróconymi polami) jako dane wejściowe dla nowego zapytania za jednym razem, podobnie jak zapytania zagnieżdżone w SQL
Stijn Sanders
1
@terjetyl Musisz to naprawdę zaplanować. Jakie pola zamierzasz prezentować na interfejsie, jeśli jest to ograniczona liczba w indywidualnym widoku, wówczas traktujesz je jako osadzone dokumenty. Kluczem jest to, że nie trzeba wykonywać połączeń. Jeśli chcesz przeprowadzić głęboką analizę, zrobisz to po fakcie w innej bazie danych. Uruchom zadanie, które przekształca dane w kostkę OLAP, aby uzyskać optymalną wydajność.
Snowburnt
4
Od wersji mongo 3.2 obsługiwane są lewe sprzężenia.
Somnath Muluk
18

Jak zauważyli inni, próbujesz utworzyć relacyjną bazę danych z żadnej relacyjnej bazy danych, której tak naprawdę nie chcesz, ale tak czy inaczej, jeśli masz przypadek, że musisz to zrobić tutaj, jest to rozwiązanie, którego możesz użyć. Najpierw szukamy znaleziska w kolekcji A (lub w twoim przypadku użytkowników), a następnie otrzymujemy każdy przedmiot jako obiekt, a następnie używamy właściwości obiektu (w twoim przypadku) do wyszukiwania w naszej drugiej kolekcji (w komentarzach w twoim przypadku), jeśli możemy to znaleźć, wtedy mamy dopasowanie i możemy go wydrukować lub coś z tym zrobić. Mam nadzieję, że to ci pomoże i powodzenia :)

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
źródło
Czy to nie znajdzie przedmiotu, nad którym aktualnie zapętlamy?
Skarlinski
18

Z odpowiednią kombinacją $ lookup , $ project i $ match możesz łączyć tabele wielokrotne na wielu parametrach. Wynika to z tego, że można je łączyć wielokrotnie.

Załóżmy, że chcemy wykonać następujące czynności ( odniesienie )

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

Krok 1: Połącz wszystkie tabele

możesz wyszukać tyle tabel, ile chcesz.

wyszukiwanie $ - jeden dla każdej tabeli w zapytaniu

$ odpręż się - ponieważ dane są poprawnie zdenormalizowane, w przeciwnym razie są pakowane w tablice

Kod Python

db.LeftTable.aggregate([
                        # connect all tables

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

                        ])

Krok 2: Zdefiniuj wszystkie warunki warunkowe

projekt $ : zdefiniuj tutaj wszystkie instrukcje warunkowe oraz wszystkie zmienne, które chcesz wybrać.

Kod Python ..

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

Krok 3: Dołącz do wszystkich warunków warunkowych

$ match - dołącz do wszystkich warunków używając OR lub AND itd. Może być ich wiele.

$ projekt : nie zdefiniuj wszystkich warunków warunkowych

Kod Python ..

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

                        ])

W ten sposób można wykonać prawie dowolną kombinację tabel, warunków i złączeń.

sbharti
źródło
17

Oto przykład „dołączenia” * do kolekcji aktorów i filmów :

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

Wykorzystuje .mapReduce() metodę

* join - alternatywa dla dołączania do baz danych zorientowanych na dokumenty

antytoksyczny
źródło
19
-1, to NIE łączy danych z dwóch kolekcji. Wykorzystuje dane z jednego zbioru (aktorów), obracając dane wokół siebie. Tak więc rzeczy, które były kluczami, są teraz wartościami, a wartości są teraz kluczami ... bardzo różniącymi się od JOIN.
Evan Teran
12
To dokładnie to, co musisz zrobić, MongoDB nie jest relacyjny, ale zorientowany na dokumenty. MapReduce pozwala grać z danymi o dużej wydajności (możesz użyć klastra itp.), Ale nawet w prostych przypadkach jest to bardzo przydatne!
Thomas Decaux,
14

Możesz dołączyć do dwóch kolekcji w Mongo za pomocą odnośnika, który jest oferowany w wersji 3.2. W twoim przypadku byłoby to zapytanie

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

lub możesz również dołączyć w odniesieniu do użytkowników, wówczas nastąpi niewielka zmiana, jak podano poniżej.

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

Będzie działał tak samo jak lewy i prawy łączenie w SQL.

jarry jafery
źródło
11

To zależy od tego, co próbujesz zrobić.

Obecnie masz skonfigurowaną znormalizowaną bazę danych, co jest w porządku, a sposób, w jaki to robisz, jest odpowiedni.

Istnieją jednak inne sposoby na zrobienie tego.

Możesz mieć kolekcję postów z osadzonymi komentarzami do każdego postu z odniesieniami do użytkowników, które możesz iteracyjnie zapytać o dostęp. Możesz przechowywać nazwę użytkownika z komentarzami, możesz przechowywać je wszystkie w jednym dokumencie.

W NoSQL chodzi o elastyczne schematy oraz bardzo szybkie czytanie i pisanie. W typowej farmie Big Data baza danych jest największym wąskim gardłem, masz mniej silników baz danych niż serwery aplikacji i front-end ... są one droższe, ale mocniejsze, a miejsce na dysku twardym jest stosunkowo tanie. Normalizacja bierze się z koncepcji oszczędzania miejsca, ale wiąże się z kosztem zmuszania baz danych do wykonywania skomplikowanych połączeń i sprawdzania integralności relacji, przeprowadzania operacji kaskadowych. Wszystko to oszczędza programistom problemów, jeśli właściwie zaprojektowali bazę danych.

W przypadku NoSQL, jeśli zaakceptujesz, że nadmiarowość i przestrzeń dyskowa nie są problemami ze względu na ich koszt (zarówno czas procesora wymagany do wykonania aktualizacji, jak i koszty dysku twardego do przechowywania dodatkowych danych), denormalizacja nie stanowi problemu (dla wbudowanych macierzy, które stają się setki tysięcy przedmiotów może to być problem z wydajnością, ale przez większość czasu to nie problem). Ponadto będziesz mieć kilka serwerów aplikacji i serwerów front-end dla każdego klastra bazy danych. Poproś ich, aby mocno podnieśli złączenia i pozwól serwerom bazy danych trzymać się odczytu i zapisu.

TL; DR: To, co robisz, jest w porządku, i są na to inne sposoby. Sprawdź wzorce modeli danych dokumentacji mongodb, aby znaleźć wspaniałe przykłady. http://docs.mongodb.org/manual/data-modeling/

Spalony śniegiem
źródło
8
„Normalizacja bierze się z koncepcji oszczędzania miejsca” - kwestionuję to. Normalizacja IMHO wynika z koncepcji unikania redundancji. Załóżmy, że przechowujesz imię użytkownika wraz z postem na blogu. Co jeśli ona wyjdzie za mąż? W nie znormalizowanym modelu będziesz musiał przedzierać się przez wszystkie posty i zmienić nazwę. W znormalizowanym modelu zwykle zmieniasz JEDEN rekord.
DanielKhan
@DanielKhan zapobieganie redundancji i oszczędzanie miejsca to podobne pojęcia, ale przy ponownej analizie zgadzam się, że redundancja jest podstawową przyczyną tego projektu. Przeredaguję. Dziękuję za notatkę.
Snowburnt
11

Istnieje specyfikacja obsługiwana przez wiele sterowników, która nazywa się DBRef.

DBRef jest bardziej formalną specyfikacją do tworzenia odniesień między dokumentami. DBRef (ogólnie) zawierają nazwę kolekcji, a także identyfikator obiektu. Większość programistów używa DBRefs tylko wtedy, gdy kolekcja może się zmieniać z jednego dokumentu do drugiego. Jeśli twoja kolekcja referencyjna zawsze będzie taka sama, powyższe instrukcje ręczne są bardziej wydajne.

Zaczerpnięte z dokumentacji MongoDB: Modele danych> Odniesienie do modelu danych> Odniesienia do bazy danych

Pikle
źródło
11

Wyszukiwanie $ (agregacja)

Wykonuje lewe zewnętrzne połączenie z niechronionym zbiorem w tej samej bazie danych, aby odfiltrować dokumenty z kolekcji „połączonej” w celu przetworzenia. Do każdego dokumentu wejściowego etap wyszukiwania $ dodaje nowe pole tablicy, którego elementami są pasujące dokumenty z kolekcji „połączonej”. Etap wyszukiwania $ przekazuje te przekształcone dokumenty do następnego etapu. Etap wyszukiwania $ ma następujące składnie:

Dopasowanie równości

Aby wykonać dopasowanie równości między polem z dokumentów wejściowych a polem z dokumentów z kolekcji „połączonej”, etap wyszukiwania $ ma następującą składnię:

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

Operacja odpowiadałaby następującej instrukcji pseudo-SQL:

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

URL Mongo

GoutamS
źródło
zapytanie podrzędne jest zupełnie inne niż łączenie, jeśli tabela po lewej stronie jest ogromna, zapytanie podrzędne oznacza, że ​​każdy wiersz musi wykonać samo zapytanie. stanie się bardzo wolny. Join jest bardzo szybki w sql.
yww325
8

W wersjach wcześniejszych niż 3.2.6 Mongodb nie obsługuje kwerendy dołączającej, tak jak mysql. poniżej rozwiązanie, które działa dla Ciebie.

 db.getCollection('comments').aggregate([
        {$match : {pid : 444}},
        {$lookup: {from: "users",localField: "uid",foreignField: "uid",as: "userData"}},
   ])
Anish Agarwal
źródło
4

Możesz uruchamiać zapytania SQL, w tym dołączyć do MongoDB z mongo_fdw z Postgres.

metdos
źródło
3

MongoDB nie zezwala na łączenia, ale do obsługi tego można użyć wtyczek. Sprawdź wtyczkę mongo-join. Jest najlepszy i już go użyłem. Możesz zainstalować go bezpośrednio za pomocą npm npm install mongo-join. Możesz sprawdzić pełną dokumentację z przykładami .

(++) naprawdę pomocne narzędzie, gdy musimy dołączyć do (N) kolekcji

(-) możemy zastosować warunki tylko na najwyższym poziomie zapytania

Przykład

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
źródło
2

Możesz to zrobić za pomocą potoku agregacji, ale pisanie go samemu jest trudne.

Możesz użyć mongo-join-querydo automatycznego utworzenia potoku agregacji na podstawie zapytania.

Tak wyglądałoby Twoje zapytanie:

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

Twój wynik uzyska obiekt użytkownika w uidpolu i możesz połączyć tyle poziomów, ile chcesz. Możesz wypełnić odniesienie do użytkownika, który odnosi się do zespołu, który odnosi się do czegoś innego itp.

Uwaga : Napisałem, mongo-join-queryaby rozwiązać dokładnie ten problem.

Marcelo Lazaroni
źródło
0

playORM może to zrobić za pomocą S-SQL (Scalable SQL), który po prostu dodaje partycjonowanie, dzięki czemu możesz wykonywać sprzężenia wewnątrz partycji.

Dean Hiller
źródło
-2

Nie, to nie wygląda na to, że robisz to źle. Połączenia MongoDB odbywają się „po stronie klienta”. Prawie tak jak powiedziałeś:

W tej chwili najpierw otrzymuję komentarze, które pasują do moich kryteriów, a następnie ustalam wszystkie numery UID w tym zestawie wyników, pobieram obiekty użytkownika i łączę je z wynikami komentarza. Wygląda na to, że robię to źle.

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.

To nie jest „prawdziwe” łączenie, ale w rzeczywistości jest o wiele bardziej przydatne niż łączenie SQL, ponieważ nie musisz zajmować się duplikatami wierszy dla „wielu” złączeń, zamiast dekorowania pierwotnie wybranego zestawu.

Na tej stronie jest wiele bzdur i FUD. Okazuje się, że 5 lat później MongoDB to wciąż coś.

Michael Cole
źródło
„nie musisz zajmować się duplikatami wierszy dla„ wielu ”złączeń jednostronnych - nie masz pojęcia, co przez to rozumiesz. Możesz wyjaśnić?
Mark Amery
1
@ MarkAmery, jasne. W SQL relacja nn zwróci zduplikowane wiersze. Np. Przyjaciele. Jeśli Bob przyjaźni się z Mary i Jane, dostaniesz 2 wiersze dla Boba: Bob, Mary i Bob, Jane. 2 Boby to kłamstwo, jest tylko jeden Bob. Dzięki dołączeniom po stronie klienta możesz zacząć od Boba i ozdobić go tak, jak lubisz: Bob, „Mary and Jane”. SQL pozwala ci to zrobić z podkwerendami, ale działa to na serwerze db, które można wykonać na kliencie.
Michael Cole,
-3

Myślę, że jeśli potrzebujesz znormalizowanych tabel danych - musisz wypróbować inne rozwiązania baz danych.

Ale ja popieram to rozwiązanie dla MOngo na Git Przy okazji, wstawia kod - ma nazwę filmu, ale identyfikator filmu noi .

Problem

Masz kolekcję aktorów z szeregiem filmów, które zrobili.

Chcesz wygenerować kolekcję filmów z zestawem aktorów w każdym z nich.

Niektóre przykładowe dane

 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'] });

Rozwiązanie

Musimy przeglądać każdy film w dokumencie aktora i emitować każdy film osobno.

Połów tutaj znajduje się w fazie redukcji. Nie możemy wyemitować tablicy z fazy redukcji, dlatego musimy zbudować tablicę aktorów w zwracanym dokumencie „wartość”.

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

Zauważ, że lista aktorów jest w rzeczywistości obiektem javascript zawierającym tablicę. Zauważ też, że mapa emituje tę samą strukturę.

Uruchom następujące polecenie, aby wykonać mapę / zmniejszyć, wyślij ją do kolekcji „przestawnej” i wydrukuj wynik:

printjson (db.actors.mapReduce (mapuj, zmniejsz, „przestaw”)); db.pivot.find (). forEach (printjson);

Oto przykładowe wyniki. Zauważ, że „Pretty Woman” i „Runaway Bride” mają zarówno „Richarda Gere”, jak i „Julię Roberts”.

{ "_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
źródło
Zauważ, że większość treści tej odpowiedzi (tj. Fragment w zrozumiałym języku angielskim) jest kopiowana z książki kucharskiej MongoDB w linku GitHub podanym przez odpowiadającego.
Mark Amery
-4

Możemy połączyć dwie kolekcje za pomocą zapytania podrzędnego mongoDB. Oto przykład: Komentarze--

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

Userss--

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

Zapytanie podrzędne MongoDB dla JOIN--

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

Uzyskaj wynik z nowo wygenerowanej kolekcji

db.newCommentUsers.find().pretty()

Wynik--

`{
    "_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"
        }
    ]
}`

Mam nadzieję, że to pomoże.

Kryszna
źródło
7
Dlaczego w zasadzie skopiowałeś tę niemal identyczną, roczną odpowiedź? stackoverflow.com/a/22739813/4186945
hackel