Quel est l’opérateur $ déroulant dans MongoDB?

Ceci est mon premier jour avec MongoDB alors s’il vous plaît, allez-y doucement avec moi 🙂

Je ne peux pas comprendre l’opérateur $unwind , 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 (citant) renvoie un document pour chaque membre du groupe déroulé dans chaque document source .

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

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

Tout d’abord, bienvenue à MongoDB!

La chose à retenir est que MongoDB utilise une approche “NoSQL” pour le stockage des données, afin de ne pas perdre les pensées des sélections, des jointures, etc. de votre esprit. La manière dont elle stocke vos données se présente sous la forme de documents et de collections, ce qui permet d’append et d’obtenir de manière dynamic les données de vos emplacements de stockage.

Cela étant dit, pour comprendre le concept derrière le paramètre $ unwind, vous devez d’abord comprendre ce que dit le cas d’utilisation que vous essayez de citer. L’exemple de document 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 fait un tableau de 3 éléments, dans ce cas-ci étant “fun”, “good” et “fun”.

Ce que $ dérouler fait vous permet de retirer un document pour chaque élément et renvoie ce document. Pour penser à cela dans une approche classique, ce serait l’équivalent de “pour chaque élément du tableau de balises, renvoyer un document avec seulement 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 avec votre incursion dans l’un des meilleurs systèmes NoSQL que j’ai rencontrés jusqu’à présent.

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

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

Comprenons cela par un exemple

Voici à quoi ressemble le document d’ entreprise :

document original

Le $unwind nous permet de prendre des documents en entrée avec un champ de valeurs de tableau et de produire des documents de sortie, de sorte qu’il existe un document de sortie pour chaque élément du tableau. la source

La phase de décompression

Revenons donc à nos exemples d’entresockets et à l’utilisation des phases 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" } } ])
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 qui ont des tableaux pour le montant et l’année.

sortie du projet

Parce que nous accédons au montant augmenté et à l’année financée pour chaque élément de la série de cycles de financement. Pour résoudre ce problème, nous pouvons inclure une phase de déroulement avant notre phase de projet dans ce pipeline d’agrégation, et paramétrer cela en disant que nous voulons unwind le tableau des cycles 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" } } ])
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" } } ]) 

dérouler a pour effet de produire à l'étape suivante plus de documents qu'il n'en reçoit en entrée

Si nous regardons le tableau funding_rounds , nous soaps que pour funding_rounds , il y a un raised_amount et un champ funded_year . Donc, pour chacun des documents qui sont des éléments du tableau funding_rounds un document de sortie. Maintenant, dans cet exemple, nos valeurs sont des ssortingng . Mais, quel que soit le type de valeur pour les éléments d’un tableau, le unwind produira un document de sortie pour chacune de ces valeurs, de sorte que le champ en question ne contiendra que cet élément. Dans le cas de funding_rounds , cet élément sera l’un de ces documents en tant que valeur pour funding_rounds pour chaque document qui sera transmis à notre phase de project . Le résultat, alors d’avoir fait cela, c’est que maintenant nous avons un amount et une year . Un pour chaque tour de financement pour chaque entreprise de notre collection. Cela signifie que notre correspondance a produit de nombreux documents d’entreprise et que chacun de ces documents d’entreprise a donné lieu à 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 ont été remis depuis le match . Et tous ces documents pour chaque entreprise sont ensuite transmis à l’étape du project .

dérouler la sortie

Ainsi, tous les documents où le bailleur de fonds était Greylock (comme dans l’exemple de la requête) seront divisé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 . Désormais, le unwind produit une copie exacte pour 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 champ funding_rounds , plutôt qu’un tableau de documents funding_rounds , a plutôt une valeur qui est un document unique, qui est un tour de financement individuel. Ainsi, une entreprise qui dispose de 4 rondes de financement se traduira par une unwind créant 4 documents. Où chaque champ est une copie exacte, à l’exception du champ funding_rounds , qui au lieu d’être un tableau pour chacune de ces copies sera plutôt un élément individuel du tableau funding_rounds du document de la société en cours de traitement. Ainsi, le unwind a pour effet de produire plus de documents qu’il n’en reçoit en entrée. Cela signifie que notre phase de project reçoit maintenant un champ funding_rounds qui, encore une fois, n’est pas un tableau, mais est à la place un document nested qui a un raised_amount et funded_year . Ainsi, le project recevra plusieurs documents pour chaque entreprise match au filtre et pourra donc traiter chacun des documents individuellement et identifier un montant et une année individuels pour chaque tour de financement pour chaque entreprise .

Permettez-moi d’expliquer d’une manière corélée à la façon de SGBDR. Ceci est la déclaration:

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

à appliquer au document / enregistrement :

 { 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 $ project / Select renvoie simplement ces champs / colonnes comme

SELECT auteur, titre, tags FROM article

Ensuite, la partie amusante de Mongo, considérez les tags : [ "fun" , "good" , "fun" ] ce tableau tags : [ "fun" , "good" , "fun" ] comme une autre table associée (ne peut pas être une table de recherche / référence car les valeurs ont des doublons) nommées “tags” . Rappelez-vous que SELECT produit généralement des choses verticales, donc dérouler les “tags”, c’est diviser () verticalement en “tags”.

Le résultat final de $ project + $ déroulé: entrer la description de l'image ici

Traduire 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 lui donner l’aspect d’une table pour effectuer une agrégation.