Se hai familiarità con SQL, potresti conoscere UNION
clausola, che concatena i risultati di due query in un unico set di risultati. In particolare, UNION ALL
include duplicati.
In MongoDB, possiamo usare $unionWith
fase della pipeline di aggregazione per ottenere lo stesso effetto di UNION ALL
produce. Il $unionWith
stage esegue un'unione di due raccolte:combina i risultati della pipeline di due raccolte in un unico set di risultati. E include duplicati.
Esempio
Supponiamo di creare due raccolte; uno chiamato cats
e un altro chiamato dogs
. E vi inseriamo i seguenti documenti:
db.cats.insertMany([
{ _id: 1, name: "Fluffy", type: "Cat", weight: 5 },
{ _id: 2, name: "Scratch", type: "Cat", weight: 3 },
{ _id: 3, name: "Meow", type: "Cat", weight: 7 }
])
db.dogs.insertMany([
{ _id: 1, name: "Wag", type: "Dog", weight: 20 },
{ _id: 2, name: "Bark", type: "Dog", weight: 10 },
{ _id: 3, name: "Fluffy", type: "Dog", weight: 40 }
])
Ora possiamo eseguire una query su tali raccolte e utilizzare $unionWith
fase per combinare i risultati di ogni query.
Esempio:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Risultato:
{ "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : 1, "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : 2, "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : 3, "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : 1, "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : 2, "name" : "Bark", "type" : "Dog", "weight" : 10 }
In questo esempio, ogni documento ha un campo tipo con cat
o dog
e quindi è abbastanza evidente quale documento proviene da quale collezione.
Ma se i documenti non avessero il campo tipo, sarebbe più difficile capire dove finisce una raccolta e inizia un'altra. In questo caso, possiamo usare una stringa letterale in $set
fase per rappresentare il nome della collezione.
Esempio:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Risultato:
{ "_id" : "cat", "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : "cat", "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : "cat", "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : "dog", "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : "dog", "name" : "Wag", "type" : "Dog", "weight" : 20 } { "_id" : "dog", "name" : "Bark", "type" : "Dog", "weight" : 10 }
Ordinamento tra raccolte
Negli esempi precedenti, i gatti ei cani sono stati ordinati in modo da separarli in due gruppi distinti; prima i gatti, poi i cani. Ciò è accaduto principalmente perché abbiamo ordinato il type
prima il campo.
Ma possiamo ordinarlo su qualsiasi altro campo, il che potrebbe comportare la combinazione di cani e gatti.
Esempio:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Risultato:
{ "_id" : "dog", "name" : "Bark", "type" : "Dog", "weight" : 10 } { "_id" : "cat", "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : "dog", "name" : "Fluffy", "type" : "Dog", "weight" : 40 } { "_id" : "cat", "name" : "Meow", "type" : "Cat", "weight" : 7 } { "_id" : "cat", "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : "dog", "name" : "Wag", "type" : "Dog", "weight" : 20 }
Proiezioni
Puoi usare il $project
fase per specificare quali campi devono essere passati alla fase successiva della pipeline. Ad esempio, puoi quindi ridurre il numero di campi restituiti dalla query.
Esempio:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Risultato:
{ "name" : "Fluffy" } { "name" : "Scratch" } { "name" : "Meow" } { "name" : "Wag" } { "name" : "Bark" } { "name" : "Fluffy" }
Rimuovi duplicati
Puoi usare il $group
fase per eliminare i duplicati ridondanti dal risultato.
Ad esempio, la query precedente ha restituito due animali chiamati Fluffy. Possiamo aggiungere un $group
fase a quella query per eliminare il duplicato ridondante, in modo che venga restituito un solo Fluffy.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Risultato:
{ "_id" : "Meow" } { "_id" : "Bark" } { "_id" : "Scratch" } { "_id" : "Wag" } { "_id" : "Fluffy" }
Questa volta viene restituito un solo Fluffy.
Colonne non corrispondenti
Uno dei vantaggi di $unionWith
di MongoDB ha superato UNION ALL
di SQL è che può essere utilizzato con colonne non corrispondenti.
L'SQL UNION
clausola richiede che:
- Entrambe le query restituiscono lo stesso numero di colonne
- Le colonne nello stesso ordine
- Le colonne corrispondenti devono essere di un tipo di dati compatibile
Il $unionWith
di MongoDB stage non impone queste limitazioni.
Pertanto, potremmo usare $unionWith
per fare qualcosa del genere:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Risultato:
{ "_id" : 2, "name" : "Sarah", "salary" : 128000 } { "_id" : 5, "name" : "Beck", "salary" : 82000 } { "_id" : 4, "name" : "Chris", "salary" : 45000 } { "_id" : 3, "name" : "Fritz", "salary" : 25000 } { "_id" : 1, "name" : "Fluffy", "type" : "Cat", "weight" : 5 } { "_id" : 2, "name" : "Scratch", "type" : "Cat", "weight" : 3 } { "_id" : 3, "name" : "Meow", "type" : "Cat", "weight" : 7 }
In questo caso, ci siamo uniti ai cats
raccolta con i employees
collezione. I employees
la raccolta non aveva gli stessi campi di cats
collezione, ma va bene:ha funzionato ancora.