Come eseguo l’equivalente di Join SQL in MongoDB?

Come eseguo l’equivalente di Join SQL in MongoDB?

Ad esempio, supponiamo di avere due raccolte (utenti e commenti) e voglio estrarre tutti i commenti con pid = 444 insieme alle informazioni utente per ciascuno.

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

C’è un modo per tirare tutti i commenti con un certo campo (ad esempio … find ({pid: 444})) e le informazioni utente associate ad ogni commento in un colpo solo?

Al momento, per prima cosa sto ottenendo i commenti che corrispondono ai miei criteri, quindi calcolando tutti gli UID in quel set di risultati, ottenendo gli oggetti utente e unendoli con i risultati del commento. Sembra che sto sbagliando.

A partire da Mongo 3.2 le risposte a questa domanda non sono più corrette. Il nuovo operatore di ricerca $ aggiunto alla pipeline di aggregazione è essenzialmente identico a un join esterno a sinistra:

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

Dai documenti:

 { $lookup: { from: , localField: , foreignField: , as:  } } 

Ovviamente Mongo non è un database relazionale, e gli sviluppatori stanno facendo attenzione a raccomandare casi d’uso specifici per la ricerca $, ma almeno fino al 3.2 fare join è ora ansible con MongoDB.

Questa pagina sul sito ufficiale di mongodb risponde esattamente a questa domanda:

http://docs.mongodb.org/ecosystem/tutorial/model-data-for-ruby-on-rails/

Quando visualizziamo la nostra lista di storie, dovremo mostrare il nome dell’utente che ha pubblicato la storia. Se usassimo un database relazionale, potremmo eseguire un join su utenti e negozi e ottenere tutti i nostri oggetti in una singola query. Ma MongoDB non supporta i join e quindi, a volte, richiede un po ‘di denormalizzazione. Qui, questo significa memorizzare nella cache l’attributo ‘username’.

I puristi relazionali possono già sentirsi a disagio, come se stessimo violando qualche legge universale. Ma teniamo a mente che le collezioni MongoDB non sono equivalenti alle tabelle relazionali; ognuno serve un objective di design unico. Una tabella normalizzata fornisce una porzione di dati atomica e isolata. Un documento, tuttavia, rappresenta più da vicino un object nel suo complesso. Nel caso di un sito di notizie sociali, si può sostenere che un nome utente è intrinseco alla storia che viene pubblicata.

Possiamo unire / unire tutti i dati all’interno di una sola raccolta con una semplice funzione in poche righe usando la console del client mongodb, e ora potremmo essere in grado di eseguire la query desiderata. Sotto un esempio completo,

.- Autori:

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

.- Categorie:

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

.- Libri

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

.- Prestito del libro

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

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

.- Ottieni i nuovi dati della collezione:

 db.booksReloaded.find().pretty() 

.- Risposta 🙂

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

Spero che queste righe possano aiutarti.

Devi farlo come hai descritto. MongoDB è un database non relazionale e non supporta i join.

Ecco un esempio di un “join” * Collezioni di attori e film :

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

.mapReduce() metodo .mapReduce()

* join – un’alternativa per unire i database orientati ai documenti

Come altri hanno sottolineato, stai cercando di creare un database relazionale da nessun database relazionale che non vuoi assolutamente fare ma comunque, se hai un caso in cui devi farlo ecco una soluzione che puoi usare. Per prima cosa facciamo una ricerca foreach sulla raccolta A (o nel tuo caso utenti) e poi prendiamo ogni object come object, quindi usiamo la proprietà object (nel tuo caso uid) per cercare nella nostra seconda raccolta (nel tuo caso commenti) se possiamo trovarlo, quindi abbiamo una corrispondenza e possiamo stampare o fare qualcosa con esso. Spero che questo ti aiuti e buona fortuna 🙂

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

Dipende da cosa stai cercando di fare.

Attualmente lo hai impostato come un database normalizzato, che va bene, e il modo in cui lo stai facendo è appropriato.

Tuttavia, ci sono altri modi per farlo.

