Schema concettuale
Quello che stavo sostanzialmente dicendo nel brevissimo commento è che invece per eseguire una query di aggregazione separata per ogni nome di "chiave" del sensore, puoi inserirlo in UNO , a patto di calcolare correttamente le "medie".
Ovviamente il problema nei tuoi dati è che le "chiavi" non sono presenti in tutti i documenti. Quindi, per ottenere la "media" corretta, non possiamo semplicemente usare $avg
poiché conterebbe "TUTTI" i documenti, indipendentemente dal fatto che la chiave fosse presente o meno.
Quindi, invece, interrompiamo la "matematica" e facciamo un $group
per il Count
totale e totale Sum
prima di ogni chiave. Questo utilizza $ifNull
per verificare la presenza del campo, e anche $cond
per alternare i valori da restituire.
.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"
]
}
}}
])
Il $cond
operatore è un operatore "ternario" che significa dove la prima condizione "se" è true
, "quindi" viene restituito il secondo argomento, "else" viene restituito il terzo argomento.
Quindi il punto del ternario nel "Count"
è da risolvere:
- Se il campo è presente, restituisci 1 per il conteggio
- Altrimenti restituisci 0 quando non è presente
Dopo il $group
è fatto, per ottenere la Average
usiamo $divide
sui due numeri prodotti per ciascuna chiave all'interno di un separato $project
fase.
Il risultato finale è la "media" per ogni chiave che fornisci, e questo ha considerato solo l'aggiunta di valori e conteggi per i documenti in cui il campo era effettivamente presente.
Quindi inserire tutte le chiavi in un'unica istruzione di aggregazione ti farà risparmiare molto tempo e risorse nell'elaborazione.
Generazione dinamica di pipeline
Quindi, per farlo "dinamicamente" in Python, inizia con l'elenco:
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]
Che genera lo stesso elenco completo sopra per un determinato elenco di "sensori".