Zarys koncepcji
Zasadniczo powiedziałem w bardzo krótkim komentarzu, że zamiast do wydawania oddzielnego zapytania agregującego dla każdej nazwy „klucza” czujnika, możesz umieścić je w JEDEN , o ile poprawnie obliczysz „średnie”.
Oczywiście problem w Twoich danych polega na tym, że „klucze” nie występują we wszystkich dokumentach. Aby uzyskać prawidłową „średnią”, nie możemy po prostu użyć $avg
ponieważ policzyłoby "WSZYSTKIE" dokumenty, niezależnie od tego, czy klucz był obecny, czy nie.
Więc zamiast tego dzielimy „matematykę” i wykonujemy $group
dla całkowitej Count
i całkowita Sum
najpierw każdego klucza. Używa $ifNull
aby przetestować obecność pola, a także $cond
do wartości alternatywnych do zwrócenia.
.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"
]
}
}}
])
$cond
operator jest operatorem „trójargumentowym”, co oznacza, że pierwszy warunek „if” ma wartość true
, "wtedy" zwracany jest drugi argument, "inaczej" zwracany jest trzeci argument.
Więc punkt trójki w "Count"
jest ćwiczenie:
- Jeśli pole tam jest, zwróć 1 dla licznika
- W przeciwnym razie zwróć 0, gdy go tam nie ma
Po $group
jest zrobione, aby uzyskać Average
używamy $divide
na dwóch liczbach wygenerowanych dla każdego klucza w osobnym $project
scena.
Wynikiem końcowym jest „średnia” dla każdego podanego klucza, a to uwzględniało tylko dodawanie wartości i liczby dla dokumentów, w których pole faktycznie było obecne.
Dzięki temu umieszczenie wszystkich kluczy w jednej instrukcji agregacji zaoszczędzi wiele czasu i zasobów związanych z przetwarzaniem.
Dynamiczne generowanie potoku
Aby zrobić to „dynamicznie” w Pythonie, zacznij od listy:
Czujnikisensors = ["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]
Co generuje to samo, co pełna lista powyżej dla danej listy „czujników”.