Aperçu du concept
Ce que je disais essentiellement dans le très bref commentaire, c'est que à la place pour émettre une requête d'agrégation distincte pour chaque nom de "clé" de capteur, vous pouvez la mettre dans UNE , tant que vous calculez correctement les "moyennes".
Bien sûr, le problème dans vos données est que les "clés" ne sont pas présentes dans tous les documents. Donc, pour obtenir la "moyenne" correcte, nous ne pouvons pas simplement utiliser $avg
puisqu'il compterait "TOUS" les documents, que la clé soit présente ou non.
Donc, à la place, nous décomposons les "maths" et faisons un $group
pour le Count
total et la Sum
totale de chaque touche en premier. Cela utilise $ifNull
pour tester la présence du champ, et aussi $cond
pour alterner les valeurs à renvoyer.
.aggregate([
{ "$match": {
"$or": [
{ "Technique-Electrique_VMC Aldes_Power4[W]": { "$exists": True } },
{ "Technique-Electrique_VMC Unelvent_Power5[W]": { "$exists": True } }
]
}}
{ "$group":{
"_id":{
"year":{ "$year":"$timestamp" },
"month":{ "$month":"$timestamp" }
},
"Technique-Electrique_VMC Aldes_Power4[W]-Sum": {
"$sum": {
"$ifNull": [ "$Technique-Electrique_VMC Aldes_Power4[W]", 0 ]
}
},
"Technique-Electrique_VMC Aldes_Power4[W]-Count": {
"$sum": {
"$cond": [
{ "$ifNull": [ "$Technique-Electrique_VMC Aldes_Power4[W]", false ] },
1,
0
]
}
},
"Technique-Electrique_VMC Unelvent_Power5[W]-Sum": {
"$sum": {
"$ifNull": [ "$Technique-Electrique_VMC Unelvent_Power5[W]", 0 ]
}
},
"Technique-Electrique_VMC Unelvent_Power5[W]-Count": {
"$sum": {
"$cond": [
{ "$ifNull": [ "$Technique-Electrique_VMC Unelvent_Power5[W]", false ] },
1,
0
]
}
}
}},
{ "$project": {
"Technique-Electrique_VMC Aldes_Power4[W]-Avg": {
"$divide": [
"$Technique-Electrique_VMC Aldes_Power4[W]-Sum",
"$Technique-Electrique_VMC Aldes_Power4[W]-Count"
]
},
"Technique-Electrique_VMC Unelvent_Power5[W]-Avg": {
"$divide": [
"Technique-Electrique_VMC Unelvent_Power5[W]-Sum",
"Technique-Electrique_VMC Unelvent_Power5[W]-Count"
]
}
}}
])
Le $cond
L'opérateur est un opérateur "ternaire", ce qui signifie que la première condition "if" est true
, "then" le deuxième argument est renvoyé, "else" le troisième argument est renvoyé.
Donc le point du ternaire dans le "Count"
est de travailler :
- Si le champ est là, renvoyez 1 pour le compte
- Sinon, renvoyez 0 lorsqu'il n'est pas là
Après le $group
est fait, afin d'obtenir la Average
nous utilisons $divide
sur les deux numéros produits pour chaque clé dans un $project
scène.
Le résultat final est la "moyenne" pour chaque clé que vous fournissez, et cela ne prend en compte que l'ajout de valeurs et de comptes pour les documents où le champ était réellement présent.
Ainsi, mettre toutes les clés dans une seule instruction d'agrégation vous fera économiser beaucoup de temps et de ressources lors du traitement.
Génération dynamique de pipeline
Donc pour faire ça "dynamiquement" en python, commencez par la liste :
sensors = ["Technique-Electrique_VMC Aldes_Power4[W]", "Technique-Electrique_VMC Unelvent_Power5[W]"]
match = { '$match': { '$or': map(lambda x: { x: { '$exists': True } },sensors) } }
group = { '$group': {
'_id': {
'year': { '$year': '$timestamp' },
'month': { '$month':'$timestamp' }
}
}}
project = { '$project': { } }
for k in sensors:
group['$group'][k + '-Sum'] = {
'$sum': { '$ifNull': [ '$' + k, 0 ] }
}
group['$group'][k + '-Count'] = {
'$sum': { '$cond': [ { '$ifNull': [ '$' + k, False ] }, 1, 0 ] }
}
project['$project'][k + '-Avg'] = {
'$divide': [ '$' + k + '-Sum', '$' + k + '-Count' ]
}
pipeline = [match,group,project]
Ce qui génère la même chose que la liste complète ci-dessus pour une liste donnée de "capteurs".