Нахождение пересечения между двумя массивами объектов на основе поля - PullRequest
0 голосов
/ 25 апреля 2018

В коллекции mongo у меня есть документы следующей структуры.

{
    "_id" : "Suzuki",
    "qty" : 10,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123",
        }, 
        {
            "_id" : "2Wqrd4yofvLmqLm5H",
            "createdAt" : 1513810712802.0,
            "date" : 1501632000000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-111",
        }
    ]
}

Я пытаюсь отфильтровать объекты в массиве accounts, чтобы он содержал только те объекты, чей rego существует в массиве plates.

Я пробовал следующий запрос, однако он выдает ошибку: all operands of $setIntersection must be arrays. One argument if of type object.

db.getCollection('dummy').aggregate([{
    $project: {
        plates: 1, 
        accounts: 1,
        intersect: {
            $setIntersection: [
                { $arrayElemAt: [ "$plates", 0 ] },
                { $arrayElemAt: [ "$accounts", 4 ] }
            ]
        }
    }
}])

Ожидаемый вывод, который я ищу:

{
    "_id" : "Suzuki",
    "qty" : 10,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123",
        }
    ]
}

1 Ответ

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

Итак, есть несколько способов, но вместо этого вы просто набираете $filter.

Использование $in, скорее всего, будетпервый выбор:

db.getCollection('dummy').aggregate([
  { "$addFields": {
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "cond": {
          "$in": [ "$$this.rego", "$plates.rego" ]
        }
      }
    }
  }}
])

Или, если у вас не установлен MongoDB 3.4, то используйте $anyElementTrue:

db.getCollection('dummy').aggregate([
  { "$project": {
    "qty": 1,
    "plates": 1,
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "as": "acc",
        "cond": {
          "$anyElementTrue": {
            "$map": {
              "input": "$plates.rego",
              "as": "rego",
              "in": { "$eq": [ "$$rego", "$$acc.rego" ] }
            }
          }
        }
      }
    }
  }}
])

или даже $setIsSubset:

db.getCollection('dummy').aggregate([
  { "$project": {
    "qty": 1,
    "plates": 1,
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "as": "acc",
        "cond": {
          "$setIsSubset": [ ["$$acc.rego"], "$plates.rego" ]
        }
      }
    }
  }}
])

Это действительно не $setIntersection для этого типа операции, так как для этого потребуется сравнение «только значений поля», так как«множество», и результат на самом деле является просто «этим», а не «объектами».

Вы могли бы сделать что-то глупое, сопоставив индексы массива с произведенными «установленными» позициями:

db.getCollection('dummy').aggregate([
  { "$addFields": {
    "accounts": {
      "$map": { 
        "input": { "$setIntersection": ["$plates.rego", "$accounts.rego"] },
        "in": {
          "$arrayElemAt": [
            "$accounts",
            { "$indexOfArray": [ "$accounts.rego", "$$this" ] }      
          ]
        }
      }
    }
  }}
])

Но в действительности вы, вероятно, просто хотите, чтобы результат $filter был гораздо более практичным.И если вы хотите, чтобы этот вывод был «установленным», то вы можете просто обернуть вывод $filter с помощью оператора $setDifference или аналогичного, чтобы сделать записи «уникальными».

Во всех вариациях они возвращают:

{
    "_id" : "Suzuki",
    "qty" : 10.0,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123"
        }
    ]
}

Отображение элементов в массиве "accounts" "отфильтрованных" в соответствии с соответствующими "rego" суммами из "plates" массив.

...