Актуализация за 2017 г.
$lookup вече може директно да използва масив като локално поле. $unwind
вече не е необходимо.
Стар отговор
$lookup
Етапът на конвейера на агрегация няма да работи директно с масив. Основната цел на дизайна е за "ляво присъединяване" като тип "един към много" (или наистина "търсене") на възможните свързани данни. Но стойността е предназначена да бъде единична, а не масив.
Следователно първо трябва да "денормализирате" съдържанието, преди да извършите $lookup
операция, за да работи това. А това означава да използвате $unwind
:
db.orders.aggregate([
// Unwind the source
{ "$unwind": "$products" },
// Do the lookup matching
{ "$lookup": {
"from": "products",
"localField": "products",
"foreignField": "_id",
"as": "productObjects"
}},
// Unwind the result arrays ( likely one or none )
{ "$unwind": "$productObjects" },
// Group back to arrays
{ "$group": {
"_id": "$_id",
"products": { "$push": "$products" },
"productObjects": { "$push": "$productObjects" }
}}
])
След $lookup
съответства на всеки член на масива, резултатът е самия масив, така че $unwind
отново и $group
до $push
нови масиви за крайния резултат.
Обърнете внимание, че всички съвпадения на "лявото присъединяване", които не са намерени, ще създадат празен масив за "productObjects" на дадения продукт и по този начин ще отричат документа за елемента "product", когато вторият $unwind
се нарича.
Въпреки че директното приложение към масив би било хубаво, това е начинът, по който в момента работи, като съпоставя единична стойност с възможни много.
Като $lookup
по принцип е много нов, в момента работи както би било познато на тези, които са запознати с mongoose като "версия за бедни хора" на .populate()
предлаган там метод. Разликата е, че $lookup
предлага обработка от страна на сървъра на "join" за разлика от клиентската и че част от "зрелостта" в $lookup
в момента липсва от това, което .populate()
оферти (като интерполиране на търсенето директно в масив).
Това всъщност е възложен проблем за подобрение на SERVER-22881, така че с малко късмет ще се появи следващата версия или скоро след това.
Като принцип на проектиране, текущата ви структура не е нито добра, нито лоша, а просто подлежи на режийни разходи при създаване на каквото и да е "присъединяване". Като такъв се прилага основният постоянен принцип на MongoDB в началото, където ако „можете“ да живеете с данните, „предварително присъединени“ в една колекция, най-добре е да го направите.
Другото нещо, което може да се каже за $lookup
като общ принцип е, че целта на "присъединяването" тук е да работи обратното, отколкото е показано тук. Така че вместо да се съхраняват „свързаните идентификатори“ на другите документи в „родителския“ документ, общият принцип, който работи най-добре, е, когато „свързаните документи“ съдържат препратка към „родителя“.
Така че $lookup
може да се каже, че "работи най-добре" с "дизайн на релации", който е обратен на нещо като mongoose .populate()
извършва свързвания от страна на клиента. Като вместо това идентифицирате "един" във всяко "много", тогава просто изтегляте свързаните елементи, без да е необходимо да $unwind
първо масива.