Esboço do conceito
O que eu estava basicamente dizendo no breve comentário é que ao invés para emitir uma consulta de agregação separada para cada nome de "chave" do sensor, você pode colocá-la em ONE , contanto que você calcule as "médias" corretamente.
Claro que o problema nos seus dados é que as "chaves" não estão presentes em todos os documentos. Portanto, para obter a "média" correta, não podemos simplesmente usar
$avg
uma vez que contaria "TODOS" os documentos, estando a chave presente ou não. Então, em vez disso, dividimos a "matemática" e fazemos um
$group
para a Count
Total e total Sum
de cada chave primeiro. Isso usa $ifNull
para testar a presença do campo e também $cond
para alternar os valores a serem retornados. .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"
]
}
}}
])
O
$cond
operador é um operador "ternário" que significa onde a primeira condição "if" é true
, "then" o segundo argumento é retornado, "else" o terceiro argumento é retornado. Portanto, o ponto do ternário no
"Count"
é trabalhar:- Se o campo estiver lá, retorne 1 para contagem
- Caso contrário, retorne 0 quando não estiver lá
Após o
$group
é feito, para obter a Average
usamos $divide
nos dois números produzidos para cada chave dentro de um $project
palco. O resultado final é a "média" para cada chave que você fornece, e isso considera apenas a adição de valores e contagens para documentos onde o campo estava realmente presente.
Portanto, colocar todas as chaves em uma única instrução de agregação economizará muito tempo e recursos no processamento.
Geração dinâmica de pipeline
Então, para fazer isso "dinamicamente" em python, comece com a lista:
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]
O que gera o mesmo que a listagem completa acima para uma determinada lista de "sensores".