MongoDB: группируйте разные поля с одинаковым значением - PullRequest
1 голос
/ 06 мая 2020

Мне нужно выполнить агрегирование коллекции mongodb. Но я не могу найти правильный конвейер агрегации.

На самом деле мне нужно получить идентификаторы разных документов при field1 == field2 (поле 1 не в том же документе, что и поле 2).

Например, я хочу найти все документы, когда источник совпадает с целевым: document1.fieldX == document2.fieldY

Речь идет о поиске целевого сервера по номерам портов.

Примеры документов:

[ 
  {
    id: 444,
    name: 'Server 1'
    portSource: 1,
    portTarget: 7,
  },
  {
    id: 555,
    name: 'Server 2'
    portSource: 7,
    portTarget: 1
  },
  {
    id: 666,
    name: 'Server 3'
    portSource: 1,
    portTarget: 8
  },
   {
    id: 777,
    name: 'Server 4'
    portSource: 3,
    portTarget: 5
  },
 {
    id: 888,
    name: 'Server 5'
    portSource: 5,
    portTarget: 3
  },
]

Желаемый результат:

[
 {
  portSource: 1, portTarget: 7, sourceId : 444, targetId: 555
 },
 {
  portSource: 3, portTarget: 5, sourceId : 777, targetId: 888
 }
]

Изменить:

Примечание 1 : сервер с id: 666 не возвращается, потому что любой другой сервер соответствует его целевому порту.

Примечание 2 : Правильный вывод также может быть таким:

(Перевернутая цель / источник)

[
 {
  portSource: 7, portTarget: 1, sourceId : 555, targetId: 444
 },
 {
  portSource: 5, portTarget: 3, sourceId : 888, targetId: 777
 }
]

Или он может содержать дважды (Обычный способ и перевернутый):

[
 {
  portSource: 1, portTarget: 7, sourceId : 444, targetId: 555
 },
 {
  portSource: 7, portTarget: 1, sourceId : 555, targetId: 444
 },
 {
  portSource: 3, portTarget: 5, sourceId : 777, targetId: 888
 },
 {
  portSource: 5, portTarget: 3, sourceId : 888, targetId: 777
 }
]

1 Ответ

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

Вам нужно попробовать $ lookup этап конвейера агрегации в той же коллекции:

db.collectionName.aggregate([
    {
      $lookup: {
        from: "collectionName", /** Same collection name */
        let: { portSource: "$portSource" }, /** create a local variable 'portSource' from value of 'portSource' of current doc in lookup stage */
        pipeline: [ /** pipline helps to execute aggregation stages */
          { $match: { $expr: { $and: [ { $eq: [ "$portTarget", "$$portSource" ] }, { $eq: [ "$portSource", "$$portTarget" ] } ] } } }, /** match to filter docs */
          { $project: { id: 1, _id: 0 } } /** project only `id` of matched doc in a new field `targetId` */
        ],
        as: "targetId" /** will be an array, will be empty if none matches (In your case, mostly will be empty or array of single object) */
      }
    },
    /** Only Pairs will have `targetId` non-empty, removing docs where `targetId` array is empty */
    { $match: { targetId: { $ne: [] } } },
    /** create a new field `sourceId` from value of existing field `id`,
     *  re-create `targetId` from value of first elements `id` field in `targetId` array,
     *  You won't find this field for the doc where none of the docs matches for given `portSource` */
    {
      $addFields: { sourceId: "$id", targetId: {  $arrayElemAt: [ "$targetId.id", 0 ] } }
    },
    /** Removing un-necessary field */
    {
      $project: { id: 0 }
    }
  ])

Тест: mongoplayground

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