Qual è l'operatore $ rilassarsi in MongoDB?


103

Questo è il mio primo giorno con MongoDB quindi per favore vacci piano con me :)

Non riesco a capire l' $unwindoperatore, forse perché l'inglese non è la mia lingua madre.

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

L'operatore del progetto è qualcosa che posso capire, suppongo (è come SELECT, non è vero?). Ma poi, $unwind(citando) restituisce un documento per ogni membro dell'array non svolto all'interno di ogni documento di origine .

È come un JOIN? Se sì, come il risultato di $project(con _id, author, titlee tagscampi) possono essere confrontati con l' tagsarray?

NOTA : ho preso l'esempio dal sito Web MongoDB, non conosco la struttura tagsdell'array. Penso che sia un semplice array di nomi di tag.

Risposte:


236

Prima di tutto, benvenuto in MongoDB!

La cosa da ricordare è che MongoDB utilizza un approccio "NoSQL" alla memorizzazione dei dati, quindi perisci dalla tua mente i pensieri di selezioni, join, ecc. Il modo in cui memorizza i dati è sotto forma di documenti e raccolte, il che consente un mezzo dinamico per aggiungere e ottenere i dati dalle posizioni di archiviazione.

Detto questo, per comprendere il concetto alla base del parametro $ unfind, devi prima capire qual è il caso d'uso che stai cercando di citare. Il documento di esempio da mongodb.org è il seguente:

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

Notare come i tag siano in realtà un array di 3 elementi, in questo caso "divertente", "buono" e "divertente".

Quello che fa $ unfind è consentire di staccare un documento per ogni elemento e restituire quel documento risultante. Pensare a questo in un approccio classico, sarebbe l'equivalente di "per ogni elemento nell'array di tag, restituisci un documento con solo quell'elemento".

Pertanto, il risultato dell'esecuzione di quanto segue:

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

restituirà i seguenti documenti:

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

Si noti che l'unica cosa che cambia nell'array dei risultati è ciò che viene restituito nel valore del tag. Se hai bisogno di un ulteriore riferimento su come funziona, ho incluso un link qui . Spero che questo aiuti, e buona fortuna con la tua incursione in uno dei migliori sistemi NoSQL che ho incontrato finora.


44

$unwind duplica ogni documento nella pipeline, una volta per elemento dell'array.

Quindi, se la pipeline di input contiene un documento di articolo con due elementi in tags, {$unwind: '$tags'}trasformerebbe la pipeline in due documenti di articolo uguali ad eccezione del tagscampo. Nel primo documento, tagsconterrebbe il primo elemento dall'array del documento originale e nel secondo documento tagsconterrebbe il secondo elemento.


22

Comprendiamolo con un esempio

Ecco come appare il documento aziendale :

documento originale

Il $unwindci permette di prendere documenti come input che hanno un campo stimato matrice e produce documenti di output, in modo tale che ci sia un documento di output per ogni elemento dell'array. fonte

La fase di $ relax

Quindi torniamo agli esempi delle nostre aziende e diamo uno sguardo all'uso delle fasi di svolgimento. Questa domanda:


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 documenti che hanno array sia per importo che per anno.

output del progetto

Perché stiamo accedendo all'importo raccolto e all'anno finanziato per ogni elemento all'interno della matrice dei round di finanziamento. Per risolvere questo problema, possiamo includere una fase di svolgimento prima della fase del nostro progetto in questa pipeline di aggregazione e parametrizzarla dicendo che vogliamo unwindl'array dei round di finanziamento:


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

lo svolgimento ha l'effetto di inviare alla fase successiva più documenti di quanti ne riceve in input

Se guardiamo l' funding_roundsarray, sappiamo che per ciascuno funding_roundsc'è raised_amountun funded_yearcampo e. Quindi, unwindper ciascuno dei documenti che sono elementi funding_roundsdell'array produrrà un documento di output. Ora, in questo esempio, i nostri valori sono strings. Ma, indipendentemente dal tipo di valore per gli elementi in un array, unwindprodurrà un documento di output per ciascuno di questi valori, in modo tale che il campo in questione avrà solo quell'elemento. Nel caso di funding_rounds, quell'elemento sarà uno di questi documenti come valore funding_roundsper ogni documento che viene passato al nostro projectstage. Il risultato, quindi, di aver eseguito questo, è che ora otteniamo un amounte a year. Uno per ogni round di finanziamento per ogni aziendanella nostra collezione. Ciò significa che la nostra corrispondenza ha prodotto molti documenti aziendali e ognuno di questi documenti aziendali si traduce in molti documenti. Uno per ogni round di finanziamento all'interno di ogni documento aziendale. unwindesegue questa operazione utilizzando i documenti che gli vengono consegnati dal matchpalco. E tutti questi documenti per ogni azienda vengono poi passati alla projectfase.

