unwind two multiple inner fields example ejemplo join mongodb normalization

two - ¿Cómo realizo el SQL Join equivalente en MongoDB?



mongodb unwind multiple fields (19)

¿Cómo realizo el SQL Join equivalente en MongoDB?

Por ejemplo, digamos que tiene dos colecciones (usuarios y comentarios) y quiero extraer todos los comentarios con pid = 444 junto con la información del usuario para cada uno.

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

¿Hay una manera de extraer todos los comentarios con un campo determinado (por ejemplo, ... buscar ({pid: 444})) y la información de usuario asociada con cada comentario de una sola vez?

En este momento, primero recibo los comentarios que coinciden con mis criterios, luego entiendo todos los uid en ese conjunto de resultados, obteniendo los objetos del usuario y fusionándolos con los resultados del comentario. Parece que lo estoy haciendo mal.


Creo que, si necesita tablas de datos normalizadas, debe probar otras soluciones de base de datos.

Pero he encontrado esa solución para MOngo en https://github.com/mongodb/cookbook/blob/master/content/patterns/pivot.txt Por cierto, en el código de inserción, tiene el nombre de la película, pero el ID de la película noi .

Problema

Tienes una colección de actores con una gran variedad de películas que han hecho.

Quieres generar una colección de películas con una variedad de actores en cada una.

Algunos datos de muestra

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

Solución

Necesitamos recorrer cada película en el documento Actor y emitir cada Película individualmente.

La captura aquí está en la fase de reducción. No podemos emitir una matriz desde la fase de reducción, por lo que debemos crear una matriz de Actores dentro del documento de "valor" que se devuelve.

El código

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

Observe cómo actor_list es en realidad un objeto javascript que contiene una matriz. Observe también que el mapa emite la misma estructura.

Ejecute lo siguiente para ejecutar el mapa / reducir, envíelo a la colección "dinámica" e imprima el resultado:

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

Aquí está la salida de muestra, tenga en cuenta que "Pretty Woman" y "Runaway Bride" tienen tanto "Richard Gere" como "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" ] } }


$ búsqueda (agregación)

Realiza una unión externa izquierda a una colección no compartida en la misma base de datos para filtrar los documentos de la colección "unida" para su procesamiento. Para cada documento de entrada, la etapa de búsqueda $ agrega un nuevo campo de matriz cuyos elementos son los documentos coincidentes de la colección "unida". La etapa de búsqueda $ pasa estos documentos remodelados a la siguiente etapa. La etapa $ lookup tiene las siguientes sintaxis:

Partido de igualdad

Para realizar una coincidencia de igualdad entre un campo de los documentos de entrada y un campo de los documentos de la colección "unida", la etapa de búsqueda $ tiene la siguiente sintaxis:

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

La operación correspondería a la siguiente declaración 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 de Mongo


A partir de Mongo 3.2, las respuestas a esta pregunta ya no son correctas. El nuevo operador de $ $ agregado al canal de agregación es esencialmente idéntico a una combinación externa izquierda:

docs.mongodb.org/master/reference/operator/aggregation/lookup/…

De la documentación:

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

Por supuesto, Mongo no es una base de datos relacional, y los desarrolladores están teniendo cuidado de recomendar casos de uso específicos para $ buscar, pero al menos a partir de 3.2 ahora es posible con MongoDB.


Antes de la versión 3.2.6 , Mongodb no admite la consulta de unión como mysql. A continuación la solución que funciona para usted.

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



Como otros han señalado, usted está tratando de crear una base de datos relacional a partir de ninguna base de datos relacional que realmente no quiere hacer, pero de todos modos, si tiene un caso que debe hacer aquí, esta es una solución que puede usar. Primero hacemos una búsqueda foreach en la colección A (o en los usuarios de su caso) y luego obtenemos cada elemento como un objeto, luego usamos la propiedad del objeto (en su caso, uid) para buscar en nuestra segunda colección (en su caso, comentarios) si podemos encontrarlo, entonces tenemos una coincidencia y podemos imprimir o hacer algo con él. Espero que esto te ayude y buena suerte :)

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


