$lookup на ObjectId в массиве
каков синтаксис для выполнения $ lookup в поле, которое является массивом 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>}
],
}
5 ответов:
The
$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новые массивы для конечного результата.обратите внимание, что любые совпадения "left join", которые не найдены, создадут пустой массив для " productObjects "на данном продукте и, таким образом, отрицают документ для элемента" product", когда второй
$unwind- это называемый.хотя прямое приложение к массиву было бы неплохо, это просто, как это работает в настоящее время, сопоставляя сингулярное значение с возможными многими.
как
$lookupв основном очень новый, он в настоящее время работает так, как было бы знакомо тем, кто знаком с мангуста как "бедный человек версия".populate()метод, предложенный там. Разница в том, что$lookupпредлагает" серверную "обработку " соединения", а не на клиенте и что некоторые из "зрелости" в$lookupв настоящее время не хватает от чего.populate()предложения (например, интерполяция поиска непосредственно в массиве ).это на самом деле назначенный вопрос для улучшения сервер-22881, так что с некоторой удачей это попадет в следующий релиз или один вскоре после этого.
как принцип проектирования, ваша текущая структура не является ни хорошей, ни плохой, а просто подвержена накладным расходам при создании любого "соединения". Как таковой, основное положение применяется принцип MongoDB в inception, где если вы "можете" жить с данными, "предварительно Соединенными" в одной коллекции, то лучше всего это сделать.
еще одна вещь, о которой можно сказать
$lookupкак общий принцип, заключается в том, что намерение "присоединиться" здесь заключается в том, чтобы работать наоборот, чем показано здесь. Поэтому вместо того, чтобы хранить "связанные идентификаторы" других документов в "Родительском" документе, общий принцип, который лучше всего работает, заключается в том, что "связанные документы" содержат ссылка на "родителя".так
$lookupможно сказать, что "лучше всего работает" с "дизайном отношений", что является обратной стороной того, как что-то вроде мангуста.populate()выполняет это соединение на стороне клиента. Вместо этого, идендифицируя "один "в каждом" много", вы просто вытаскиваете связанные элементы без необходимости$unwindпервый массив.
на
$lookupэтап конвейера агрегации теперь работает непосредственно с массивом (on Версия 3.3.4).посмотреть: поиск между локальным (множественным)массивом значений и внешним (одиночным) значением
использовать $unwind вы получите первый объект вместо массива объектов
запрос:
db.getCollection('vehicles').aggregate([ { $match: { status: "AVAILABLE", vehicleTypeId: { $in: Array.from(newSet(d.vehicleTypeIds)) } } }, { $lookup: { from: "servicelocations", localField: "locationId", foreignField: "serviceLocationId", as: "locations" } }, { $unwind: "$locations" } ]);результат:
{ "_id" : ObjectId("59c3983a647101ec58ddcf90"), "vehicleId" : "45680", "regionId" : 1.0, "vehicleTypeId" : "10TONBOX", "locationId" : "100", "description" : "Isuzu/2003-10 Ton/Box", "deviceId" : "", "earliestStart" : 36000.0, "latestArrival" : 54000.0, "status" : "AVAILABLE", "accountId" : 1.0, "locations" : { "_id" : ObjectId("59c3afeab7799c90ebb3291f"), "serviceLocationId" : "100", "regionId" : 1.0, "zoneId" : "DXBZONE1", "description" : "Masafi Park Al Quoz", "locationPriority" : 1.0, "accountTypeId" : 0.0, "locationType" : "DEPOT", "location" : { "makani" : "", "lat" : 25.123091, "lng" : 55.21082 }, "deliveryDays" : "MTWRFSU", "timeWindow" : { "timeWindowTypeId" : "1" }, "address1" : "", "address2" : "", "phone" : "", "city" : "", "county" : "", "state" : "", "country" : "", "zipcode" : "", "imageUrl" : "", "contact" : { "name" : "", "email" : "" }, "status" : "", "createdBy" : "", "updatedBy" : "", "updateDate" : "", "accountId" : 1.0, "serviceTimeTypeId" : "1" } } { "_id" : ObjectId("59c3983a647101ec58ddcf91"), "vehicleId" : "81765", "regionId" : 1.0, "vehicleTypeId" : "10TONBOX", "locationId" : "100", "description" : "Hino/2004-10 Ton/Box", "deviceId" : "", "earliestStart" : 36000.0, "latestArrival" : 54000.0, "status" : "AVAILABLE", "accountId" : 1.0, "locations" : { "_id" : ObjectId("59c3afeab7799c90ebb3291f"), "serviceLocationId" : "100", "regionId" : 1.0, "zoneId" : "DXBZONE1", "description" : "Masafi Park Al Quoz", "locationPriority" : 1.0, "accountTypeId" : 0.0, "locationType" : "DEPOT", "location" : { "makani" : "", "lat" : 25.123091, "lng" : 55.21082 }, "deliveryDays" : "MTWRFSU", "timeWindow" : { "timeWindowTypeId" : "1" }, "address1" : "", "address2" : "", "phone" : "", "city" : "", "county" : "", "state" : "", "country" : "", "zipcode" : "", "imageUrl" : "", "contact" : { "name" : "", "email" : "" }, "status" : "", "createdBy" : "", "updatedBy" : "", "updateDate" : "", "accountId" : 1.0, "serviceTimeTypeId" : "1" } }
вы также можете использовать этап конвейера для проверки массива
вот пример использования python (извините, что я змеи люди).
db.products.aggregate([ {'$loookup': {'from': 'products', 'let': {'pid': '$products'}, 'pipeline': [ {'$match': {'expr': {'$in': ['$_id', '$$pid']}}} # Additional stages here ], 'as':'productObjects' }} ])здесь загвоздка в том, чтобы соответствовать все объекты в массиве аргумента object_id (иностранные 'параметр _id' то есть в местных "продуктах").
вы также можете очистить или проецировать внешние записи с дополнительными этапами.
агрегация с
$lookupи в последующем$groupдовольно громоздко, поэтому если (и это среда, если) вы используете node & Mongoose или вспомогательную библиотеку с некоторыми подсказками в схеме, вы можете использовать.populate()чтобы получить эти документы:var mongoose = require("mongoose"), Schema = mongoose.Schema; var productSchema = Schema({ ... }); var orderSchema = Schema({ _id : Number, products: [ { type: Schema.Types.ObjectId, ref: "Product" } ] }); var Product = mongoose.model("Product", productSchema); var Order = mongoose.model("Order", orderSchema); ... Order .find(...) .populate("products") ...
Comments