Si potrebbe avere una raccolta di post con commenti incorporati per ogni post con riferimenti agli utenti che è ansible interrogare in modo iterativo per ottenere. È ansible memorizzare il nome dell’utente con i commenti, è ansible memorizzarli tutti in un unico documento.

La cosa con NoSQL è progettata per schemi flessibili e lettura e scrittura molto veloci. In una tipica fattoria Big Data, il database è il collo di bottiglia più grande, si hanno meno motori di database di quelli di applicazioni e server front-end … sono più costosi ma più potenti, anche lo spazio su disco rigido è relativamente economico. La normalizzazione deriva dal concetto di cercare di risparmiare spazio, ma comporta un costo nel rendere i propri database eseguire complicati join e verificare l’integrità delle relazioni, eseguendo operazioni a cascata. Tutto ciò salva gli sviluppatori mal di testa se hanno progettato correttamente il database.

Con NoSQL, se si accetta che la ridondanza e lo spazio di archiviazione non sono problemi a causa del loro costo (sia nel tempo richiesto dal processore per eseguire aggiornamenti e costi del disco rigido per archiviare dati aggiuntivi), denormalizzare non è un problema (per gli array incorporati che diventano centinaia di migliaia di articoli può essere un problema di prestazioni, ma il più delle volte non è un problema). Inoltre, avrai diversi application e front-end per ogni cluster di database. Fagli fare il lavoro più impegnativo per i join e lascia che i server di database si attengano alla lettura e alla scrittura.

TL; DR: Quello che stai facendo va bene, e ci sono altri modi per farlo. Dai un’occhiata ai modelli dei modelli di dati della documentazione di mongodb per alcuni grandi esempi. http://docs.mongodb.org/manual/data-modeling/

C’è una specifica che molti driver supportano chiamata DBRef.

DBRef è una specifica più formale per la creazione di riferimenti tra documenti. DBRefs (generalmente) include un nome di collezione e un ID object. La maggior parte degli sviluppatori usa DBRefs solo se la collezione può cambiare da un documento all’altro. Se la tua collezione di riferimento sarà sempre la stessa, i riferimenti manuali descritti sopra sono più efficienti.

Tratto da MongoDB Documentazione: Modelli di dati> Riferimento modello dati> Riferimenti database

Puoi unire due raccolte in Mongo usando la ricerca che è disponibile nella versione 3.2. Nel tuo caso la domanda sarebbe

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

oppure puoi anche unirti agli utenti, quindi ci sarà un piccolo cambiamento come indicato di seguito.

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

Funzionerà esattamente come il join sinistro e destro in SQL.

Con la giusta combinazione di ricerca $ , $ project e $ match , puoi unire tabelle multiple su più parametri. Questo perché possono essere concatenati più volte.

Supponiamo di voler seguire ( riferimento )

 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 

Passaggio 1: collega tutti i tavoli

puoi $ cercare quante tabelle vuoi.

$ ricerca – uno per ogni tabella nella query

$ unwind – perché i dati sono denormalizzati correttamente, altrimenti racchiusi negli array

