MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Vários campos onde as chaves no documento variam Agregação média

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".