rilassarsi in uscita

Quindi, tutti i documenti in cui il finanziatore era Greylock (come nell'esempio di query) verranno suddivisi in un numero di documenti, pari al numero di round di finanziamento per ogni azienda che corrisponde al filtro $match: {"funding_rounds.investments.financial_org.permalink": "greylock" }. E ognuno di quei documenti risultanti sarà poi passato al nostro project. Ora, unwindproduce una copia esatta per ciascuno dei documenti che riceve come input. Tutti i campi hanno la stessa chiave e valore, con un'eccezione, ovvero che il funding_roundscampo anziché essere un array di funding_roundsdocumenti, ha invece un valore che è un singolo documento, che è un round di finanziamento individuale. Quindi, un'azienda che ha 4 round di finanziamento si tradurrà nella unwindcreazione di 4documenti. Dove ogni campo è una copia esatta, ad eccezione del funding_roundscampo, che invece di essere un array per ciascuna di quelle copie sarà invece un singolo elemento funding_roundsdell'array dal documento aziendale unwindattualmente in elaborazione. Quindi, unwindha l'effetto di inviare alla fase successiva più documenti di quanti ne riceve come input. Ciò significa che il nostro projectstage ora ottiene un funding_roundscampo che di nuovo non è un array, ma è invece un documento nidificato che ha raised_amountun funded_yearcampo e. Quindi, projectriceverà più documenti per ogni azienda che matchinserisce il filtro e può quindi elaborare ciascuno dei documenti individualmente e identificare un importo e un anno individuali per ogni round di finanziamento per ciascuna azienda.


2
sarà meglio usare lo stesso documento.
Jeb50

1
Come primo caso d'uso per $ rilassarsi avevo un insieme nidificato di insiemi annidati piuttosto complicato. Passando tra mongo docs e stackowerflow, la tua risposta mi ha finalmente aiutato a capire meglio $ project e $ rilassati. Grazie @Zameer!
sette del

3

Secondo la documentazione ufficiale di mongodb:

$ unfind Decostruisce un campo array dai documenti di input per produrre un documento per ogni elemento. Ogni documento di output è il documento di input con il valore del campo matrice sostituito dall'elemento.

Spiegazione tramite un esempio di base:

Un inventario della collezione ha i seguenti documenti:

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

Le seguenti operazioni $ unfind sono equivalenti e restituiscono un documento per ogni elemento nel campo delle dimensioni . Se il campo delle dimensioni non si risolve in un array ma non è mancante, null o un array vuoto, $ unfind considera l'operando non array come un array a singolo elemento.

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

o

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

Sopra l'output della query:

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

Perché è necessario?

$ rilassarsi è molto utile durante l'esecuzione dell'aggregazione. rompe un documento complesso / annidato in un documento semplice prima di eseguire varie operazioni come l'ordinamento, la ricerca, ecc.

Per saperne di più su $ rilassarsi:

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

Per saperne di più sull'aggregazione:

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


2

considera l'esempio seguente per comprendere questi dati in una raccolta

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

Query - db.test1.aggregate ([{$ unfind: "$ size"}]);

produzione

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

1

Lascia che ti spieghi in un modo correlato al modo RDBMS. Questa è l'affermazione:

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

da applicare al documento / 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 }
}

Il progetto $ / Select restituisce semplicemente questi campi / colonne come

SELEZIONA autore, titolo, tag DA articolo

La prossima è la parte divertente di Mongo, considera questo array tags : [ "fun" , "good" , "fun" ]come un'altra tabella correlata (non può essere una tabella di ricerca / riferimento perché i valori hanno alcune duplicazioni) denominata "tag". Ricorda che SELECT generalmente produce cose verticali, quindi srotolare i "tag" significa dividere () verticalmente in "tag" di tabella.

Il risultato finale di $ project + $ relax: inserisci qui la descrizione dell'immagine

Traduci l'output in 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"}

Perché non abbiamo detto a Mongo di omettere il campo "_id", quindi è stato aggiunto automaticamente.

La chiave è rendere simile a una tabella per eseguire l'aggregazione.


O un altro modo di pensarci è UNION ALL
Jeb50
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.