Я использую 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}
Как я могу это сделать? Каким должен быть запрос? Спасибо.
Сначала вы добавляете поле 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"]
}
}
}
])
Есть ли у вас в коллекции какой-либо документ, в котором нет массивов egVoters
и mlVoters
, даже если они пустые? Это объяснило бы эту ошибку, потому что в этом случае $addFields: { "mlVoters.voteType": "ml" }
создаст поле типа "mlVoters" : { "voteType" : "ml" }
. Вы можете решить эту проблему, сначала создав (пустые) массивы, если они не существуют.
Да. Как я могу создавать пустые массивы, если их не существует?
Ваш ответ выглядит вполне разумным, но он дает мне
"errmsg" : "$concatArrays only supports arrays, not object"
. Это потому, что вместо простого массива они представляют собой массив объектов?