Vous voulez essentiellement $cond
pour choisir de passer 1
ou 0
à l'accumulateur $sum
dans la canalisation $group
, et une valeur initiale comme un « tableau » pour les deux champs à l'aide $unwind
pour créer une copie du document pour chaque personne.
db.data.aggregate([
{ "$addFields": {
"val": ["$installer","$tester"]
}},
{ "$unwind": "$val" },
{ "$group": {
"_id": { "_id": "$_id", "val": "$val" },
"installer": {
"$max": {
"$cond": [
{ "$eq": ["$installer","$val"] },
1,
0
]
}
},
"tester": {
"$max": {
"$cond": [
{ "$eq": ["$tester","$val"] },
1,
0
]
}
}
}},
{ "$group": {
"_id": "$_id.val",
"installer": { "$sum": "$installer" },
"tester": { "$sum": "$tester" }
}}
])
Pour contrer le cas où un document donné pourrait avoir deux le même « installateur » et les valeurs « testeur » nous devrions effectivement agréger sur le « document » par l'émis « val » comme une première étape. L'utilisation du $cond
à l'intérieur d'un accumulateur $max
fait de ce cas un document "unique" au lieu de "deux", soit un pour chaque entrée de tableau.
L'autre cas est bien sûr de revenir simplement « l'ensemble » des valeurs en appliquant $setUnion
contre la liste initiale pour éviter la duplication dans un tel cas:
db.data.aggregate([
{ "$addFields": {
"val": { "$setUnion": [["$installer","$tester"]] }
}},
{ "$unwind": "$val" },
{ "$group": {
"_id": "$val",
"installer": {
"$sum": {
"$cond": [
{ "$eq": ["$installer","$val"] },
1,
0
]
}
},
"tester": {
"$sum": {
"$cond": [
{ "$eq": ["$tester","$val"] },
1,
0
]
}
}
}}
])
j'ai ajouté un document à votre source comme :
{ "installer": "jack", "tester": "jack" }
Afin d'illustrer le résultat.
Quant à $cond
, il est un « ternaire » ou if..then..else
condition, où les arguments sont « première » if
pour une condition à évaluer en tant que valeur booléenne, then
étant la valeur de retour quand une valeur true
et else
pour revenir lorsque la condition est false
.
Il peut être tour à tour écrit comme:
"$cond": {
"if": { "$eq": ["$installer","$val"] },
"then": 1,
"else": 0
}
Mais la syntaxe « tableau » d'origine est un peu plus court pour écrire des expressions simples. La plupart des gens reconnaîtraient toujours le "ternaire" pour ce qu'il est, mais si vous pensez que cela rend le code plus clair, vous pouvez utiliser le formulaire "clés nommées" à la place.
Le résultat est bien sûr le 1
est seulement retourné lorsque le champ est présent dans le document, ce qui donne les comptes corrects:
/* 1 */
{
"_id" : "jack",
"installer" : 1.0,
"tester" : 1.0
}
/* 2 */
{
"_id" : "dave",
"installer" : 0.0,
"tester" : 2.0
}
/* 3 */
{
"_id" : "bob",
"installer" : 1.0,
"tester" : 1.0
}
/* 4 */
{
"_id" : "chris",
"installer" : 2.0,
"tester" : 1.0
}
/* 5 */
{
"_id" : "anthony",
"installer" : 2.0,
"tester" : 1.0
}
Ajout du « tableau » initial du document peut alternativement être fait en utilisant $project
si votre version MongoDB ne prend pas en charge $addFields
. La seule différence est « explicitement », y compris les autres domaines qui sont nécessaires plus tard:
{ "$project": {
"tester": 1,
"installer": 1,
"val": { "$setUnion": [["$installer","$tester"]] }
}}
Et si votre MongoDB est toujours en fait plus que MongoDB 3.2 qui permet que la notation d'un « tableau », vous pouvez alors utiliser à la place $map
de MongoDB 2,6 et vers le haut:
{ "$project": {
"tester": 1,
"installer": 1,
"val": {
"$setUnion": [
{ "$map": {
"input": ["A","B"],
"as": "a",
"in": {
"$cond": [{ "$eq": ["$$a", "A"] }, "$installer", "$tester"]
}
}
]
}
}}
Encore une fois en utilisant $cond
pour sélectionner alternativement la valeur à présent comme éléments du tableau.
En outre, vous devriez vraiment éviter de faire des choses comme l'ajout d'un $project
à la fin des instructions. Vous pouvez bien sûr le faire, mais cela signifie que tous les résultats de l'étape précédente du pipeline sont «réexécutés» afin d'effectuer les changements supplémentaires. Pour quelque chose d'aussi trivial que de changer "_id"
à "name"
, il est généralement préférable d'accepter simplement que la "clé de regroupement" est appelée _id
et de laisser cela.
À la suite de $group
, il fait est le « identifiant unique » pour lequel _id
est la nomenclature commune.
Cela devrait vous aider: https://stackoverflow.com/questions/18501064/mongodb-aggregation-counting-distinct-fields –