A Voyage AI se une ao MongoDB para impulsionar aplicativos de AI mais precisos e confiáveis no Atlas.

Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Desenvolvedor do MongoDB
Centro de desenvolvedores do MongoDB
chevron-right
Produtos
chevron-right
MongoDB
chevron-right

Como usar a fase do pipeline de agregação Union All no MongoDB 4.4

Adrienne Tacke16 min read • Published Jan 31, 2022 • Updated Sep 09, 2024
MongoDBFramework de agregação
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Com o lançamento do MongoDB 4.4 vem um novo estágio doaggregation pipeline chamado $unionWith. Este estágio permite combinar várias collection em um único conjunto de resultados!
Veja como você usaria isso:
Sintaxe simplificada, sem processamento adicional na coleção especificada
1db.collection.aggregate([
2 { $unionWith: "<anotherCollection>" }
3])
Sintaxe estendida, usando o campo de pipeline opcional
1db.collection.aggregate([
2 { $unionWith: { coll: "<anotherCollection>", pipeline: [ <stage1>, etc. ] } }
3])
++ Se você usar o campo pipeline para processar sua collection antes de combinar, lembre-se de que os estágios que escrevem dados, como $out e $merge, não podem ser usados!
Os documentos resultantes mesclarão o fluxo de documentos da collection atual (ou pipeline) com os documentos da collection/pipeline especificado. Lembre-se de que isso pode incluir duplicatas!

Isso soa meio familiar..

Se você já usou a operaçãoUNION ALL no SQL antes, a funcionalidade do estágio$unionWith pode soar familiar para você, e você não está errado! Ambos combinam os conjuntos de resultados de várias queries e retornam as linhas mescladas, algumas das quais podem ser duplicadas. No entanto, é aqui que as similaridades terminam.Ao contrário do estágio$unionWithdo MongoDB, você precisa seguir algumas regras para executar uma operaçãoUNION ALLválida no SQL:
  • Certifique-se de que suas duas queries tenham o mesmo número de colunas
  • Certifique-se de que a ordem das colunas seja a mesma
  • Certifique-se de que as colunas correspondentes sejam tipos de dados compatíveis.
Seria algo assim em SQL:
1SELECT column1, expression1, column2
2FROM table1
3UNION ALL
4SELECT column1, expression1, column2
5FROM table2
6WHERE [conditions]
Com o estágio$unionWith do MongoDB, você não precisa se preocupar com essas restrições rigorosas.

Então, como está o MongoDB

$unionWith

estágio diferente?

A diferença mais conveniente entre o estágio$unionWithe outras operações UNION é que não há restrição de esquema correspondente. Esse suporte flexível ao esquema significa que você pode combinar documentos que talvez não tenham o mesmo tipo ou número de campos. Isso é comum em determinados cenários, em que os dados que precisamos usar vêm de fontes diferentes:
  • Dados do TimeSeries que são armazenados por mês/trimestre/alguma outra unidade de tempo
  • Dados do dispositivo IoT, por armação ou versão
  • Dados regionais
Com o estágio$unionWithdo MongoDB, é possível combinar essas fontes de dados.
Pronto para experimentar o novo palco$unionWith? Acompanhe concluindo algumas etapas de configuração primeiro. Ou você pode pular para os exemplos de código. 😉

Pré-requisitos

Primeiro, uma compreensão geral do que é a estrutura de agregação e como usá-la será importante para o restante deste tutorial. Se você não está familiarizado com a estrutura de agregação, confira esta excelente introdução à estrutura de agregação do MongoDB, escrita pelo colega defensor do desenvolvimento Ken Alger!
Em seguida, com base na sua situação, você já pode ter alguns pré-requisitos configurados ou precisar começar do zero. De qualquer forma, escolha seu cenário para configurar as coisas que você precisa para que você possa seguir o restante deste tutorial!
Escolha seu cenário:
Ainda não tenho um cluster Atlas configurado:
  1. Você precisará de uma conta Atlas para usar o MongoDB Atlas! Crie um, caso ainda não o tenha feito. Caso contrário, inicie sessão na sua conta Atlas.
  2. Configure um cluster Atlas gratuito (nenhum cartão de crédito necessário!). Certifique-se de selecionar MongoDB 4.4 (pode ser Beta, o que é OK) como sua versão nas Configurações Adicionais!
    💡 Se você não vir a solicitação para criar um cluster: talvez você seja solicitado a criar um projeto antes de ver a solicitação para criar seu primeiro cluster. Nesse caso, vá em frente e crie um projeto primeiro (deixando todas as configurações padrão). Em seguida, continue com as instruções para implantar seu primeiro cluster gratuito!
  3. Depois que o cluster estiver configurado, adicione seu endereço IP às configurações de conexão do cluster. Isso informa ao cluster quem tem permissão para se conectar a ele.
  4. Por fim, crie um utilizador de banco de dados para seu cluster. Atlas exige que qualquer pessoa ou coisa que acesse seus clusters se autentique como usuário do banco de MongoDB database por motivos de segurança! Mantenha estas credenciais à mão, pois você precisará delas mais tarde.
  5. Continue com as etapas em Conectando-se ao cluster.
