$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>}
],
}
690   5  

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

    Ничего не найдено.