Sim, isso é um pouco mais difícil, considerando que existem vários arrays e, se você tentar os dois ao mesmo tempo, acaba com uma "condição cartesiana" em que um array multiplica o conteúdo do outro.
Portanto, basta combinar o conteúdo do array no início, o que provavelmente indica como você deve armazenar os dados em primeiro lugar:
Model.aggregate(
[
{ "$project": {
"company": 1,
"model": 1,
"data": {
"$setUnion": [
{ "$map": {
"input": "$pros",
"as": "pro",
"in": {
"type": { "$literal": "pro" },
"value": "$$pro"
}
}},
{ "$map": {
"input": "$cons",
"as": "con",
"in": {
"type": { "$literal": "con" },
"value": "$$con"
}
}}
]
}
}},
{ "$unwind": "$data" }
{ "$group": {
"_id": {
"company": "$company",
"model": "$model",
"tag": "$data.value"
},
"pros": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "pro" ] },
1,
0
]
}
},
"cons": {
"$sum": {
"$cond": [
{ "$eq": [ "$data.type", "con" ] },
1,
0
]
}
}
}
],
function(err,result) {
}
)
Então, através do primeiro
$project
preparar o $map
os operadores estão adicionando o valor "type" a cada item de cada array. Não que isso realmente importe aqui, pois todos os itens devem processar "único" de qualquer maneira, o $setUnion
operador "contate" cada array em um array singular. Como mencionado anteriormente, você provavelmente deveria estar armazenando dessa maneira em primeiro lugar.
Em seguida, processe
$unwind
seguido por $group
, em que cada "prós" e "contras" é então avaliado por meio de $cond
para o "tipo" correspondente, retornando 1
ou 0
onde a correspondência é respectivamente true/false
para o $sum
acumulador de agregação. Isso fornece uma "correspondência lógica" para contar cada "tipo" respectivo na operação de agregação de acordo com as chaves de agrupamento especificadas.