Eu tenho um cluster do Atlas configurado:
Excelente! Você pode pular para Conectar-se ao seu cluster.
Conectando-se ao seu cluster
Para se conectar ao seu cluster, usaremos a extensão MongoDB para Visual Studio Code (VS Code para abreviar !). Você pode visualizar seus dados diretamente, interagir com suas coleções e muito mais com esta extensão útil! Isso também consolida nosso espaço de trabalho em uma única janela, removendo a necessidade de alternarmos entre nosso código e o MongoDB Atlas!
} Embora estejamos usando a extensão de VS Code VS Code para o restante deste tutorial, não é obrigatório usar o estágio de pipeline$unionWith! Você também pode usar a CLI, drivers específicos de idiomaou Compass , se preferir!
  1. Instale a extensãoMongoDB for VS Code(ou instale VS Code primeiro, se ainda não o tiver 😉).
  2. Para se conectar ao seu cluster, você precisará de uma string de conexão. Você pode obter essa cadeia de conexão nas configurações de conexão do cluster. Go para seu cluster e selecione a opção "Conectar":
    Conectando ao seu cluster do Atlas
  3. Selecione a opção "Conectar-se usando o MongoDB Compass". Isso nos fornecerá uma connection string no formato de conexão DNS Seedlist que podemos usar com a extensão MongoDB.
    Escolhendo a opção "Conectar com o MongoDB Compass"
    . . . MongoDB for VS Code também suporta o formato de connection string padrão. O uso do formato de conexão de lista de seed de DNS é apenas preferência.
  4. Pule para a segunda etapa e copie a connection string (não se preocupar com as outras configurações, você não precisará delas):
    Copie a connection string nas configurações de conexão do MongoDB Atlas
  5. Volte para o VS Code. Pressione Ctrl + Shift + P (no Windows) ou Shift + Command + P (no Mac) para abrir a paleta de comandos. Isso mostra uma lista de todos os comandos do VS Code.
    Mostrando a paleta de comando no VS Code
  6. Comece a digitar "MongoDB" até ver a lista de comandos disponíveis da extensão MongoDB. Selecione a opção "MongoDB: conectar com a connection string".
    Procurando comandos de extensão MongoDB
  7. Cole sua string de conexão copiada. Não se esqueça! Você precisa substituir a senha do placeholder pela sua senha real!
    Colando e modificando nossa connection string do MongoDB Atlas cluster
  8. Pressione Enter para conectar! Você saberá que a conexão foi bem-sucedida se ver uma mensagem de confirmação no canto inferior direito. Você também verá seu cluster listado quando expandir o painel de extensão do MongoDB.
Com a extensão do MongoDB instalada e seu cluster conectado, agora você pode usar o MongoDB Playgrounds para testar os exemplos$unionWith! O MongoDB Playgrounds nos dá uma boa caixa de areia para escrever e testar facilmente as consultas do Mongo. Adoro usá-lo quando estou fazendo um protótipo ou tentando algo novo, porque ele tem preenchimento automático de consultas e realce de sintaxe, algo que você não tem na maioria dos terminais.
Vamos finalmente mergulhar em alguns exemplos!

Exemplos

Para acompanhar, você pode usar estes arquivos de Playground do MongoDB que criei para acompanhar esta publicação no blog ou criar os seus próprios!
💡 Se você criar seu próprio playground, lembre-se de alterar o nome do banco de dados e excluir o código do modelo padrão primeiro!
$unionWith

