Ако сте запознати с SQL, може да знаете за UNION
клауза, която обединява резултатите от две заявки в един набор от резултати. По-специално UNION ALL
включва дубликати.
В MongoDB можем да използваме $unionWith
етап на тръбопровод на агрегация за постигане на същия ефект като UNION ALL
произвежда. $unionWith
stage изпълнява обединение на две колекции – комбинира резултатите от конвейера от две колекции в един набор от резултати. И включва дубликати.
Пример
Да предположим, че създаваме две колекции; един, наречен cats
и друг, наречен dogs
. И вмъкваме в тях следните документи:
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 }
])
Вече можем да изпълняваме заявки към тези колекции и да използваме $unionWith
етап за комбиниране на резултатите от всяка заявка.
Пример:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Резултат:
{ "_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" :"Пухкав", "тип" :"Куче", "тегло" :40 }{ "_id" :1, "име" :"Размахване", "тип" :"Куче", "тегло" :20 }{ " _id" :2, "name" :"Лая", "тип" :"Куче", "тегло" :10 }
В този пример всеки документ има поле за тип с cat
или dog
и така е съвсем очевидно кой документ от коя колекция идва.
Но ако документите нямат полето за тип, тогава би било по-трудно да се разбере къде завършва една колекция и започва друга. В този случай можем да използваме низов литерал в $set
етап за представяне на името на колекцията.
Пример:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { type: 1, weight: -1, name: 1 } }
] )
Резултат:
{ "_id" :"cat", "name" :"Meow", "type" :"Cat", "weight" :7 }{ "_id" :"cat", "name" :"Puffy" , "type" :"Cat", "weight" :5 }{ "_id" :"cat", "name" :"Scratch", "type" :"Cat", "weight" :3 }{ "_id" :"куче", "име" :"Пухкаво", "тип" :"Куче", "тегло" :40 }{ "_id" :"куче", "име" :"Размахване", "тип" :"Куче ", "тегло" :20 }{ "_id" :"куче", "име" :"Ла", "тип" :"Куче", "тегло" :10 }
Сортиране в колекции
В предишните примери котките и кучетата бяха сортирани по начин, който ги раздели на две отделни групи; първо котки, после кучета. Това се случи главно защото сортирахме по type
първо поле.
Но можем да го сортираме във всяко друго поле, което може да доведе до комбиниране на котки и кучета.
Пример:
db.cats.aggregate( [
{ $set: { _id: "cat" } },
{ $unionWith: { coll: "dogs", pipeline: [ { $set: { _id: "dog" } } ] } },
{ $sort: { name: 1 } }
] )
Резултат:
{ "_id" :"куче", "name" :"Лая", "тип" :"Куче", "тегло" :10 }{ "_id" :"котка", "име" :"Пухкав" , "тип" :"Котка", "тегло" :5 }{ "_id" :"куче", "име" :"Пухкаво", "тип" :"Куче", "тегло" :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 }
Прожекции
Можете да използвате $project
етап, за да посочите кои полета да бъдат предадени към следващия етап в конвейера. Така например можете да намалите броя на полетата, върнати от заявката.
Пример:
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} }
] )
Резултат:
{ "name" :"Fluffy" }{ "name" :"Scratch" }{ "name" :"Meow" }{ "name" :"Wag" }{ "name" :"Bark" }{ " име" :"Пухкав" }
Премахване на дубликати
Можете да използвате $group
етап за премахване на излишните дубликати от резултата.
Например, предишната заявка върна два домашни любимци, наречени Fluffy. Можем да добавим $group
етап към тази заявка, за да елиминирате излишния дубликат, така че да бъде върнат само един Fluffy.
db.cats.aggregate( [
{ $project: { name: 1, _id: 0 } },
{ $unionWith: { coll: "dogs", pipeline: [ { $project: { name: 1, _id: 0 } } ]} },
{ $group: { _id: "$name" } }
] )
Резултат:
{ "_id" :"Мяу" }{ "_id" :"Ла" }{ "_id" :"Надраскване" }{ "_id" :"Размахване" }{ "_id" :"Пухкав" }предварително>Този път се връща само един Fluffy.
Несъответстващи колони
Едно от предимствата на
$unionWith
на MongoDB има надUNION ALL
на SQL е, че може да се използва с несъответстващи колони.SQL
UNION
клаузата изисква следното:
- И двете заявки връщат един и същ брой колони
- Колоните в същия ред
- Съвпадащите колони трябва да са от съвместим тип данни
MongoDB $unionWith
етап не налага тези ограничения.
Следователно бихме могли да използваме $unionWith
да направите нещо подобно:
db.cats.aggregate( [
{ $set: { _id: "$_id" } },
{ $unionWith: { coll: "employees", pipeline: [ { $set: { _id: "$_id" } } ] } },
{ $sort: { type: 1, salary: -1 } }
] )
Резултат:
{ "_id" :2, "name" :"Сара", "заплата" :128000 }{ "_id" :5, "name" :"Бек", "заплата" :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, "име" :"Мяу", "тип" :"Котка", "тегло" :7 }
В този случай се присъединихме към cats
колекция със employees
колекция. employees
колекцията нямаше същите полета като cats
колекция, но това е добре – все още работи.