Con la combinación correcta de $ lookup , $ project y $ match , puede unir varias tablas en múltiples parámetros. Esto se debe a que se pueden encadenar varias veces.

Supongamos que queremos hacer lo siguiente ( reference )

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

Paso 1: Vincular todas las tablas

Puedes $ buscar tantas tablas como quieras.

$ búsqueda - una para cada tabla en la consulta

$ desenrollar - porque los datos se han normalizado correctamente, de lo contrario se envuelven en matrices

Código Python ..

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

Paso 2: Definir todos los condicionales.

$ proyecto : define todas las declaraciones condicionales aquí, además de todas las variables que te gustaría seleccionar.

Código 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 }} ])

Paso 3: Unir todos los condicionales.

$ match - une todas las condiciones usando OR u AND, etc. Puede haber múltiplos de estas.

$ proyecto : undefine todos los condicionales

Código 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 }} ])

Casi cualquier combinación de tablas, condicionales y combinaciones se puede hacer de esta manera.


Depende de lo que estés tratando de hacer.

Actualmente lo tiene configurado como una base de datos normalizada, lo cual está bien, y la forma en que lo está haciendo es apropiada.

Sin embargo, hay otras formas de hacerlo.

Podría tener una colección de publicaciones que haya incorporado comentarios para cada publicación con referencias a los usuarios que puede consultar iterativamente para obtener. Puede almacenar el nombre del usuario con los comentarios, puede almacenarlos todos en un documento.

Lo que pasa con NoSQL es que está diseñado para esquemas flexibles y lectura y escritura muy rápidas. En una granja de Big Data típica, la base de datos es el mayor cuello de botella, tiene menos motores de base de datos que los servidores front-end y de aplicaciones ... son más caros pero más potentes, además el espacio en el disco duro es muy barato en comparación. La normalización proviene del concepto de tratar de ahorrar espacio, pero conlleva el costo de hacer que sus bases de datos realicen combinaciones complicadas y verificar la integridad de las relaciones, realizando operaciones en cascada. Todo lo cual ahorra a los desarrolladores algunos dolores de cabeza si diseñan la base de datos correctamente.

Con NoSQL, si acepta que la redundancia y el espacio de almacenamiento no son problemas debido a su costo (tanto en el tiempo de procesador requerido para hacer las actualizaciones como en los costos del disco duro para almacenar datos adicionales), la desnormalización no es un problema (para arreglos integrados que se convierten en cientos de miles de elementos puede ser un problema de rendimiento, pero la mayoría de las veces no es un problema). Además, tendrá varias aplicaciones y servidores front-end para cada clúster de base de datos. Pídales que hagan el trabajo pesado de las uniones y que los servidores de bases de datos se adhieran a la lectura y la escritura.

TL; DR: Lo que estás haciendo está bien, y hay otras formas de hacerlo. Echa un vistazo a los patrones del modelo de datos de la documentación de mongodb para ver algunos ejemplos excelentes. http://docs.mongodb.org/manual/data-modeling/


Esta página en el sitio oficial de mongodb aborda exactamente esta pregunta:

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

Cuando mostremos nuestra lista de historias, tendremos que mostrar el nombre del usuario que publicó la historia. Si estuviéramos utilizando una base de datos relacional, podríamos realizar una unión en usuarios y tiendas, y obtener todos nuestros objetos en una sola consulta. Pero MongoDB no admite uniones y, por lo tanto, a veces, requiere un poco de desnormalización. Aquí, esto significa almacenar en caché el atributo ''nombre de usuario''.

Los puristas relacionales ya pueden sentirse incómodos, como si estuviéramos violando alguna ley universal. Pero tengamos en cuenta que las colecciones de MongoDB no son equivalentes a las tablas relacionales; Cada uno sirve un objetivo de diseño único. Una tabla normalizada proporciona una parte atómica, aislada de datos. Un documento, sin embargo, representa más de cerca un objeto en su conjunto. En el caso de un sitio de noticias sociales, se puede argumentar que un nombre de usuario es intrínseco a la historia que se publica.