usando um pipeline

} Use este playground se quiser acompanhar o código pré-escrito para este exemplo.
Logo na parte superior, especifique o banco de dados que você usará. Neste exemplo, estou usando um banco de dados também chamado union-walkthrough:
1use('union-walkthrough');
Na verdade, aindanão criei um banco de dados chamadounion-walkthroughno Atlas, mas isso não é problema! Quando o playground for executado, ele verá que ele ainda não existe e criará um banco de dados com o nome especificado!
Em seguida, precisamos de dados! Particularmente sobre alguns planetas. E particularmente sobre planetas em uma determinada série de filmes. 😉
Usando a ótima API SWAPI, coletamos essas informações sobre alguns planetas. Vamos adicioná-los a duas collection, separadas por compatibilidade.
Quaisquer planetas que apareçam em pelo menos 2 ou mais filmes são considerados populares. Caso contrário, iremos adicioná-los à coleçãolonely_planets:
1// Insert a few documents into the lonely_planets collection.
2db.lonely_planets.insertMany([
3 {
4 "name": "Endor",
5 "rotation_period": "18",
6 "orbital_period": "402",
7 "diameter": "4900",
8 "climate": "temperate",
9 "gravity": "0.85 standard",
10 "terrain": "forests, mountains, lakes",
11 "surface_water": "8",
12 "population": "30000000",
13 "residents": [
14 "http://swapi.dev/api/people/30/"
15 ],
16 "films": [
17 "http://swapi.dev/api/films/3/"
18 ],
19 "created": "2014-12-10T11:50:29.349000Z",
20 "edited": "2014-12-20T20:58:18.429000Z",
21 "url": "http://swapi.dev/api/planets/7/"
22 },
23 {
24 "name": "Kamino",
25 "rotation_period": "27",
26 "orbital_period": "463",
27 "diameter": "19720",
28 "climate": "temperate",
29 "gravity": "1 standard",
30 "terrain": "ocean",
31 "surface_water": "100",
32 "population": "1000000000",
33 "residents": [
34 "http://swapi.dev/api/people/22/",
35 "http://swapi.dev/api/people/72/",
36 "http://swapi.dev/api/people/73/"
37 ],
38 "films": [
39 "http://swapi.dev/api/films/5/"
40 ],
41 "created": "2014-12-10T12:45:06.577000Z",
42 "edited": "2014-12-20T20:58:18.434000Z",
43 "url": "http://swapi.dev/api/planets/10/"
44 },
45 {
46 "name": "Yavin IV",
47 "rotation_period": "24",
48 "orbital_period": "4818",
49 "diameter": "10200",
50 "climate": "temperate, tropical",
51 "gravity": "1 standard",
52 "terrain": "jungle, rainforests",
53 "surface_water": "8",
54 "population": "1000",
55 "residents": [],
56 "films": [
57 "http://swapi.dev/api/films/1/"
58 ],
59 "created": "2014-12-10T11:37:19.144000Z",
60 "edited": "2014-12-20T20:58:18.421000Z",
61 "url": "http://swapi.dev/api/planets/3/"
62 },
63 {
64 "name": "Hoth",
65 "rotation_period": "23",
66 "orbital_period": "549",
67 "diameter": "7200",
68 "climate": "frozen",
69 "gravity": "1.1 standard",
70 "terrain": "tundra, ice caves, mountain ranges",
71 "surface_water": "100",
72 "population": "unknown",
73 "residents": [],
74 "films": [
75 "http://swapi.dev/api/films/2/"
76 ],
77 "created": "2014-12-10T11:39:13.934000Z",
78 "edited": "2014-12-20T20:58:18.423000Z",
79 "url": "http://swapi.dev/api/planets/4/"
80 },
81 {
82 "name": "Bespin",
83 "rotation_period": "12",
84 "orbital_period": "5110",
85 "diameter": "118000",
86 "climate": "temperate",
87 "gravity": "1.5 (surface), 1 standard (Cloud City)",
88 "terrain": "gas giant",
89 "surface_water": "0",
90 "population": "6000000",
91 "residents": [
92 "http://swapi.dev/api/people/26/"
93 ],
94 "films": [
95 "http://swapi.dev/api/films/2/"
96 ],
97 "created": "2014-12-10T11:43:55.240000Z",
98 "edited": "2014-12-20T20:58:18.427000Z",
99 "url": "http://swapi.dev/api/planets/6/"
100 }
101]);
102
103// Insert a few documents into the popular_planets collection.
104db.popular_planets.insertMany([
105 {
106 "name": "Tatooine",
107 "rotation_period": "23",
108 "orbital_period": "304",
109 "diameter": "10465",
110 "climate": "arid",
111 "gravity": "1 standard",
112 "terrain": "desert",
113 "surface_water": "1",
114 "population": "200000",
115 "residents": [
116 "http://swapi.dev/api/people/1/",
117 "http://swapi.dev/api/people/2/",
118 "http://swapi.dev/api/people/4/",
119 "http://swapi.dev/api/people/6/",
120 "http://swapi.dev/api/people/7/",
121 "http://swapi.dev/api/people/8/",
122 "http://swapi.dev/api/people/9/",
123 "http://swapi.dev/api/people/11/",
124 "http://swapi.dev/api/people/43/",
125 "http://swapi.dev/api/people/62/"
126 ],
127 "films": [
128 "http://swapi.dev/api/films/1/",
129 "http://swapi.dev/api/films/3/",
130 "http://swapi.dev/api/films/4/",
131 "http://swapi.dev/api/films/5/",
132 "http://swapi.dev/api/films/6/"
133 ],
134 "created": "2014-12-09T13:50:49.641000Z",
135 "edited": "2014-12-20T20:58:18.411000Z",
136 "url": "http://swapi.dev/api/planets/1/"
137 },
138 {
139 "name": "Alderaan",
140 "rotation_period": "24",
141 "orbital_period": "364",
142 "diameter": "12500",
143 "climate": "temperate",
144 "gravity": "1 standard",
145 "terrain": "grasslands, mountains",
146 "surface_water": "40",
147 "population": "2000000000",
148 "residents": [
149 "http://swapi.dev/api/people/5/",
150 "http://swapi.dev/api/people/68/",
151 "http://swapi.dev/api/people/81/"
152 ],
153 "films": [
154 "http://swapi.dev/api/films/1/",
155 "http://swapi.dev/api/films/6/"
156 ],
157 "created": "2014-12-10T11:35:48.479000Z",
158 "edited": "2014-12-20T20:58:18.420000Z",
159 "url": "http://swapi.dev/api/planets/2/"
160 },
161 {
162 "name": "Naboo",
163 "rotation_period": "26",
164 "orbital_period": "312",
165 "diameter": "12120",
166 "climate": "temperate",
167 "gravity": "1 standard",
168 "terrain": "grassy hills, swamps, forests, mountains",
169 "surface_water": "12",
170 "population": "4500000000",
171 "residents": [
172 "http://swapi.dev/api/people/3/",
173 "http://swapi.dev/api/people/21/",
174 "http://swapi.dev/api/people/35/",
175 "http://swapi.dev/api/people/36/",
176 "http://swapi.dev/api/people/37/",
177 "http://swapi.dev/api/people/38/",
178 "http://swapi.dev/api/people/39/",
179 "http://swapi.dev/api/people/42/",
180 "http://swapi.dev/api/people/60/",
181 "http://swapi.dev/api/people/61/",
182 "http://swapi.dev/api/people/66/"
183 ],
184 "films": [
185 "http://swapi.dev/api/films/3/",
186 "http://swapi.dev/api/films/4/",
187 "http://swapi.dev/api/films/5/",
188 "http://swapi.dev/api/films/6/"
189 ],
190 "created": "2014-12-10T11:52:31.066000Z",
191 "edited": "2014-12-20T20:58:18.430000Z",
192 "url": "http://swapi.dev/api/planets/8/"
193 },
194 {
195 "name": "Coruscant",
196 "rotation_period": "24",
197 "orbital_period": "368",
198 "diameter": "12240",
199 "climate": "temperate",
200 "gravity": "1 standard",
201 "terrain": "cityscape, mountains",
202 "surface_water": "unknown",
203 "population": "1000000000000",
204 "residents": [
205 "http://swapi.dev/api/people/34/",
206 "http://swapi.dev/api/people/55/",
207 "http://swapi.dev/api/people/74/"
208 ],
209 "films": [
210 "http://swapi.dev/api/films/3/",
211 "http://swapi.dev/api/films/4/",
212 "http://swapi.dev/api/films/5/",
213 "http://swapi.dev/api/films/6/"
214 ],
215 "created": "2014-12-10T11:54:13.921000Z",
216 "edited": "2014-12-20T20:58:18.432000Z",
217 "url": "http://swapi.dev/api/planets/9/"
218 },
219 {
220 "name": "Dagobah",
221 "rotation_period": "23",
222 "orbital_period": "341",
223 "diameter": "8900",
224 "climate": "murky",
225 "gravity": "N/A",
226 "terrain": "swamp, jungles",
227 "surface_water": "8",
228 "population": "unknown",
229 "residents": [],
230 "films": [
231 "http://swapi.dev/api/films/2/",
232 "http://swapi.dev/api/films/3/",
233 "http://swapi.dev/api/films/6/"
234 ],
235 "created": "2014-12-10T11:42:22.590000Z",
236 "edited": "2014-12-20T20:58:18.425000Z",
237 "url": "http://swapi.dev/api/planets/5/"
238 }
239]);
Essa separação é indicativa de como nossos dados podem ser agrupados. Apesar da separação, podemos usar o estágio$unionWith para combinar essas duas collection se precisarmos analisá-las como um único conjunto de resultados!
Digamos que precisássemos descobrir a população total de planetas, agrupados por clima. Além disso, gostaria de deixar de fora todos os planetas que não tenham dados populacionais de nosso cálculo. Podemos fazer isso usando uma agregação:
1// Run an aggregation to view total planet populations, grouped by climate type.
2use('union-walkthrough');
3
4db.lonely_planets.aggregate([
5 {
6 $match: {
7 population: { $ne: 'unknown' }
8 }
9 },
10 {
11 $unionWith: {
12 coll: 'popular_planets',
13 pipeline: [{
14 $match: {
15 population: { $ne: 'unknown' }
16 }
17 }]
18 }
19 },
20 {
21 $group: {
22 _id: '$climate', totalPopulation: { $sum: { $toLong: '$population' } }
23 }
24 }
25]);
Se você seguiu em seu próprio playground do MongoDB e copiou o código até agora, tente executar a agregação!
E se você estiver usando o playground MongoDB fornecido que criei, realce as linhas 264 a 290 e execute o código selecionado.
Você notará no trecho de código acima que eu adicionei outro método use('union-walkthrough');logo acima do código de agregação. Faço isso para facilitar a seleção do código relevante no playground. Isso também é necessário para que o código de agregação possa ser executado no banco de dados correto. No entanto, a mesma coisa pode ser feita selecionando várias linhas, ou seja, a linhause('union-walkthrough')original na parte superior e qualquer exemplo adicional que você queira executar!
Você deverá ver os resultados assim:
1[
2 {
3 _id: 'arid',
4 totalPopulation: 200000
5 },
6 {
7 _id: 'temperate',
8 totalPopulation: 1007536000000
9 },
10 {
11 _id: 'temperate, tropical',
12 totalPopulation: 1000
13 }
14]
Não é de surpreender que planetas com climas temperados " " pareçam ter mais habitantes. Algo sobre esse legal 75 F / 23.8 C, eu acho 🌞
Vamos decompor essa agregação:
O primeiro objeto que passamos para nossa agregação também é nosso primeiro estágio, usado aqui como nossos critérios de filtro. Especificamente, usamos o estágio de pipeline$match :
1{
2 $match: {
3 population: { $ne: 'unknown' }
4 }
5},
Neste exemplo, filtramos todos os documentos que tenham unknown como seu valorpopulation usando o operador$ne (não igual).
O próximo objeto (e o próximo estágio) em nossa agregação é nosso estágio$unionWith. Aqui, especificamos com qual coleção gostaria de realizar uma união (incluindo quaisquer duplicatas). Também usamos o campo de pipeline para filtrar de forma semelhante quaisquer documentos em nossa coleçãopopular_planets que tenham uma população desconhecida:
1{
2 $unionWith: {
3 coll: 'popular_planets',
4 pipeline: [
5 {
6 $match: {
7 population: { $ne: 'unknown' }
8 }
9 }
10 ]
11 }
12},
Finalmente, temos nosso último estágio em nossa agregação. Depois de combinar nossas collectionslonely_planets e popular_planets(ambos filtrando documentos sem dados populacionais), agrupamos os documentos resultantes usando um estágio$group:
1{
2 $group: {
3 _id: '$climate',
4 totalPopulation: { $sum: { $toLong: '$population' } }
5 }
6}
Como queremos saber a população total por tipo de clima, primeiro especificamos _id como o campo$climatedo nosso conjunto de resultados combinado. Em seguida, calculamos um novo campo chamado totalPopulation usando um operador$sum para adicionar os valores de população de cada documento correspondente. Você também notará que, com base nos dados que temos, precisávamos usar um operador$toLong para primeiro converter nosso campo$population em um valor calculável!
$unionWith