Codice Python ..

 db.LeftTable.aggregate([ # connect all tables {"$lookup": { "from": "RightTable", "localField": "ID", "foreignField": "ID", "as": "R" }}, {"$unwind": "R"} ]) 

Passaggio 2: definire tutti i condizionali

$ project : definisce qui tutte le istruzioni condizionali, oltre a tutte le variabili che desideri selezionare.

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

Step 3: Unisciti a tutti i condizionali

$ match – unisciti a tutte le condizioni usando OR o AND etc. Ci possono essere multipli di questi.

$ project : undefine a tutti i condizionali

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

Praticamente qualsiasi combinazione di tabelle, condizionali e join può essere eseguita in questo modo.

Prima di 3.2.6 , Mongodb non supporta la query di join come mysql. sotto la soluzione che funziona per te.

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

È ansible eseguire query SQL incluso join su MongoDB con mongo_fdw da Postgres.

MongoDB non consente i join, ma puoi usare i plugin per gestirli. Controlla il plugin mongo-join. È il migliore e l’ho già usato. Puoi installarlo usando npm direttamente come questo npm install mongo-join . È ansible controllare la documentazione completa con esempi .

(++) strumento davvero utile quando abbiamo bisogno di unire (N) collezioni

(-) possiamo applicare le condizioni solo al livello più alto della query

Esempio

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

$ ricerca (aggregazione)

Esegue un join esterno sinistro in una raccolta non definita nello stesso database per filtrare i documenti dalla raccolta “unita” per l’elaborazione. A ciascun documento di input, la fase di ricerca $ aggiunge un nuovo campo array i cui elementi sono i documenti corrispondenti dalla raccolta “unita”. La fase di ricerca $ passa questi documenti rimodellati allo stadio successivo. La fase di ricerca $ ha le seguenti syntax:

Partita di uguaglianza

Per eseguire una corrispondenza di uguaglianza tra un campo dei documenti di input con un campo dei documenti della raccolta “unita”, la fase di ricerca $ ha la seguente syntax:

 { $lookup: { from: , localField: , foreignField: , as:  } } 

L’operazione corrisponderebbe alla seguente istruzione pseudo-SQL:

 SELECT *,  FROM collection WHERE  IN (SELECT  FROM  WHERE  ); 

URL Mongo

playORM può farlo per te usando S-SQL (Scalable SQL) che aggiunge solo partizioni in modo tale che tu possa fare join all’interno delle partizioni.

Puoi farlo usando la pipeline di aggregazione, ma è difficile scriverlo da solo.

È ansible utilizzare mongo-join-query per creare automaticamente la pipeline di aggregazione dalla query.

Ecco come sarà la tua richiesta:

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

Il risultato dovrebbe avere l’object utente nel campo uid e puoi colbind tutti i livelli nel modo desiderato. È ansible popolare il riferimento all’utente, che fa riferimento a una squadra, che fa riferimento a qualcos’altro, ecc.

Disclaimer : Ho scritto mongo-join-query per affrontare questo esatto problema.

Penso che, se hai bisogno di tabelle di dati normalizzate, devi provare altre soluzioni di database.

Però, ho usato quella soluzione per MOngo su Git. A proposito, nel codice degli inserti – ha il nome del film, ma l’ID del film noi .

Problema

Hai una collezione di attori con una serie di film che hanno fatto.

Vuoi generare una collezione di film con una serie di attori in ciascuno.

Alcuni dati di esempio

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

Soluzione

Abbiamo bisogno di scorrere ogni film nel documento attore ed emettere ogni film singolarmente.

Il fermo qui è nella fase di riduzione. Non possiamo emettere una matrice dalla fase di riduzione, quindi dobbiamo build una matrice di attori all’interno del documento “valore” che viene restituito.

Il codice

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

Si noti che actor_list è in realtà un object javascript che contiene un array. Si noti inoltre che la mappa emette la stessa struttura.

Esegui quanto segue per eseguire la mappa / ridurre, inviarla alla raccolta “pivot” e stampare il risultato:

printjson (db.actors.mapReduce (map, reduce, “pivot”)); db.pivot.find () foreach (printjson).;

Ecco l’output del campione, nota che “Pretty Woman” e “Runaway Bride” hanno sia “Richard Gere” che “Julia 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" ] } } 

No, non sembra che tu stia sbagliando. I join MongoDB sono “lato client”. Praticamente come hai detto tu:

Al momento, per prima cosa sto ottenendo i commenti che corrispondono ai miei criteri, quindi calcolando tutti gli UID in quel set di risultati, ottenendo gli oggetti utente e unendoli con i risultati del commento. Sembra che sto sbagliando.

 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. 

Non è un join “reale”, ma in realtà è molto più utile di un join SQL perché non devi occuparti di righe duplicate per “molti” join, invece di decorare il set originariamente selezionato.

C’è un sacco di sciocchezze e FUD in questa pagina. Risulta 5 anni dopo MongoDB è ancora una cosa.

Possiamo unire due raccolte utilizzando la sub query mongoDB. Ecco un esempio, Commenti –

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

Sottopassaggio di MongoDB per JOIN–

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

Ottieni risultato dalla raccolta appena generata–

 db.newCommentUsers.find().pretty() 

Risultato–

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

Spero che questo possa aiutare.