web-dev-qa-db-fra.com

Quel est l'opérateur $ dérouler dans MongoDB?

C’est mon premier jour avec MongoDB alors allez-y doucement avec moi :)

Je ne peux pas comprendre le $unwind _ opérateur, peut-être parce que l'anglais n'est pas ma langue maternelle.

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

L’opérateur du projet est quelque chose que je peux comprendre, je suppose (c’est comme SELECT, n’est-ce pas?). Mais alors, $unwind (citing) renvoie un document pour chaque membre du tableau non déroulé de chaque document source .

Est-ce comme un JOIN? Si oui, comment le résultat de $project (avec _id, author, title et tags champs) peuvent être comparés au tableau tags?

NOTE : J'ai pris l'exemple du site Web MongoDB, je ne connais pas la structure du tableau tags. Je pense que c'est un simple tableau de noms de balises.

69
gremo

Tout d’abord, bienvenue sur MongoDB!

La chose à retenir est que MongoDB utilise une approche "NoSQL" pour le stockage de données, alors perdez l'esprit de vos pensées, des jointures, etc. La manière dont il stocke vos données se présente sous la forme de documents et de collections, ce qui permet un moyen dynamique d’ajouter et d’obtenir les données à partir de vos emplacements de stockage.

Cela dit, pour comprendre le concept du paramètre $ undind, vous devez d’abord comprendre ce que dit le cas d’utilisation que vous essayez de citer. Le document d'exemple de mongodb.org est le suivant:

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

Notez que les balises sont en réalité un tableau de 3 éléments, dans ce cas-ci "amusement", "bien" et "amusement".

Ce que fait $ unwinde vous permet de détacher un document pour chaque élément et de retourner le document résultant. Pour penser à cela dans une approche classique, cela équivaudrait à "pour chaque élément du tableau de balises, renvoyer un document contenant uniquement cet élément".

Ainsi, le résultat de l'exécution de ce qui suit:

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

renverrait les documents suivants:

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

Notez que la seule chose qui change dans le tableau de résultats est ce qui est retourné dans la valeur des balises. Si vous avez besoin d'une référence supplémentaire sur la façon dont cela fonctionne, j'ai inclus un lien ici . J'espère que cela vous aidera, et bonne chance dans votre incursion dans l'un des meilleurs systèmes NoSQL que j'ai rencontrés jusqu'à présent.

172
HGS Labs

$unwind duplique chaque document du pipeline, une fois par élément de tableau.

Donc, si votre pipeline d’entrée contient un document d’article contenant deux éléments dans tags, {$unwind: '$tags'} transformerait le pipeline en deux documents d’article identiques, à l’exception du champ tags. Dans le premier document, tags contiendrait le premier élément du tableau du document d'origine et dans le second document, tags contiendrait le deuxième élément.

36
JohnnyHK

Comprenons cela par un exemple

Voici à quoi ressemble le document company :

original document

Le $unwind nous permet de prendre en entrée des documents comportant un champ de valeur de tableau et de générer des documents de sortie, de sorte qu’il existe un document de sortie pour chaque élément du tableau. source

The $unwind stage

Revenons donc à nos exemples d'entreprises et examinons l'utilisation des étapes de déroulement. Cette requête:


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

produit des documents contenant des tableaux pour le montant et l'année.

project output

Parce que nous avons accès au montant recueilli et à l’année de financement pour chaque élément du tableau des rondes de financement. Pour résoudre ce problème, nous pouvons inclure une étape de décompression avant la phase de projet dans ce pipeline d'agrégation et paramétrer ceci en indiquant que nous voulons unwind le tableau des tours de financement:


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

unwind has the effect of outputting to the next stage more documents than it receives as input