sem um pipeline

} Use este playground se quiser acompanhar o código pré-escrito para este exemplo.
Agora, se você não precisar executar algum processamento adicional na coleção com a qual está combinando, você não precisa! O campopipeline é opcional e só estará lá se você precisar dele.
Então, se você só precisa trabalhar com os dados do planeta como um conjunto unificado, também pode fazer isso:
1// Run an aggregation with no pipeline
2use('union-walkthrough');
3
4db.lonely_planets.aggregate([
5 { $unionWith: 'popular_planets' }
6]);
Copie esta agregação em seu próprio playground e execute-a! Como alternativa, selecione e execute as linhas 293 - 297 se estiver usando o playground MongoDB fornecido!
Pronto! Agora você pode usar esse conjunto de dados unificado para análise ou processamento adicional.

Esquemas diferentes

Combinar os mesmos esquemas é ótimo, mas também podemos fazer isso no SQL comum! A verdadeira conveniência do estágio do pipeline$unionWithé que ele também pode combinar collection com esquemas diferentes. Vamos dar uma olhada!
$unionWith

usando collection com esquemas diferentes

} Use este playground se quiser acompanhar o código pré-escrito para este exemplo.
Como antes, especificaremos o banco de dados que queremos usar:
1use('union-walkthrough');
Desta vez, usaremos algumas informações adquiridas sobre determinadas naves e veículos que são usados nessa mesma série de filmes. Vamos adicioná-los às suas respectivas collection:
1// Insert a few documents into the starships collection
2db.starships.insertMany([
3 {
4 "name": "Death Star",
5 "model": "DS-1 Orbital Battle Station",
6 "manufacturer": "Imperial Department of Military Research, Sienar Fleet Systems",
7 "cost_in_credits": "1000000000000",
8 "length": "120000",
9 "max_atmosphering_speed": "n/a",
10 "crew": 342953,
11 "passengers": 843342,
12 "cargo_capacity": "1000000000000",
13 "consumables": "3 years",
14 "hyperdrive_rating": 4.0,
15 "MGLT": 10,
16 "starship_class": "Deep Space Mobile Battlestation",
17 "pilots": []
18 },
19 {
20 "name": "Millennium Falcon",
21 "model": "YT-1300 light freighter",
22 "manufacturer": "Corellian Engineering Corporation",
23 "cost_in_credits": "100000",
24 "length": "34.37",
25 "max_atmosphering_speed": "1050",
26 "crew": 4,
27 "passengers": 6,
28 "cargo_capacity": 100000,
29 "consumables": "2 months",
30 "hyperdrive_rating": 0.5,
31 "MGLT": 75,
32 "starship_class": "Light freighter",
33 "pilots": [
34 "http://swapi.dev/api/people/13/",
35 "http://swapi.dev/api/people/14/",
36 "http://swapi.dev/api/people/25/",
37 "http://swapi.dev/api/people/31/"
38 ]
39 },
40 {
41 "name": "Y-wing",
42 "model": "BTL Y-wing",
43 "manufacturer": "Koensayr Manufacturing",
44 "cost_in_credits": "134999",
45 "length": "14",
46 "max_atmosphering_speed": "1000km",
47 "crew": 2,
48 "passengers": 0,
49 "cargo_capacity": 110,
50 "consumables": "1 week",
51 "hyperdrive_rating": 1.0,
52 "MGLT": 80,
53 "starship_class": "assault starfighter",
54 "pilots": []
55 },
56 {
57 "name": "X-wing",
58 "model": "T-65 X-wing",
59 "manufacturer": "Incom Corporation",
60 "cost_in_credits": "149999",
61 "length": "12.5",
62 "max_atmosphering_speed": "1050",
63 "crew": 1,
64 "passengers": 0,
65 "cargo_capacity": 110,
66 "consumables": "1 week",
67 "hyperdrive_rating": 1.0,
68 "MGLT": 100,
69 "starship_class": "Starfighter",
70 "pilots": [
71 "http://swapi.dev/api/people/1/",
72 "http://swapi.dev/api/people/9/",
73 "http://swapi.dev/api/people/18/",
74 "http://swapi.dev/api/people/19/"
75 ]
76 },
77]);
78
79// Insert a few documents into the vehicles collection
80db.vehicles.insertMany([
81 {
82 "name": "Sand Crawler",
83 "model": "Digger Crawler",
84 "manufacturer": "Corellia Mining Corporation",
85 "cost_in_credits": "150000",
86 "length": "36.8 ",
87 "max_atmosphering_speed": 30,
88 "crew": 46,
89 "passengers": 30,
90 "cargo_capacity": 50000,
91 "consumables": "2 months",
92 "vehicle_class": "wheeled",
93 "pilots": []
94 },
95 {
96 "name": "X-34 landspeeder",
97 "model": "X-34 landspeeder",
98 "manufacturer": "SoroSuub Corporation",
99 "cost_in_credits": "10550",
100 "length": "3.4 ",
101 "max_atmosphering_speed": 250,
102 "crew": 1,
103 "passengers": 1,
104 "cargo_capacity": 5,
105 "consumables": "unknown",
106 "vehicle_class": "repulsorcraft",
107 "pilots": [],
108 },
109 {
110 "name": "AT-AT",
111 "model": "All Terrain Armored Transport",
112 "manufacturer": "Kuat Drive Yards, Imperial Department of Military Research",
113 "cost_in_credits": "unknown",
114 "length": "20",
115 "max_atmosphering_speed": 60,
116 "crew": 5,
117 "passengers": 40,
118 "cargo_capacity": 1000,
119 "consumables": "unknown",
120 "vehicle_class": "assault walker",
121 "pilots": [],
122 "films": [
123 "http://swapi.dev/api/films/2/",
124 "http://swapi.dev/api/films/3/"
125 ],
126 "created": "2014-12-15T12:38:25.937000Z",
127 "edited": "2014-12-20T21:30:21.677000Z",
128 "url": "http://swapi.dev/api/vehicles/18/"
129 },
130 {
131 "name": "AT-ST",
132 "model": "All Terrain Scout Transport",
133 "manufacturer": "Kuat Drive Yards, Imperial Department of Military Research",
134 "cost_in_credits": "unknown",
135 "length": "2",
136 "max_atmosphering_speed": 90,
137 "crew": 2,
138 "passengers": 0,
139 "cargo_capacity": 200,
140 "consumables": "none",
141 "vehicle_class": "walker",
142 "pilots": [
143 "http://swapi.dev/api/people/13/"
144 ]
145 },
146 {
147 "name": "Storm IV Twin-Pod cloud car",
148 "model": "Storm IV Twin-Pod",
149 "manufacturer": "Bespin Motors",
150 "cost_in_credits": "75000",
151 "length": "7",
152 "max_atmosphering_speed": 1500,
153 "crew": 2,
154 "passengers": 0,
155 "cargo_capacity": 10,
156 "consumables": "1 day",
157 "vehicle_class": "repulsorcraft",
158 "pilots": [],
159 }
160]);
Você pode estar refletindo (como eu criei pela primeira vez): qual é a diferença entre espaçonaves e veículos? Você ficará satisfeito em saber que as espaçonaves são definidas como qualquer "nave de transporte única que tenha capacidade de hiperdrive". Qualquer outra nave de transporte que não tenha capacidade de hiperdrive é considerada um veículo. Quanto mais você sabe! . . .
Se você observar as duas collections, verá que elas têm duas diferenças principais:
  • O campomax_atmosphering_speed está presente em ambas as collections, mas é um string na collectionstarships e um int na collectionvehicles.
  • A collectionstarships tem dois campos (hyperdrive_rating, MGLT) que não estão presentes na collectionvehicles, pois ela se relaciona apenas com espaçonaves.
