Каков синтаксис для выполнения поиска $ в поле, которое является массивом ObjectIds, а не просто одним ObjectId?
Пример документа для заказа:
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
]
}
Не рабочий запрос:
db.orders.aggregate([
{
$lookup:
{
from: "products",
localField: "products",
foreignField: "_id",
as: "productObjects"
}
}
])
Желаемый результат
{
_id: ObjectId("..."),
products: [
ObjectId("..<Car ObjectId>.."),
ObjectId("..<Bike ObjectId>..")
],
productObjects: [
{<Car Object>},
{<Bike Object>}
],
}
mongodb
mongodb-query
aggregation-framework
Джейсон Лин
источник
источник
Ответы:
Обновление 2017 г.
$ lookup теперь может напрямую использовать массив в качестве локального поля .
$unwind
больше не нужен.Старый ответ
Этап
$lookup
конвейера агрегации не работает напрямую с массивом. Основная цель дизайна - «левое соединение» как тип соединения «один ко многим» (или действительно «поиск») возможных связанных данных. Но значение должно быть единичным, а не массивом.Поэтому перед выполнением
$lookup
операции вы должны «денормализовать» контент, чтобы это работало. А это означает использование$unwind
:После
$lookup
сопоставления каждого члена массива результатом является сам массив, поэтому вы$unwind
снова и снова переходите$group
к$push
новым массивам для окончательного результата.Обратите внимание, что любые совпадения «левого соединения», которые не найдены, создадут пустой массив для «productObjects» данного продукта и, таким образом, отменит документ для элемента «product» при
$unwind
вызове второго .Хотя прямое приложение к массиву было бы неплохо, это именно то, как это работает сейчас, сопоставляя единичное значение с возможным множеством.
Поскольку
$lookup
это в основном очень новый метод , он в настоящее время работает так, как это было бы знакомо тем, кто знаком с мангустом, как с «версией для бедняков».populate()
предлагаемого там метода. Разница в том, что$lookup
предлагает обработку «соединения» «на стороне сервера», а не на клиенте, и что в$lookup
настоящее время отсутствует некоторая «зрелость» в том, какие.populate()
предложения (например, интерполяция поиска непосредственно в массиве).Это фактически назначенная проблема для улучшения SERVER-22881 , поэтому, если повезет, это коснется следующего выпуска или вскоре после него.
Как принцип дизайна, ваша текущая структура не является ни хорошей, ни плохой, а просто требует накладных расходов при создании любого "соединения". Таким образом, с самого начала применяется основной постоянный принцип MongoDB: если вы «можете» жить с данными, «предварительно объединенными» в одной коллекции, то лучше сделать это.
Еще одна вещь, которую можно назвать
$lookup
общим принципом, заключается в том, что цель "соединения" здесь - работать наоборот, чем показано здесь. Таким образом, вместо того, чтобы хранить «связанные идентификаторы» других документов в «родительском» документе, лучше всего работает общий принцип, когда «связанные документы» содержат ссылку на «родительский».Таким образом,
$lookup
можно сказать, что «лучше всего работает» с «дизайном отношений», который противоположен тому, как что-то вроде mongoose.populate()
выполняет соединения на стороне клиента. Идентифицируя вместо этого «один» в каждом «множестве», вы просто извлекаете связанные элементы без необходимости$unwind
сначала обращаться к массиву.источник
$lookup
и проверке документов как к функциям, находящимся в зачаточном состоянии и которые, вероятно, улучшатся. Таким образом, прямое расширение массива будет приветствоваться, как и «запрос» для фильтрации результатов. И то, и другое было бы гораздо более увязано с.populate()
процессом мангуста, к которому многие привыкли. Добавление ссылки на проблему непосредственно в содержание ответа.$lookup
теперь работает непосредственно с массивом.Начиная с MongoDB v3.4 (выпущенного в 2016 году),
$lookup
этап конвейера агрегации также может работать напрямую с массивом . Больше в этом нет необходимости$unwind
.Это было отслежено в SERVER-22881 .
источник
Вы также можете использовать
pipeline
сцену для проверки массива субдокументов.Вот пример использования
python
(извините, я змея).Уловка здесь заключается в том, чтобы сопоставить все объекты в
ObjectId
array
(чужом,_id
который находится вlocal
поле / опореproducts
).Вы также можете очистить или спроецировать внешние записи с дополнительными
stage
s, как указано в комментарии выше.источник
используйте $ unwind, вы получите первый объект вместо массива объектов
запрос:
результат:
источник
Агрегирование с
$lookup
и последующим$group
довольно громоздким делом, поэтому, если (и это средний, если) вы используете node и Mongoose или вспомогательную библиотеку с некоторыми подсказками в схеме, вы можете использовать a.populate()
для получения этих документов:источник
Я должен не согласиться, мы можем заставить $ lookup работать с массивом идентификаторов, если мы поставим перед ним стадию $ match.
Это становится более сложным, если мы хотим передать результат поиска конвейеру. Но опять же, есть способ сделать это (уже предложенный @ user12164):
источник