¿Qué es el operador de desenrollado $ en MongoDB?

103

Este es mi primer día con MongoDB, así que no se preocupe :)

No entiendo al $unwindoperador, tal vez porque el inglés no es mi lengua materna.

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

El operador del proyecto es algo que puedo entender, supongo (es como SELECT, ¿no?). Pero luego, $unwind(citando) devuelve un documento por cada miembro de la matriz desenrollada dentro de cada documento fuente .

¿Es esto como un JOIN? En caso afirmativo, cómo el resultado de $project(a _id, author, titley tagscampos) se pueden comparar con la tagsmatriz?

NOTA : He tomado el ejemplo del sitio web de MongoDB, no conozco la estructura de la tagsmatriz. Creo que es una simple matriz de nombres de etiquetas.

gremo
fuente

Respuestas:

235

En primer lugar, ¡bienvenido a MongoDB!

Lo que hay que recordar es que MongoDB emplea un enfoque "NoSQL" para el almacenamiento de datos, así que olvídese de los pensamientos de selecciones, uniones, etc. La forma en que almacena sus datos es en forma de documentos y colecciones, lo que permite un medio dinámico de agregar y obtener los datos de sus ubicaciones de almacenamiento.

Dicho esto, para comprender el concepto detrás del parámetro $ desenrollar, primero debe comprender qué dice el caso de uso que está tratando de citar. El documento de ejemplo de mongodb.org es el siguiente:

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

Observe cómo las etiquetas son en realidad una matriz de 3 elementos, en este caso, "divertido", "bueno" y "divertido".

Lo que hace $ desenrollar es permitirle despegar un documento para cada elemento y devolver ese documento resultante. Para pensar en esto en un enfoque clásico, sería el equivalente de "para cada elemento en la matriz de etiquetas, devolver un documento con sólo ese elemento".

Por lo tanto, el resultado de ejecutar lo siguiente:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

devolvería los siguientes documentos:

{
     "result" : [
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "good"
             },
             {
                     "_id" : ObjectId("4e6e4ef557b77501a49233f6"),
                     "title" : "this is my title",
                     "author" : "bob",
                     "tags" : "fun"
             }
     ],
     "OK" : 1
}

Tenga en cuenta que lo único que cambia en la matriz de resultados es lo que se devuelve en el valor de las etiquetas. Si necesita una referencia adicional sobre cómo funciona esto, he incluido un enlace aquí . Espero que esto ayude, y buena suerte con su incursión en uno de los mejores sistemas NoSQL con los que me he encontrado hasta ahora.

Laboratorios HGS
fuente
44

$unwind duplica cada documento en la canalización, una vez por elemento de matriz.

Entonces, si su canalización de entrada contiene un documento de artículo con dos elementos tags, {$unwind: '$tags'}se transformaría la canalización en dos documentos de artículo que son iguales excepto por el tagscampo. En el primer documento, tagscontendría el primer elemento de la matriz del documento original, y en el segundo documento, tagscontendría el segundo elemento.

JohnnyHK
fuente
22

Entendamos con un ejemplo

Así es como se ve el documento de la empresa :

documento original

El $unwindcon nosotros permite tomar como documentos de entrada que tiene un campo valorado matriz y produce documentos de salida, de manera que hay un documento de salida para cada elemento de la matriz. fuente

La etapa de $ relajarse

Así que volvamos a los ejemplos de nuestras empresas y echemos un vistazo al uso de las etapas de relajación. Esta consulta:


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

produce documentos que tienen matrices tanto por cantidad como por año.

salida del proyecto

Porque estamos accediendo al monto recaudado y al año financiado para cada elemento dentro de la matriz de rondas de financiamiento. Para solucionar esto, podemos incluir una etapa de desenrollado antes de nuestra etapa de proyecto en esta canalización de agregación, y parametrizar esto diciendo que queremos unwindla matriz de rondas de financiación:


db.companies.aggregate([
    { $match: {"funding_rounds.investments.financial_org.permalink": "greylock" } },
    { $unwind: "$funding_rounds" },
    { $project: {
        _id: 0,
        name: 1,
        amount: "$funding_rounds.raised_amount",
        year: "$funding_rounds.funded_year"
    } }
])

desenrollar tiene el efecto de enviar a la siguiente etapa más documentos de los que recibe como entrada

Si miramos la funding_roundsmatriz, sabemos que para cada uno funding_roundshay raised_amountun funded_yearcampo y un campo. Entonces, unwindpara cada uno de los documentos que son elementos de la funding_roundsmatriz se producirá un documento de salida. Ahora, en este ejemplo, nuestros valores son strings. Pero, independientemente del tipo de valor para los elementos en una matriz, unwindproducirá un documento de salida para cada uno de estos valores, de modo que el campo en cuestión tendrá solo ese elemento. En el caso de funding_rounds, ese elemento será uno de estos documentos como valor funding_roundspara cada documento que se pase a nuestro projectescenario. El resultado, luego de haber ejecutado esto, es que ahora obtenemos una amounty una year. Uno para cada ronda de financiación de cada empresaen nuestra colección. Lo que esto significa es que nuestra coincidencia produjo muchos documentos de la empresa y cada uno de esos documentos de la empresa da como resultado muchos documentos. Uno para cada ronda de financiación dentro de cada documento de la empresa. unwindrealiza esta operación utilizando los documentos que se le entregan desde el matchescenario. Y todos estos documentos para cada empresa se pasan al projectescenario.

desenrollar la salida

Por lo tanto, todos los documentos en los que el financiador era Greylock (como en el ejemplo de consulta) se dividirán en una cantidad de documentos, igual a la cantidad de rondas de financiamiento para cada empresa que coincida con el filtro $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }. Y cada uno de esos documentos resultantes se pasará a nuestro project. Ahora, unwindproduce una copia exacta de cada uno de los documentos que recibe como entrada. Todos los campos tienen la misma clave y valor, con una excepción, y es que el funding_roundscampo, en lugar de ser una matriz de funding_roundsdocumentos, tiene un valor que es un solo documento, que es una ronda de financiación individual. Por lo tanto, una empresa que tenga 4 rondas de financiación resultará en la unwindcreación de 4documentos. Donde cada campo es una copia exacta, excepto el funding_roundscampo, que en lugar de ser una matriz para cada una de esas copias, será un elemento individual de la funding_roundsmatriz del documento de la empresa que unwindse está procesando actualmente. Por lo tanto, unwindtiene el efecto de enviar a la siguiente etapa más documentos de los que recibe como entrada. Lo que eso significa es que nuestra projectetapa ahora obtiene un funding_roundscampo que, nuevamente, no es una matriz, sino que es un documento anidado que tiene raised_amountun funded_yearcampo y un campo. Por lo tanto, projectrecibirá varios documentos para cada empresa que matchingresa el filtro y, por lo tanto, podrá procesar cada uno de los documentos individualmente e identificar un monto y año individual para cada ronda de financiamiento para cada empresa..

Zameer
fuente
2
será mejor usar el mismo documento.
Jeb50
1
Como primer caso de uso para $ desenrollar, tenía un conjunto anidado bastante complicado de conjuntos anidados. Al pasar de mongo docs y stackowerflow, su respuesta finalmente me ayudó a comprender mejor $ project y $ relajarse. ¡Gracias @Zameer!
siete
3

Según la documentación oficial de mongodb:

$ desenrollar Deconstruye un campo de matriz de los documentos de entrada para generar un documento para cada elemento. Cada documento de salida es el documento de entrada con el valor del campo de matriz reemplazado por el elemento.

Explicación mediante un ejemplo básico:

Un inventario de colección tiene los siguientes documentos:

{ "_id" : 1, "item" : "ABC", "sizes": [ "S", "M", "L"] }
{ "_id" : 2, "item" : "EFG", "sizes" : [ ] }
{ "_id" : 3, "item" : "IJK", "sizes": "M" }
{ "_id" : 4, "item" : "LMN" }
{ "_id" : 5, "item" : "XYZ", "sizes" : null }

Las siguientes operaciones de $ desenrollar son equivalentes y devuelven un documento para cada elemento en el campo de tamaños . Si el campo de tamaños no se resuelve en una matriz, pero no falta, es nulo o una matriz vacía, $ desenrollar trata el operando que no es una matriz como una matriz de un solo elemento.

db.inventory.aggregate( [ { $unwind: "$sizes" } ] )

o

db.inventory.aggregate( [ { $unwind: { path: "$sizes" } } ] 

Resultado de la consulta anterior:

{ "_id" : 1, "item" : "ABC", "sizes" : "S" }
{ "_id" : 1, "item" : "ABC", "sizes" : "M" }
{ "_id" : 1, "item" : "ABC", "sizes" : "L" }
{ "_id" : 3, "item" : "IJK", "sizes" : "M" }

¿Por qué es necesario?

$ relajarse es muy útil al realizar la agregación. divide el documento complejo / anidado en un documento simple antes de realizar varias operaciones como ordenar, buscar, etc.

Para saber más sobre $ relajarse:

https://docs.mongodb.com/manual/reference/operator/aggregation/unwind/

Para saber más sobre la agregación:

https://docs.mongodb.com/manual/reference/operator/aggregation-pipeline/

Amitesh Bharti
fuente
2

considere el siguiente ejemplo para comprender estos datos en una colección

{
        "_id" : 1,
        "shirt" : "Half Sleeve",
        "sizes" : [
                "medium",
                "XL",
                "free"
        ]
}

Consulta - db.test1.aggregate ([{$ desenrollar: "$ tamaños"}]);

salida

{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "medium" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "XL" }
{ "_id" : 1, "shirt" : "Half Sleeve", "sizes" : "free" }
Pravin
fuente
1

Permítanme explicarles de una manera relacionada con RDBMS. Esta es la declaración:

db.article.aggregate(
    { $project : {
        author : 1 ,
        title : 1 ,
        tags : 1
    }},
    { $unwind : "$tags" }
);

para aplicar al documento / registro :

{
 title : "this is my title" ,
 author : "bob" ,
 posted : new Date () ,
 pageViews : 5 ,
 tags : [ "fun" , "good" , "fun" ] ,
 comments : [
             { author :"joe" , text : "this is cool" } ,
             { author :"sam" , text : "this is bad" }
 ],
 other : { foo : 5 }
}

El proyecto $ / Seleccionar simplemente devuelve estos campos / columnas como

SELECCIONAR autor, título, etiquetas DEL artículo

La siguiente es la parte divertida de Mongo, considere esta matriz tags : [ "fun" , "good" , "fun" ]como otra tabla relacionada (no puede ser una tabla de búsqueda / referencia porque los valores tienen alguna duplicación) llamada "etiquetas". Recuerde que SELECT generalmente produce cosas verticales, así que desenrollar las "etiquetas" es dividir () verticalmente en "etiquetas" de tabla.

El resultado final de $ proyecto + $ desenrollar: ingrese la descripción de la imagen aquí

Traduzca la salida a JSON:

{ "author": "bob", "title": "this is my title", "tags": "fun"},
{ "author": "bob", "title": "this is my title", "tags": "good"},
{ "author": "bob", "title": "this is my title", "tags": "fun"}

Debido a que no le dijimos a Mongo que omitiera el campo "_id", por lo que se agrega automáticamente.

La clave es que sea similar a una tabla para realizar la agregación.

Jeb50
fuente
O otra forma de pensar en ello es UNION ALL
Jeb50