Mongodb объединяет несколько массивов

Я использую MongoDB версии v3.4. У меня есть коллекция documents, и образцы данных выглядят следующим образом:

{
    "mlVoters" : [ 
        {"email" : "[email protected]", "isApproved" : false}
    ],
    "egVoters" : [ 
        {"email" : "[email protected]", "isApproved" : false}, 
        {"email" : "[email protected]", "isApproved" : true}
    ]
},{
    "mlVoters" : [ 
        {"email" : "[email protected]", "isApproved" : false}, 
        {"email" : "[email protected]", "isApproved" : true}
    ],
    "egVoters" : [ 
        {"email" : "[email protected]", "isApproved" : true}
    ]
}

Теперь, если я хочу подсчитать количество различных адресов электронной почты для mlVoters:

db.documents.aggregate([
  {$project: { mlVoters: 1 } },
  {$unwind: "$mlVoters" },
  {$group: { _id: "$mlVoters.email", mlCount: { $sum: 1 } }},
  {$project: { _id: 0, email: "$_id", mlCount: 1 } },
  {$sort: { mlCount: -1 } }
])

Результат запроса:

{"mlCount" : 2.0,"email" : "[email protected]"}
{"mlCount" : 1.0,"email" : "[email protected]"}

И если мне нужно количество различных адресов электронной почты для egVoters, я делаю то же самое для поля egVoters. Результатом этого запроса будет:

{"egCount" : 1.0,"email" : "[email protected]"}
{"egCount" : 1.0,"email" : "[email protected]"}
{"egCount" : 1.0,"email" : "[email protected]"}

Итак, я хочу объединить эти две агрегации и получить следующий результат (отсортированный по totalCount):

{"email" : "[email protected]", "mlCount" : 2, "egCount" : 1, "totalCount":3}
{"email" : "[email protected]", "mlCount" : 1, "egCount" : 1, "totalCount":2}
{"email" : "[email protected]", "mlCount" : 0, "egCount" : 1, "totalCount":1}

Как я могу это сделать? Каким должен быть запрос? Спасибо.

Использование JavaScript и MongoDB
Использование JavaScript и MongoDB
Сегодня я собираюсь вкратце рассказать о прототипах в JavaScript, а также представить и объяснить вам работу с базой данных MongoDB.
0
0
969
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Сначала вы добавляете поле voteType в каждый голос. В этом поле указывается его тип. Имея это поле, вам не нужно хранить голоса в двух отдельных массивах mlVoters и egVoters; вместо этого вы можете объединить эти массивы в один массив для каждого документа, а затем развернуть его.

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

Наконец, вы добавляете поле totalCount как сумму двух других подсчетов.

db.documents.aggregate([
  {
    $addFields: {
      mlVoters: {
        $ifNull: [ "$mlVoters", []]
      },
      egVoters: {
        $ifNull: [ "$egVoters", []]
      }
    }
  },
  {
    $addFields: {
      "mlVoters.voteType": "ml",
      "egVoters.voteType": "eg"
    }
  },
  {
    $project: {
      voters: { $concatArrays: ["$mlVoters", "$egVoters"] }
    }
  },
  {
    $unwind: "$voters"
  },
  {
    $project: {
      email: "$voters.email",
      voteType: "$voters.voteType"
    }
  },
  {
    $group: {
      _id: "$email",
      mlCount: {
        $sum: {
          $cond: {
            "if": { $eq: ["$voteType", "ml"] },
            "then": 1,
            "else": 0
          }
        }
      },
      egCount: {
        $sum: {
          $cond: {
            "if": { $eq: ["$voteType", "eg"] },
            "then": 1,
            "else": 0
          }
        }
      }
    }
  },
  {
    $addFields: {
      totalCount: {
        $sum: ["$mlCount", "$egCount"]
      }
    }
  }
])

Ваш ответ выглядит вполне разумным, но он дает мне "errmsg" : "$concatArrays only supports arrays, not object". Это потому, что вместо простого массива они представляют собой массив объектов?

Selcuk 25.10.2018 01:48

Есть ли у вас в коллекции какой-либо документ, в котором нет массивов egVoters и mlVoters, даже если они пустые? Это объяснило бы эту ошибку, потому что в этом случае $addFields: { "mlVoters.voteType": "ml" } создаст поле типа "mlVoters" : { "voteType" : "ml" }. Вы можете решить эту проблему, сначала создав (пустые) массивы, если они не существуют.

abl 25.10.2018 01:58

Да. Как я могу создавать пустые массивы, если их не существует?

Selcuk 25.10.2018 02:01

Другие вопросы по теме