Выполнение $ lookup на основе сопоставления атрибута объекта в массиве другой коллекции - PullRequest
0 голосов
/ 26 апреля 2018

Я пытаюсь выполнить $lookup для сбора с условиями, проблема, с которой я сталкиваюсь, состоит в том, что я хочу сопоставить поле text всех объектов, которые находятся внутри массива (массив accounts), с другими ( plates) коллекция. Я пытался использовать $map, а также $in и $setIntersection, но ничего не работает. И я не могу найти способ сопоставления текстовых полей каждого из объектов в массиве.

Мои структуры документов следующие:

plates коллекция:

{
    "_id": "Batch 1",
    "rego" : "1QX-WA-123",
    "date" : 1516374000000.0
    "accounts": [{
        "text": "Acc1",
        "date": 1516374000000
    },{
        "text": "Acc2",
        "date": 1516474000000
    }]
}

accounts коллекция:

{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0
}

Я пытаюсь достичь чего-то вроде этого:

{
    $lookup: { 
        from: 'plates',
        let: { 'accountId': '$_id' },
        pipeline: [{
            '$match': {
                '$expr': { '$and': [ 
                    { '$eq': [ '$account.text', '$$accountId' ] }, 
                    { '$gte': [ '$date', ISODate ("2016-01-01T00:00:00.000Z").getTime() ] },
                    { '$lte': [ '$date', ISODate ("2019-01-01T00:00:00.000Z").getTime() ] }
                ]}
            }
        }],
        as: 'cusips' 
    }
},

Вывод, который я пытаюсь получить:

{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0,
    "plates": [{
        "_id": "Batch 1",
        "rego": "1QX-WA-123"
     }]
}

Ответы [ 2 ]

0 голосов
/ 27 апреля 2018

Лично я бы начал агрегацию из коллекции "plates", где начальные условия $match могут более четко фильтровать диапазон дат. Чтобы получить желаемый результат, достаточно просто «раскрутить» результирующие "accounts" совпадения и «инвертировать» содержимое.

Достаточно просто с функциями MongoDB 3.6, которые вы должны иметь, чтобы использовать $lookup с $expr. Нам даже не нужна эта форма для $lookup здесь:

db.plates.aggregate([
  { "$match": {
    "date": { 
      "$gte": new Date("2016-01-01").getTime(),
      "$lte": new Date("2019-01-01").getTime()
    }
  }},
  { "$lookup": {
    "from": "accounts",
    "localField": "accounts.text",
    "foreignField": "_id",
    "as": "accounts"
  }},
  { "$unwind": "$accounts" },
  { "$group": {
    "_id": "$accounts",
    "plates": { "$push": { "_id": "$_id", "rego": "$rego" } }
  }},
  { "$replaceRoot": {
    "newRoot": {
      "$mergeObjects": ["$_id", { "plates": "$plates" }]
    }
  }}
])

Это, конечно, «ВНУТРЕННЕЕ СОЕДИНЕНИЕ», которое будет возвращать только "accounts" записей, где МАТ

Выполнение «соединения» из коллекции "accounts" означает, что вам требуется дополнительная обработка для удаления несовпадающих записей из массива "accounts" в коллекции "plates":

db.accounts.aggregate([
  { "$lookup": {
    "from": "plates",
    "let": { "account": "$_id" },
    "pipeline": [
      { "$match": {
        "date": { 
          "$gte": new Date("2016-01-01").getTime(),
          "$lte": new Date("2019-01-01").getTime()
        },
        "$expr": { "$in": [ "$$account", "$accounts.text" ] }
      }},
      { "$project": { "_id": 1, "rego": 1 } }
    ],
    "as": "plates"
  }}
])

Обратите внимание, что $match в свойствах "date" должны быть выражены как обычное условие запроса, а не в блоке $expr для оптимальной производительности запроса.

$in используется для сравнения «массива» значений "$accounts.text" с локальной переменной, определенной для значения "_id" документа "accounts", к которому присоединяется. Таким образом, первый аргумент $in является «единственным» значением, а второй - «массивом» только "text" значений, которые должны совпадать.

Это также, в частности, «LEFT JOIN», который возвращает все "accounts" независимо от того, есть ли совпадения "plates" с условиями, и, следовательно, вы можете получить пустой массив "plates" в возвращаемых результатах , Вы можете отфильтровать их, если они вам не нужны, но в этом случае прежняя форма запроса действительно намного эффективнее, чем эта, поскольку отношение определено, и мы когда-либо имеем дело только с "plates", который соответствует критериям .

Любой метод возвращает тот же ответ из данных, представленных в вопросе:

{
        "_id" : "Acc1",
        "date" : 1516374000000,
        "createdAt" : 1513810712802,
        "plates" : [
                {
                        "_id" : "Batch 1",
                        "rego" : "1QX-WA-123"
                }
        ]
}

В каком направлении вы на самом деле выбираете это, зависит от того, действительно ли вам нужна форма соединения «ВЛЕВО» или «ВНУТРЕННЯЯ», а также от того, где можно создать наиболее эффективные условия запроса для элементов, которые вы на самом деле хотите выбрать.

0 голосов
/ 26 апреля 2018

Хм, не уверен, как вы пробовали $ в , но у меня это работает:

{
    $lookup: { 
        from: 'plates',
        let: { 'accountId': '$_id' },
        pipeline: [{
            '$match': {
                '$expr':  { '$and': [ 
                    { '$in': [ '$$accountId',  '$accounts.text'] },
                    { '$gte': [ '$date', ISODate ("2016-01-01T00:00:00.000Z").getTime() ] },
                    { '$lte': [ '$date', ISODate ("2019-01-01T00:00:00.000Z").getTime() ] }
                ]}
            }, 
        }],
        as: 'cusips' 
    } 
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...