Mas você sabe o que? Isso não é um problema para a fase$unionWith! Você pode combiná-los exatamente como antes:
1// Run an aggregation with no pipeline and differing schemas
2use('union-walkthrough');
3
4db.starships.aggregate([
5 { $unionWith: 'vehicles' }
6]);
Tente executar a agregação no seu playground! Ou, se você estiver acompanhando o playground do MongoDB que forneci, selecione e execute as linhas 185 a 189! Você deve obter o seguinte conjunto de resultados combinado como sua saída:
1[
2 {
3 _id: 5f306ddca3ee8339643f137e,
4 name: 'Death Star',
5 model: 'DS-1 Orbital Battle Station',
6 manufacturer: 'Imperial Department of Military Research, Sienar Fleet Systems',
7 cost_in_credits: '1000000000000',
8 length: '120000',
9 max_atmosphering_speed: 'n/a',
10 crew: 342953,
11 passengers: 843342,
12 cargo_capacity: '1000000000000',
13 consumables: '3 years',
14 hyperdrive_rating: 4,
15 MGLT: 10,
16 starship_class: 'Deep Space Mobile Battlestation',
17 pilots: []
18 },
19 {
20 _id: 5f306ddca3ee8339643f137f,
21 name: 'Millennium Falcon',
22 model: 'YT-1300 light freighter',
23 manufacturer: 'Corellian Engineering Corporation',
24 cost_in_credits: '100000',
25 length: '34.37',
26 max_atmosphering_speed: '1050',
27 crew: 4,
28 passengers: 6,
29 cargo_capacity: 100000,
30 consumables: '2 months',
31 hyperdrive_rating: 0.5,
32 MGLT: 75,
33 starship_class: 'Light freighter',
34 pilots: [
35 'http://swapi.dev/api/people/13/',
36 'http://swapi.dev/api/people/14/',
37 'http://swapi.dev/api/people/25/',
38 'http://swapi.dev/api/people/31/'
39 ]
40 },
41 // + 7 other results, omitted for brevity
42]
Você pode imaginar fazendo isso em SQL? Dica: não pode! No entanto, esse tipo de restrição de esquema é algo com que você não precisa se preocupar com o MongoDB!

