Пробовали ли вы http://simplefocus.com/flowtype/ ?
Это то, что я использую для своих сайтов и отлично работаю. Надеюсь, что это поможет.
Конвейер агрегации $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
в основном очень новый, он в настоящее время работает так же, как и для тех, кто знаком с mongoose как «версия для бедных мужчин» предложенного там метода .populate()
. Разница заключается в том, что $lookup
предлагает обработку на стороне сервера «join» в отличие от клиента и что некоторая «зрелость» в $lookup
в настоящее время отсутствует из того, что предлагает .populate()
(например, интерполяция lookup непосредственно на массив).
Это фактически назначенная проблема для улучшения SERVER-22881 , поэтому с некоторой удачей это ударит по следующей версии или вскоре после этого.
Как принцип проектирования, ваша текущая структура не является ни хорошей, ни плохой, но просто подлежит накладным расходам при создании любого «соединения». Таким образом, применяется основной принцип принципа MongoDB в начале, где, если вы «можете» жить с данными «предварительно объединенными» в одной коллекции, тогда лучше всего это сделать.
другая вещь, которую можно сказать о $lookup
как общий принцип, заключается в том, что целью «присоединиться» здесь является работа по-другому, чем показано здесь. Поэтому вместо того, чтобы хранить «связанные идентификаторы» других документов в «родительском» документе, общий принцип, который лучше всего работает, - это где «связанные документы» содержат ссылку на «родительский».
So $lookup
можно сказать, что «лучше работать» с «дизайном отношений», который является обратным тому, как что-то вроде mongoose .populate()
выполняет, он присоединяется к стороне клиента. Идентифицируя «один» внутри каждого «много», вместо этого вы просто втягиваете связанные элементы, не требуя сначала $unwind
массива.
Агрегация с $lookup
и последующим $group
довольно громоздка, поэтому, если (и это среда, если), вы используете node & amp; 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")
...
использовать $ unwind, вы получите первый объект вместо массива объектов
query:
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"
}
]);
result:
{
"_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"
}
}
Сценарий конвейера агрегации $lookup
NOW работает непосредственно с массивом (в версии 3.3.4).
Смотрите: поиск между локальным (множественным) массивом значений и внешним (одиночным) значение
Вы также можете использовать этап конвейера для проверки массива
. Вот пример использования python (извините, что я змеиный человек).
db.products.aggregate([
{'$loookup':
{'from': 'products',
'let': {'pid': '$products'},
'pipeline': [
{'$match': {'expr': {'$in': ['$_id', '$$pid']}}}
# Additional stages here
],
'as':'productObjects'
}}
])
Ловушка здесь соответствуют всем объектам в массиве object_id (foreign '_id', который находится в локальных «продуктах»).
Вы также можете очистить или проецировать чужие записи с дополнительными этапами.
$lookup
и Document Validation как функции в раннем детстве и, вероятно, улучшатся. Поэтому будет приветствоваться прямое расширение массива, как и запрос «запрос». для фильтрации результатов. Оба из них были бы намного более согласованы с процессом mongoose.populate()
, к которому многие привыкли. Добавление ссылки проблемы непосредственно в контент ответа. – Blakes Seven 16 March 2016 в 23:29