$ lookup on array, не работающий в mongodb v3.2 [duplicate]

Пробовали ли вы http://simplefocus.com/flowtype/ ?

Это то, что я использую для своих сайтов и отлично работаю. Надеюсь, что это поможет.

51
задан Blakes Seven 23 January 2016 в 21:29
поделиться

5 ответов

Конвейер агрегации $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 массива.

80
ответ дан Blakes Seven 20 August 2018 в 10:19
поделиться
  • 1
    Спасибо, это работает! Является ли это индикатором того, что мои данные не структурированы / нормализованы должным образом? – Jason Lin 23 January 2016 в 21:33
  • 2
    @JasonLin Не так сильно, как «хороший / плохой», поэтому в ответ добавляется немного больше объяснений. Это зависит от того, что вам подходит. – Blakes Seven 23 January 2016 в 21:48
  • 3
    текущая реализация несколько непреднамеренно. имеет смысл искать все значения в массиве локального поля, не имеет смысла использовать массив буквально, поэтому SERVER-22881 будет отслеживать исправление этого. – Asya Kamsky 16 March 2016 в 23:05
  • 4
    @AsyaKamsky Это имеет смысл. Обычно я рассматриваю запросы re $lookup и Document Validation как функции в раннем детстве и, вероятно, улучшатся. Поэтому будет приветствоваться прямое расширение массива, как и запрос «запрос». для фильтрации результатов. Оба из них были бы намного более согласованы с процессом mongoose .populate(), к которому многие привыкли. Добавление ссылки проблемы непосредственно в контент ответа. – Blakes Seven 16 March 2016 в 23:29
  • 5
    Спасибо, тон. Хотя, я все еще чувствую, что присоединение довольно сложно, когда приходит в MongoDB по сравнению с реляционными решениями БД. Но, опять же, MongoDB не предназначен для таких операций. – zubair1024 12 June 2017 в 19:07

Агрегация с $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")
    ...
-1
ответ дан Archimedix 20 August 2018 в 10:19
поделиться

использовать $ 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"
    }
}
3
ответ дан KARTHIKEYAN.A 20 August 2018 в 10:19
поделиться

Сценарий конвейера агрегации $lookup NOW работает непосредственно с массивом (в версии 3.3.4).

Смотрите: поиск между локальным (множественным) массивом значений и внешним (одиночным) значение

33
ответ дан styvane 20 August 2018 в 10:19
поделиться
  • 1
    спасибо, это облегчает мою жизнь – vaibhavmaster 17 June 2017 в 21:33
  • 2
    нет, похоже, не работает! все еще у меня такая же проблема – Lionel Dcosta 10 January 2018 в 18:46

Вы также можете использовать этап конвейера для проверки массива

. Вот пример использования 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', который находится в локальных «продуктах»).

Вы также можете очистить или проецировать чужие записи с дополнительными этапами.

1
ответ дан user12164 20 August 2018 в 10:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: