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

498

¿Cómo realizo el equivalente de SQL Join 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 alguna manera de extraer todos los comentarios con un determinado campo (por ejemplo, ... find ({pid: 444})) y la información del usuario asociada con cada comentario de una vez?

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

El desconocido
fuente
35
La última respuesta a esta pregunta es probablemente la más relevante, ya que MongoDB 3.2+ implementó una solución de unión llamada $ lookup. Pensé que lo empujaría aquí porque tal vez no todos leerán hasta el fondo. stackoverflow.com/a/33511166/2593330
thefourtheye
66
Correcto, $ búsqueda se introdujo en MongoDB 3.2. Los detalles se pueden encontrar en docs.mongodb.org/master/reference/operator/aggregation/lookup/…
NDB

Respuestas:

306

A partir de Mongo 3.2, las respuestas a esta pregunta ya no son correctas. El nuevo operador de búsqueda $ agregado a la canalización de agregación es esencialmente idéntico a una unión externa izquierda:

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

De los documentos:

{
   $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 $ lookup, pero al menos a partir de 3.2 ahora es posible hacer uniones con MongoDB.

Clayton Gulick
fuente
@clayton: ¿Qué tal más de dos colecciones?
Dipen Dedania
1
@DipenDedania simplemente agrega etapas adicionales de búsqueda de $ a la tubería de agregación.
Clayton Gulick
No puedo unirme a ningún campo en la matriz en la colección izquierda con su identificación correspondiente en la colección derecha. ¿Alguien puede ayudarme?
Prateek Singh
1
Estoy un poco confundido acerca de esto: ¿hay alguna forma de especificar que solo desea ciertos documentos en la colección "desde", o se une automáticamente a todos en la base de datos a la vez?
user3413723
¿Me pregunto si el último Spring Data MongoDB es compatible con 3.2?
gtiwari333
142

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

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

Cuando mostramos nuestra lista de historias, necesitaremos mostrar el nombre del usuario que publicó la historia. Si estuviéramos usando 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 combinaciones 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 pueden sentirse incómodos ya, como si estuviéramos violando alguna ley universal. Pero tengamos en cuenta que las colecciones MongoDB no son equivalentes a las tablas relacionales; cada uno sirve un objetivo de diseño único. Una tabla normalizada proporciona una porción de datos atómica y aislada. Sin embargo, un documento representa más de cerca un objeto como un todo. 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.

William Stein
fuente
51
@dudelgrincen es un cambio de paradigma de la normalización y las bases de datos relacionales. El objetivo de un NoSQL es leer y escribir desde la base de datos muy rápidamente. Con BigData, tendrás montones de aplicaciones y servidores front-end con números más bajos en bases de datos. Se espera que haga millones de transacciones por segundo. Descargue el trabajo pesado de la base de datos y póngalo en el nivel de la aplicación. Si necesita un análisis profundo, ejecuta un trabajo de integración que coloca sus datos en una base de datos OLAP. De todos modos, no debería recibir muchas consultas profundas de sus dbs OLTP.
Snowburnt el
18
@dudelgrincen También debería decir que no es para cada proyecto o diseño. Si tiene algo que funciona en una base de datos de tipo SQL, ¿por qué cambiarlo? Si no puede dar masajes a su esquema para trabajar con noSQL, entonces no lo haga.
Quemado por la nieve el
99
Las migraciones y los esquemas en constante evolución también son mucho más fáciles de administrar en un sistema NoSQL.
justin
14
¿Qué sucede si el usuario tiene 3.540 publicaciones en el sitio web y cambia su nombre de usuario en el perfil? ¿Deben actualizarse todas las publicaciones con el nuevo nombre de usuario?
Ivo Pereira
2
@IvoPereira Sí, y es exactamente por eso que uno debe evitar modelar datos de esta manera. Hay un artículo que explica el mismo escenario y sus consecuencias: por qué nunca deberías usar MongoDB
Omid
138

Podemos fusionar / unir todos los datos dentro de una sola colección con una función fácil en pocas líneas utilizando la consola del cliente mongodb, y ahora podríamos realizar la consulta deseada. Debajo de 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);
    }
);

.- Obtenga los nuevos datos de recopilació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 te puedan ayudar.

