MongoDB $lookup
не будет "обновлять" элементы в существующем массиве совпадениями из коллекции "lookup". Он будет только выводить «массив» совпадений с заданными критериями, будь то сопоставленный с «существующим массивом» значений, как у вас, или с единичным значением.
Чтобы «объединить» записи с помощью «серверной» операции $lookup
, вместо этого вам нужно будет выполнить одну из следующих опций, чтобы вернуться в нужную форму.
$ раскрутить массив первым
Самая простая форма - просто изменить структуру документов так, чтобы каждый элемент массива из источника был собственным документом в первую очередь , прежде чем вы действительно попытаетесь "объединить" связанную информацию:
db.studijneProgramy.aggregate([
{ "$unwind": "$garranti" },
{ "$lookup": {
"from": "osoby",
"as": "garranti.garrant",
"localField": "garranti.id",
"foreignField": "_id"
}},
{ "$unwind": "$garranti.garrant" },
{ "$group": {
"_id": "$_id",
"garranti": { "$push": "$garranti" }
}}
])
Поскольку исходный материал массива теперь представляет собой единичные документы, каждый из них получает только «массив» совпадений из объединенной коллекции. Это снова $unwind
и, наконец, использование $group
для $push
до окончательной формы массива с «объединенными» записями.
Соотнесите "массивы"
Немного интереснее в версиях, которые его поддерживают, - использовать функции $indexOfArray
и $arrayElemAt
, чтобы "совпасть" с выходным массивом $lookup
для существующих записей массива в документе:
db.studijneProgramy.aggregate([
{ "$lookup": {
"from": "osoby",
"as": "related",
"localField": "garranti.id",
"foreignField": "_id"
}},
{ "$project": {
"garranti": {
"$map": {
"input": "$garranti",
"in": {
"typ": "$$this.typ",
"id": "$$this.id",
"garrant": {
"$arrayElemAt": [
"$related",
{ "$indexOfArray": [ "$related._id", "$$this.id" ] }
]
}
}
}
}
}}
])
Таким образом, поиск возвращает «массив совпадений» (related
), и вы «просматриваете» соответствующие записи этих записей и переносите их в исходный массив документов с помощью $map
. Конечно, для изменения формы документа требуется дополнительная ступень $project
или аналогичная, поскольку вы не можете "нацелить" каждый элемент существующего массива в выводе $lookup
, как упоминалось ранее.
Это на самом деле прямая корреляция на "сервере" того, что некоторые библиотеки, такие как "mongoose", делают для "эмуляции соединения на клиенте". По сути, «чужие» записи «сопоставляются» с существующим массивом.
Обработка под-трубопровода
Немного причудливее и длиннее - это еще одна альтернатива, использующая обработку "под-конвейера" Некоррелированного подзапроса , доступного от MongoDB 3.6 и выше. Здесь мы в основном выполняем манипуляции в «под-конвейере» $lookup
вместо обработки на последующих этапах агрегирования:
db.studijneProgramy.aggregate([
{ "$lookup": {
"from": "osoby",
"as": "garranti",
"let": { "garranti": "$garranti" },
"pipeline": [
{ "$match": {
"$expr": { "$in": [ "$_id", "$$garranti.id" ] }
}},
{ "$addFields": {
"docs": {
"$filter": {
"input": "$$garranti",
"cond": {
"$eq": [ "$$this.id", "$_id" ]
}
}
}
}},
{ "$unwind": "$docs" },
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$docs",
{ "garrant": {
"$arrayToObject": {
"$filter": {
"input": { "$objectToArray": "$$ROOT" },
"cond": { "$ne": [ "$$this.k", "docs"] }
}
}
}}
]
}
}}
]
}}
])
Этот тип переворачивает операцию "над головой" и эффективно помещает "совпадающие элементы массива" из "исходного документа" в каждый соответствующий внешний элемент в виде массива.
Затем обработка эффективно использует $unwind
в отфильтрованном исходном списке, а затем объединяет содержимое из внешней коллекции, так что теперь создается впечатление, что $lookup
«выходной массив» на самом деле данные из «локального массива» теперь «объединены» с «чужим контентом».
На самом деле это просто более интересный вызов того же процесса $map
, описанного выше, но выполняющий "корреляцию" записей до , результаты объединяются с исходным родительским документом, перезаписывающим свойство исходного массива.
Я думаю, что где-то есть JIRA, но у меня вроде есть ощущение, что "работает, как задумано" отмечен во всех таких отчетах, так что вряд ли он изменится с тем, что в настоящее время делает.
Таким образом, у вас было неправильное представление о том, что «соединение» «сливается» с записями массива «автоматически». Это не так.
Если вы действительно хотите «объединить вывод массива», то подходы, описанные выше, - это «серверный» подход.