$unionCom o uso de coleções com diferentes esquemas e um pipeline

} Use este playground se quiser acompanhar o código pré-escrito para este exemplo.
Para que possamos combinar esquemas diferentes sem problema. E se precisarmos fazer um pouco de trabalho extra em nossa collection antes de combiná-la? É aqui que entra o campopipeline !
Digamos que haja algumas informações classificadas em nossos dados sobre os veículos. Ou seja, qualquer veículo produzido pela Kuat Drive Yards (AKA, uma divisão do departamento imperial de pesquisa milita).
Por ordens diretas, você é instruído a não fornecer essas informações em nenhuma circunstância. Na verdade, você precisa interceptar todas as solicitações de informações do veículo e remover esses veículos classificados da lista!
Podemos fazer isso da seguinte forma:
1use('union-walkthrough');
2
3db.starships.aggregate([
4 {
5 $unionWith: {
6 coll: 'vehicles',
7 pipeline: [
8 {
9 $redact: {
10 $cond: {
11 if: { $eq: [ "$manufacturer", "Kuat Drive Yards, Imperial Department of Military Research"] },
12 then: "$$PRUNE",
13 else: "$$DESCEND"
14 }
15 }
16 }
17 ]
18 }
19 }
20]);
Neste exemplo, estamos combinando as starships vehicles collections e como antes, usando o $unionWith estágio de pipeline . Também processamos os vehicle dados um pouco mais, usando $unionWith o pipeline campo opcional do :
1// Pipeline used with the vehicle collection
2{
3 $redact: {
4 $cond: {
5 if: { $eq: [ "$manufacturer", "Kuat Drive Yards, Imperial Department of Military Research"] },
6 then: "$$PRUNE",
7 else: "$$DESCEND"
8 }
9 }
10}
Dentro do pipeline do$unionWith, usamos um estágio$redact para restringir o conteúdo de nossos documentos com base em uma condição. A condição é especificada usando o operador$cond, que atua como uma declaraçãoif/else.
Em nosso caso, estamos avaliando se o campomanufacturer contém ou não um valor de "Kuat Drive Yards, departamento imperial de pesquisa armada". Em caso afirmativo (uh oh, isso é classificado!), usamos uma variável de sistema chamada $$PRUNE, que nos permite excluir todos os campos no nível do documento atual/documento incorporado. Caso contrário, usamos outra variável do sistema chamada $$DESCEND, que retornará todos os campos no nível do documento atual, exceto quaisquer documentos incorporados.
Isso funciona perfeitamente para nosso caso de uso. Tente executar a aggregation (linhas 192 - 211, se estiver usando o MongoDB Playground fornecido). Você deve ver um conjunto combinado de resultados, menos quaisquer veículos fabricados pela Imperial:
1[
2 {
3 _id: 5f306ddca3ee8339643f137e,
4 name: 'Death Star',
5 model: 'DS-1 Orbital Battle Station',
6 manufacturer: 'Imperial Department of Military Research, Sienar Fleet Systems',
7 cost_in_credits: '1000000000000',
8 length: '120000',
9 max_atmosphering_speed: 'n/a',
10 crew: 342953,
11 passengers: 843342,
12 cargo_capacity: '1000000000000',
13 consumables: '3 years',
14 hyperdrive_rating: 4,
15 MGLT: 10,
16 starship_class: 'Deep Space Mobile Battlestation',
17 pilots: []
18 },
19 {
20 _id: 5f306ddda3ee8339643f1383,
21 name: 'X-34 landspeeder',
22 model: 'X-34 landspeeder',
23 manufacturer: 'SoroSuub Corporation',
24 cost_in_credits: '10550',
25 length: '3.4 ',
26 max_atmosphering_speed: 250,
27 crew: 1,
28 passengers: 1,
29 cargo_capacity: 5,
30 consumables: 'unknown',
31 vehicle_class: 'repulsorcraft',
32 pilots: []
33 },
34 // + 5 more non-Imperial manufactured results, omitted for brevity
35]
Fizemos a nossa parte para restringir informações confidenciais! 🎶 Cantarolas Marcha Imperial 🎶