Orlando Becerra
fuente
2
Me pregunto si este mismo código se puede ejecutar utilizando la doctrina mongodb.
Abbood
44
¿Qué sucede cuando uno de los objetos de referencia recibe una actualización? ¿Esa actualización se refleja automáticamente en el objeto del libro? ¿O ese ciclo necesita ejecutarse nuevamente?
balupton
14
Esto está bien siempre que sus datos sean pequeños. Traerá el contenido de cada libro a su cliente y luego buscará cada categoría, préstamo y autores uno por uno. En el momento en que sus libros están en miles, esto iría realmente muy lento. Una mejor técnica probablemente sería utilizar la canalización de agregación y generar los datos combinados en una colección separada. Déjame volver a eso otra vez. Agregaré eso una respuesta.
Sandeep Giri
¿Puedes adaptar tu algoritmo a este otro ejemplo? stackoverflow.com/q/32718079/287948
Peter Krauss
1
@SandeepGiri, ¿cómo puedo hacer la tubería agregada ya que tengo datos realmente intensivos en una colección separada?
Yassine Abdul-Rahman
38

Tienes que hacerlo de la manera que describiste. MongoDB es una base de datos no relacional y no admite combinaciones.

Otto Allmendinger
fuente
44
Parece un rendimiento incorrecto procedente de un fondo de servidor sql, pero ¿quizás no sea tan malo con un documento db?
terjetyl
3
desde un fondo de servidor sql también, agradecería que MongoDB tomara un 'conjunto de resultados' (con campos devueltos seleccionados) como entrada para una nueva consulta de una vez, al igual que las consultas anidadas en SQL
Stijn Sanders
1
@terjetyl Tienes que planearlo realmente. ¿Qué campos presentará en el front-end? Si es una cantidad limitada en una vista individual, entonces los toma como documentos incrustados. La clave es no tener que hacer uniones. Si desea hacer un análisis profundo, hágalo después del hecho en otra base de datos. Ejecute un trabajo que transforme los datos en un cubo OLAP para un rendimiento óptimo.
Snowburnt el
44
Desde la versión mongo 3.2, se admiten combinaciones izquierdas.
Somnath Muluk
18

Como otros han señalado, está tratando de crear una base de datos relacional a partir de ninguna base de datos relacional que realmente no desea hacer, pero de todos modos, si tiene un caso en el que tiene que hacer esto, aquí hay una solución que puede usar. Primero hacemos una búsqueda foreach en la colección A (o en su caso, los usuarios) 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 los comentarios de su caso) si podemos encontrarlo, entonces tenemos una coincidencia y podemos imprimir o hacer algo con ella. 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
    }
});
grepit
fuente
¿No encontrará esto el artículo en el que estamos haciendo un bucle?
Skarlinski
18

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 ( referencia )

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: enlace todas las tablas

puede $ buscar tantas tablas como desee.

$ lookup : uno para cada tabla en la consulta

$ unwind - porque los datos están desnormalizados correctamente, de lo contrario están envueltos en matrices

Código de 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 : defina todas las declaraciones condicionales aquí, además de todas las variables que desea 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: une todos los condicionales

$ match : une todas las condiciones con OR o AND, etc. Puede haber varios de estos.

$ proyecto : indefinir 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.

sbharti
fuente
17

Aquí hay un ejemplo de una colección "unirse" * Actores y películas :

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

Hace uso de .mapReduce() método

* join : una alternativa para unirse en bases de datos orientadas a documentos

antitóxico
fuente
19
-1, esto NO está uniendo datos de dos colecciones. Está utilizando datos de una sola colección (actores) que giran datos. De modo que las cosas que eran claves ahora son valores y los valores ahora son claves ... muy diferentes a un JOIN.
Evan Teran
12
Esto es exactamente lo que tienes que hacer, MongoDB no es relacional sino orientado a documentos. MapReduce permite jugar con datos con gran rendimiento (puede usar clúster, etc.), pero incluso en casos simples, ¡es muy útil!
Thomas Decaux
14

Puedes unir dos colecciones en Mongo usando la búsqueda que se ofrece en la versión 3.2. En su 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 unión izquierda y derecha en SQL.

Jarry Jafery
fuente
11

Depende de lo que intentes hacer.

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

Sin embargo, hay otras formas de hacerlo.

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

