Mongodb объединяет несколько строк на основе вычисленного условия на значении строки - PullRequest
0 голосов
/ 07 мая 2018

У меня есть пример данных, как это:

[
  { objectId: 1, user: 1, phones: [1, 2], emails: ['a'] },
  { objectId: 2, user: 1, phones: [1, 5], emails: ['a', 'f'] },
  { objectId: 3, user: 1, phones: [8, 9], emails: ['f', 'g'] },
  { objectId: 4, user: 1, phones: [10], emails: ['h'] },
  { objectId: 5, user: 2, phones: [1, 2, 3], emails: ['aa', 'bb', cc'] },
]

Теперь мне нужно объединить все связанные строки в одну при следующих условиях:

  • У того же пользователя
  • Иметь хотя бы один общий телефон или электронную почту

Итак, выведите что-то вроде этого:

[
  { objectId: 1, user: 1, phones: [1, 2, 5, 8, 9], emails: ['a', 'f', 'g'] },
  { objectId: 4, user: 1, phones: [10], emails: ['h'] },
  { objectId: 5, user: 2, phones: [1, 2, 3], emails: ['aa', 'bb', cc'] },
]

Это то, что я придумал до сих пор:

[
  {
    $unwind: {
      path: "$phones",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $group: {
      _id: {
        user: "$user",
        phone: "$phones"
      },
      objectIds: {
        $addToSet: "$_id"
      },
      emailsList: {
        $push: "$emails"
      },
      user: { $first: "$user" },
      phones: {
        $first: "$phones"
      }
    }
  },
  {
    "$addFields": {
      "emails": {
        "$reduce": {
          "input": "$emailsList",
          "initialValue": [],
          "in": { "$setUnion": ["$$value", "$$this"] }
        }
      }
    }
  },

  {
    "$project": {
      "emailsList": 0
    }
  },
  {
    $unwind: {
      path: "$emails",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $group: {
      _id: {
        user: "$user",
        phone: "$emails"
      },
      objectIdsList: {
        $push: "$objectIds"
      }
    }
  },
  {
    "$project": {
      "mergedObjectIds": {
        "$reduce": {
          "input": "$objectIdsList",
          "initialValue": [],
          "in": { "$setUnion": ["$$value", "$$this"] }
        }
      }
    }
  }
]

И затем у нас есть список объектных идентификаторов, которые необходимо объединить, тогда я объединю все это в коде приложения. Так что в любом случае я могу сделать это в одной структуре агрегации или передать результат этого агрегата следующему

1 Ответ

0 голосов
/ 07 мая 2018

Если я что-то упустил, это просто "наборы" для каждого пользователя. Так что просто размотайте оба массива и накопите через $addToSet для каждого из "phones" и "emails":

db.collection.aggregate([
  { "$unwind": "$phones" },
  { "$unwind": "$emails" },
  { "$group": {
    "_id": "$user",
    "phones": { "$addToSet": "$phones" },
    "emails": { "$addToSet": "$emails" }
  }}
])

Что возвращает:

{ "_id" : 2, "phones" : [ 3, 2, 1 ], "emails" : [ "cc", "bb", "aa" ] }
{ "_id" : 1, "phones" : [ 9, 1, 2, 5, 8 ], "emails" : [ "g", "f", "a" ] }

«Набор» на самом деле не считается «заказанным», поэтому, если вы ожидаете определенного заказа, вам нужно отсортировать его в другом месте, и, вероятно, лучше всего в клиенте.

Любые «уникальные» идентификаторы здесь не применяются. В любом случае вы бы использовали другой аккумулятор, такой как $min или $max, или, может быть, $first в зависимости от того, что вы хотите, однако единственный соответствующий подробности, которые я вижу здесь, это "user" для группировки и других накопленных «установленных» значений.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...