Restrições para UNION ALL

Agora que sabemos como o estágio$unionWithfunciona, é importante discutir seus limites e restrições.

Duplicatas

Já mencionamos isso, mas é importante reiterar: o uso do estágio$unionWith fornecerá um conjunto de resultados combinado que pode incluir duplicatas! Isso também é equivalente à forma como o operador UNION ALLfunciona emSQL. Como solução alternativa, é recomendável usar um estágio$group no final do pipeline para remover duplicatas, mas somente quando possível e se os dados resultantes não forem enviesados de forma imprecisa.
Há planos de adicionar funcionalidade semelhante ao UNION (que combina conjuntos de resultados, mas remove duplicatas), mas isso pode estar em uma versão futura.

Coleções fragmentadas

Se você usar um estágio $unionWithcomo parte de um pipeline$lookup, a coleção que você especificar para o $unionWith não poderá ser fragmentada. Como exemplo, dê uma olhada nesta agregação:
1// Invalid aggregation (tried to use sharded collection with $unionWith)
2db.lonely_planets.aggregate([
3 {
4 $lookup: {
5 from: "extinct_planets",
6 let: { last_known_population: "$population", years_extinct: "$time_extinct" },
7 pipeline: [
8 // Filter criteria
9 { $unionWith: { coll: "questionable_planets", pipeline: [ { pipeline } ] } },
10 // Other pipeline stages
11 ],
12 as: "planetdata"
13 }
14 }
15])
O coll questionable_planets (localizado dentro do estágio$unionWith ) não pode ser fragmentado. Isso é imposto para evitar uma redução significativa no desempenho devido ao embaralhamento de dados no cluster, pois ele determina o melhor plano de execução.

