new - ¿Cuál es el operador $ unwind en MongoDB?
project mongodb (4)
Este es mi primer día con MongoDB, así que por favor ve tranquilo conmigo :)
No puedo entender el operador $unwind
, 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
(citing) devuelve un documento para cada miembro de la matriz desenrollada dentro de cada documento fuente .
¿Es esto como un JOIN
? En caso afirmativo, ¿cómo se puede comparar el resultado de $project
(con los _id
, author
, title
y tags
) con el conjunto de tags
?
NOTA : Tomé el ejemplo del sitio web de MongoDB, no conozco la estructura del conjunto de tags
. Creo que es una simple variedad de nombres de etiquetas.
Comprendamos por un ejemplo
Así es como se ve el documento de la compañía :
El $unwind
nos permite tomar documentos como entrada que tienen un campo con valor de matriz y produce documentos de salida, de modo que haya un documento de salida para cada elemento en la matriz. source
Volvamos a nuestros ejemplos de empresas y analicemos el uso de las etapas de desenrollado. 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.
Porque estamos accediendo a la cantidad recaudada y al año financiado por cada elemento dentro del conjunto de rondas de financiación. Para solucionar esto, podemos incluir una etapa de desenrollado antes de la etapa de nuestro proyecto en esta canalización de agregación, y parametrizar esto diciendo que queremos unwind
el conjunto 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"
} }
])
Si miramos el conjunto de funding_rounds
, sabemos que para cada funding_rounds
, hay un campo raised_amount
y un funded_year
. Por lo tanto, unwind
voluntad para cada uno de los documentos que son elementos de la matriz de funding_rounds
producir un documento de salida. Ahora, en este ejemplo, nuestros valores son string
s. Pero, independientemente del tipo de valor para los elementos en una matriz, unwind
producirá 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
, este elemento será uno de estos documentos como el valor de funding_rounds
para cada documento que pasa a nuestra etapa de project
. El resultado, luego de haber ejecutado esto, es que ahora obtenemos una amount
y un year
. Uno para cada ronda de financiación para cada empresa de nuestra colección. Lo que esto significa es que nuestro partido 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. unwind
realiza esta operación usando los documentos entregados desde la etapa de match
. Y todos estos documentos para cada empresa pasan a la etapa de project
.
Entonces, todos los documentos donde el financiador fue Greylock (como en el ejemplo de consulta) se dividirán en una cantidad de documentos, igual al número de rondas de financiamiento para cada compañía que coincida con el filtro $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }
. Y cada uno de esos documentos resultantes se transmitirá a nuestro project
. Ahora, unwind
produce una copia exacta para 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 campo funding_rounds
lugar de ser una serie de documentos de funding_rounds
, tiene un valor que es un documento único, que es una ronda de financiación individual. Entonces, una empresa que tiene 4 rondas de financiación dará como resultado la creación de 4 documentos. Donde cada campo es una copia exacta, excepto el campo funding_rounds
, que en lugar de ser una matriz para cada una de esas copias, será un elemento individual de la matriz funding_rounds
del documento de la empresa que actualmente se está procesando. Por lo tanto, unwind
tiene el efecto de enviar a la siguiente etapa más documentos de los que recibe como entrada. Lo que eso significa es que nuestra etapa de project
ahora obtiene un campo de funding_rounds
que una vez más, no es una matriz, sino que es un documento anidado que tiene un campo raised_amount
y un funded_year
. Por lo tanto, el project
recibirá múltiples documentos para cada compañía que match
con el filtro y, por lo tanto, puede procesar cada uno de los documentos individualmente e identificar un monto y año individual para cada ronda de financiamiento para cada empresa .
Déjame explicarte de una manera correlacionada 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 }
}
$ Project / Select simplemente devuelve estos campos / columnas como
SELECCIONAR autor, título, etiquetas FROM artículo
Lo siguiente es la parte divertida de Mongo, considera estas tags : [ "fun" , "good" , "fun" ]
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, por lo que desenrollar las "etiquetas" es dividir () verticalmente en "etiquetas" de la tabla.
El resultado final de $ project + $ unwind:
Traducir 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"}
Porque no le dijimos a Mongo que omitiera el campo "_id", por lo que se agregó automáticamente.
La clave es hacerlo como una tabla para realizar la agregación.
Primero, bienvenido a MongoDB!
Lo que hay que recordar es que MongoDB emplea un enfoque "NoSQL" para el almacenamiento de datos, por lo que se pierden los pensamientos de selects, joins, etc. de tu mente. La forma en que almacena sus datos se presenta en forma de documentos y colecciones, lo que permite un medio dinámico para agregar y obtener datos de sus ubicaciones de almacenamiento.
Dicho esto, para entender el concepto detrás del parámetro $ unwind, primero debes entender qué es lo que está diciendo el caso de uso que estás 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 son "divertidos", "buenos" y "divertidos".
Lo que $ unwind hace es permitirte pelar un documento para cada elemento y devolver ese documento resultante. Pensar en esto en un enfoque clásico, sería el equivalente de "para cada elemento del conjunto de etiquetas, devolver un documento con solo 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 here . Espero que esto ayude, y buena suerte con su incursión en uno de los mejores sistemas NoSQL que he encontrado hasta ahora.
$unwind
duplica cada documento en la canalización, una vez por elemento de matriz.
Entonces, si su canalización de entrada contiene un artículo doc con dos elementos en tags
, {$unwind: ''$tags''}
transformaría el pipeline en dos documentos de artículo que son iguales excepto para el campo de tags
. En el primer documento, las tags
contendrían el primer elemento de la matriz original del documento, y en el segundo documento, las tags
contendrían el segundo elemento.