Lo que ocurre con NoSQL es que está diseñado para esquemas flexibles y lectura y escritura muy rápidas. En una granja típica de Big Data, la base de datos es el mayor cuello de botella, tiene menos motores de base de datos que los servidores de aplicaciones y front-end ... son más caros pero más potentes, también el espacio en el disco duro es muy barato en comparación. La normalización proviene del concepto de tratar de ahorrar espacio, pero tiene un costo para hacer que sus bases de datos realicen uniones complicadas y verifiquen la integridad de las relaciones, realizando operaciones en cascada. Todo lo cual ahorra a los desarrolladores algunos dolores de cabeza si diseñaron 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 realizar actualizaciones como en los costos del disco duro para almacenar datos adicionales), la desnormalización no es un problema (para los arreglos integrados que se convierten 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 grupo de bases de datos. Pídales que hagan el trabajo pesado de las uniones y que los servidores de bases de datos se dediquen a leer y escribir.

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

Quemada por la nieve
fuente
8
"La normalización proviene del concepto de tratar de ahorrar espacio" Cuestiono esto. La normalización de la OMI viene del concepto de evitar la redundancia. Supongamos que almacena el nombre de un usuario junto con una publicación de blog. ¿Y si ella se casa? En un modelo no normalizado, tendrá que leer todas las publicaciones y cambiar el nombre. En un modelo normalizado, generalmente cambia UN registro.
DanielKhan
@DanielKhan que evita la redundancia y ahorra espacio son conceptos similares, pero en un nuevo análisis estoy de acuerdo, la redundancia es la causa principal de este diseño. Voy a reformularlo. Gracias por la nota
Quemado por la nieve el
11

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

DBRef es una especificación más formal para crear referencias entre documentos. Los DBRefs (generalmente) incluyen 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 referenciada siempre será la misma, las referencias manuales descritas 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

Pickels
fuente
11

$ búsqueda (agregación)

Realiza una unión externa izquierda a una colección no sombreada en la misma base de datos para filtrar 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 reformados a la siguiente etapa. La etapa de búsqueda $ tiene las siguientes sintaxis:

Igualdad

Para realizar una coincidencia de igualdad entre un campo de los documentos de entrada con 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 instrucció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

GoutamS
fuente
la subconsulta es totalmente diferente a la combinación, si la tabla del lado izquierdo es enorme, la subconsulta significa que cada fila tiene que hacer una consulta. se volverá muy lento. unirse es muy rápido en sql.
yww325
8

Antes de 3.2.6 , Mongodb no admite consultas de combinación como mysql. debajo de la solución que funciona para usted.

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

Puede ejecutar consultas SQL, incluida la unión en MongoDB con mongo_fdw desde Postgres.

metdos
fuente
3

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

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

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


            });

    });
Amine_Dev
fuente
2

Puede hacerlo utilizando la canalización de agregación, pero es difícil escribirlo usted mismo.

Puede usar mongo-join-querypara crear la canalización de agregación automáticamente a partir de su consulta.

Así es como se vería su 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 uidcampo y puede vincular tantos niveles como desee. Puede completar la referencia al usuario, que hace referencia a un Equipo, que hace referencia a otra cosa, etc.

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

Marcelo Lazaroni
fuente
0

playORM puede hacerlo por usted usando S-SQL (SQL escalable) que simplemente agrega particiones de modo que pueda hacer uniones dentro de particiones.

Dean Hiller
fuente
-2

No, no parece que lo estés haciendo mal. Las uniones de MongoDB son "del lado del cliente". Más o menos como dijiste:

En este momento, primero recibo los comentarios que coinciden con mis criterios, luego descifro todos los uid en ese conjunto de resultados, obtengo los objetos del usuario y los combino 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 unión "real", pero en realidad es mucho más útil que una unión SQL porque no tiene que lidiar con filas duplicadas para "muchas" uniones laterales, en lugar de decorar el conjunto originalmente seleccionado.

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

Michael Cole
fuente
"no tiene que lidiar con filas duplicadas para" muchas "uniones laterales", no tengo idea de lo que quiere decir con esto. ¿Puedes aclarar?
Mark Amery
1
@ MarkAmery, claro. En SQL, una relación nn devolverá filas duplicadas. Por ejemplo, amigos. Si Bob es amigo de Mary y Jane, obtendrá 2 filas para Bob: Bob, Mary y Bob, Jane. 2 Bobs es una mentira, solo hay un Bob. Con las uniones del lado del cliente, puede comenzar con Bob y decorar a su gusto: Bob, "Mary and Jane". SQL le permite hacer esto con subconsultas, pero eso está haciendo un trabajo en el servidor db que podría hacerse en el cliente.
Michael Cole
-3

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

Pero he encontrado esa solución para MOngo en Git. Por cierto, en código de inserción: tiene el nombre de la película, pero no la identificación de la película .

Problema

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

Desea 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 del 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 construir 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 "pivote" 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 "Richard Gere" y "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" ] } }

Max Sherbakov
fuente
Tenga en cuenta que la mayor parte del contenido de esta respuesta (es decir, el bit que está en inglés comprensible) se copia del libro de cocina de MongoDB en el enlace de GitHub que proporcionó el contestador.
Mark Amery
-4

Podemos fusionar dos colecciones utilizando la subconsulta 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"  }])

Subconsulta de MongoDB para JOIN--

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

Obtenga el resultado de la Colección recié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.

Krishna
fuente
77
¿Por qué básicamente copiaste esta respuesta casi idéntica de hace un año? stackoverflow.com/a/22739813/4186945
hackel