Transações

Os pipelines de agregação não podem usar o estágio$unionWith dentro de transações porque um raro, mas possível, pode ocorrer um deadlock de thread 3em cenários muito específicos. Além disso, no MongoDB 4.4, há uma definição inicial de uma visualização que restringiria sua leitura a partir de uma transação.
$out

e a

$merge
Os estágios $out e $merge não podem ser usados em um $unionWith pipeline . Como $out e $merge são estágios que escrevem dados em uma collection, eles precisam ser o último estágio de um pipeline. Isso entra em conflito com o uso do $unionWith estágio , pois ele envia seu conjunto de resultados combinado para o próximo estágio, que pode ser usado em qualquer ponto de um aggregation pipeline.

Agrupamentos

Se sua agregação incluir um agrupamento, esse agrupamento será utilizado para a operação, ignorando quaisquer outros agrupamentos.
No entanto, se sua agregação não incluir um agrupamento, ele usará o agrupamento para o agrupamento/visualização de nível superior no qual a agregação é executada:
  • Se o $unionWith coll for uma coleção, seu agrupamento será ignorado.
  • Se a $unionWith coll for uma visualização, seu agrupamento deverá corresponder ao da coleção/visualização de nível superior. Caso contrário, a operação apresentará erro.

Você chegou ao fim!

Discutimos o que é o estágio de pipeline do$unionWith e como você pode usá-lo em suas agregações para combinar dados de várias collection. Embora seja semelhante à operaçãoUNION ALLdo SQL, o estágio$unionWith do MongoDB se distingue por algumas características convenientes e necessárias. O mais notável é a capacidade de combinar collection com esquemas diferentes! E, como uma melhoria muito necessária, o uso de um estágio$unionWith elimina a necessidade de escrever código adicional, código que era necessário porque não havia outra maneira de combinar nossos dados!
Se você tiver alguma dúvida sobre o $unionWith estágio do pipeline ou sobre esta publicação no blog,acesse o MongoDB Community ou me twite!

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Artigo

8 Melhores práticas para criar aplicativos FastAPI e MongoDB


Apr 23, 2024 | 8 min read
Tutorial

Como construir um aplicativo web Go com Gi, MongoDB e AI


Aug 30, 2024 | 11 min read
Início rápido

Aggregation Framework com Node.js 3.3.2 Tutorial


Oct 01, 2024 | 9 min read
Início rápido

Construindo o aplicativo Quakus com MongoDB e Panache


Dec 03, 2024 | 5 min read
Sumário