Si on regarde le funding_rounds tableau, nous savons que pour chaque funding_rounds, Il y a un raised_amount et un funded_year champ. Donc, unwind sera pour chacun des documents qui sont des éléments du funding_rounds tableau produit un document de sortie. Maintenant, dans cet exemple, nos valeurs sont strings. Mais, quel que soit le type de valeur pour les éléments d'un tableau, unwind produira un document de sortie pour chacune de ces valeurs, de sorte que le champ en question contiendra uniquement cet élément. Dans le cas de funding_rounds, cet élément sera l’un de ces documents comme valeur pour funding_rounds pour chaque document transmis à notre stade project. Le résultat, après avoir exécuté ceci, est que nous obtenons maintenant un amount et un year. Un pour chaque tour de financement pour chaque entreprise de notre collection. Cela signifie que notre correspondance a généré de nombreux documents d'entreprise et que chacun de ces documents résulte en de nombreux documents. Un pour chaque tour de financement dans chaque document d'entreprise. unwind effectue cette opération en utilisant les documents qui lui sont transmis à partir de l'étape match. Et tous ces documents pour chaque entreprise sont ensuite passés à l'étape project.

unwind output

Ainsi, tous les documents dont le bailleur de fonds était Greylock (comme dans l'exemple de requête) seront scindés en un nombre de documents égal au nombre de tours de financement. pour chaque entreprise correspondant au filtre $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }. Et chacun de ces documents sera ensuite transmis à notre project. Maintenant, unwind produit une copie exacte de chacun des documents reçus en entrée. Tous les champs ont la même clé et la même valeur, à une exception près: le funding_rounds champ plutôt que d'être un tableau de funding_rounds documents a plutôt une valeur qui est un document unique, qui est un tour de financement individuel. Ainsi, une entreprise qui a 4 tours de financement aboutira à unwind création 4 documents. Où chaque champ est une copie exacte, à l'exception du funding_rounds champ, qui au lieu d’être un tableau pour chacune de ces copies sera plutôt un élément individuel du funding_rounds tableau du document de l'entreprise que unwind est en train de traiter. Ainsi, unwind a pour effet d’afficher à l’étape suivante plus de documents qu’elle ne reçoit en entrée. Ce que cela signifie, c'est que notre scène project obtient maintenant un funding_rounds champ qui, encore une fois, n’est pas un tableau, mais un document imbriqué qui a un raised_amount et un funded_year champ. Ainsi, project recevra plusieurs documents pour chaque entreprise matching le filtre et pourra donc traiter chaque document individuellement et identifier un montant individuel et une année pour chaque tour de financement pour chaque entreprise .

13
student

Selon la documentation officielle de mongodb:

$ déroulé Déconstruit un champ de tableau à partir des documents d'entrée pour générer un document pour chaque élément. Chaque document de sortie est le document d'entrée avec la valeur du champ de tableau remplacé par l'élément.

Explication par exemple de base:

Un inventaire de collection comprend les documents suivants:

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

Les opérations $ dérouler suivantes sont équivalentes et renvoient un document pour chaque élément du champ tailles. Si le champ de tailles ne se résout pas en un tableau mais qu'il ne manque ni à null ni à un tableau vide, $ unwind traite l'opérande non-tableau comme un tableau à élément unique.

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

ou

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

Sortie de requête ci-dessus:

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

Pourquoi est-ce nécessaire?

$ unwind est très utile pour effectuer une agrégation. il divise un document complexe/imbriqué en un simple document avant d'effectuer diverses opérations telles que le tri, la recherche, etc.

Pour en savoir plus sur $ dérouler:

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

Pour en savoir plus sur l'agrégation:

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

2
Amitesh

Permettez-moi de vous expliquer de manière corrélée au SGBDR. C'est la déclaration:

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

appliquer au document/record:

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

Le $ projet/Select renvoie simplement ces champs/colonnes comme

[~ # ~] sélectionnez [~ # ~] auteur, titre, tags [~ # ~] de [~ # ~] article

Suivant la partie amusante de Mongo, considérons ce tableau tags : [ "fun" , "good" , "fun" ] comme une autre table liée (ne peut pas être une table de consultation/référence car les valeurs ont une duplication) nommée "balises". Rappelez-vous que SELECT produit généralement des objets à la verticale, aussi dérouler les "tags" à scinder () verticalement dans le tableau "tags".

Le résultat final de $ projet + $ dérouler: enter image description here

Traduisez la sortie en 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"}

Parce que nous n'avons pas dit à Mongo d'omettre le champ "_id", il est donc ajouté automatiquement.

La clé est de le rendre semblable à une table pour effectuer une agrégation.

1
Jeb50

considérez l'exemple ci-dessous pour comprendre ces données dans une collection.

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

Requête - db.test1.aggregate ([{$ dérouler: "$ tailles"}]);

sortie

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