Hay una especificación que muchos controladores admiten que se llama DBRef.

DBRef es una especificación más formal para crear referencias entre documentos. DBRefs (generalmente) incluye un nombre de colección así como una identificación de objeto. La mayoría de los desarrolladores solo usan DBRefs si la colección puede cambiar de un documento a otro. Si su colección a la que se hace referencia siempre será la misma, las referencias del manual descrito anteriormente son más eficientes.

Tomado de la documentación de MongoDB: Modelos de datos> Referencia del modelo de datos> Referencias de bases de datos


MongoDB no permite uniones, pero puede usar complementos para manejar eso. Compruebe el complemento mongo-join. Es lo mejor y ya lo he usado. Puede instalarlo usando npm directamente como este npm install mongo-join . Puedes consultar la documentación completa con ejemplos .

(++) herramienta realmente útil cuando necesitamos unir (N) colecciones

(-) podemos aplicar condiciones solo en el nivel superior de la consulta

Ejemplo

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


No, no parece que lo estés haciendo mal. MongoDB se une a "lado del cliente". Al igual que usted dijo:

En este momento, primero recibo los comentarios que coinciden con mis criterios, luego entiendo todos los uid en ese conjunto de resultados, obteniendo los objetos del usuario y fusionándolos con los resultados del comentario. Parece que lo estoy haciendo mal.

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.

No es una combinación "real", pero en realidad es mucho más útil que una combinación de SQL porque no tiene que lidiar con filas duplicadas para "muchas" uniones de lados, en lugar de decorar el conjunto seleccionado originalmente.

Hay muchas tonterías y FUD en esta página. Resulta que 5 años después, MongoDB sigue siendo una cosa.


Podemos combinar / unir todos los datos dentro de una sola colección con una función fácil en pocas líneas usando la consola del cliente mongodb, y ahora podremos realizar la consulta deseada. Abajo un ejemplo completo,

.- Autores:

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

.- Categorías:

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

.- Libros

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

.- préstamo de libros

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

.- Obtener los nuevos datos de colección:

db.booksReloaded.find().pretty()

.- Respuesta :)

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

Espero que estas líneas puedan ayudarte.


Podemos fusionar dos colecciones utilizando la sub consulta mongoDB. Aquí hay un ejemplo, Comentarios

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

Usuarios--

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

Sub consulta de MongoDB para JOIN--

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

Obtener el resultado de la nueva colección generada--

db.newCommentUsers.find().pretty()

Resultado--

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

Espero que esto ayude.


Puede ejecutar consultas SQL, incluido unirse en MongoDB con mongo_fdw de Postgres.


Puede hacerlo utilizando la canalización de agregación, pero es un dolor escribirlo usted mismo.

Puede usar mongo-join-query para crear la canalización de agregación automáticamente desde su consulta.

Así es como se vería tu consulta:

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

Su resultado tendría el objeto de usuario en el campo uid y puede vincular tantos niveles como desee. Puede rellenar la referencia al usuario, lo que hace referencia a un Equipo, lo que hace referencia a otra cosa, etc.

Descargo de responsabilidad : escribí mongo-join-query para abordar este problema exacto.


Puede unir dos colecciones en Mongo usando la búsqueda que se ofrece en la versión 3.2. En tu caso la consulta sería

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

o también puede unirse con respecto a los usuarios, entonces habrá un pequeño cambio como se indica a continuación.

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

Funcionará igual que la izquierda y la derecha se unen en SQL.


Tienes que hacerlo como lo describiste. MongoDB es una base de datos no relacional y no admite uniones.


playORM puede hacerlo por usted usando S-SQL (SQL escalable) que solo agrega particiones de manera que puede hacer uniones dentro de las particiones.