AGREGADO POR FECHAS CON MONGODB

estudio de datos de luminosidad agregados por hora

El escenario de agregados por fecha es muy habitual. Lo mostraremos con un ejemplo práctico.

EL PROBLEMA
Tenemos una colección de datos ‘SofiaSolarStation0’ que se popula mediante sensores que realizan lecturas cada 10 segundos.

Nuestro objetivo será crear una gráfica que represente la luminosidad y mostrar su tendencia a lo largo de los días.

Nos centraremos en los siguientes datos:

  • La luminosidad tomada de un sensor como ‘PlantillaBase.luminosidad.lux’
  • La fecha de la lectura es el campo ‘PlantillaBase.timestamp’

Con una toma de datos tan frecuente, la cantidad de documentos que se van a acumular en mongo a lo largo del tiempo puede ser relativamente voluminosa, mas de 250000 registros al mes.

CREAR UNA REPRESENTACION
Cuando realizamos una query online, nos vemos frecuentemente limitados en número de respuestas por el sistema.

Nuestro sistema es sofia2.com, que en condiciones normales limita a un máximo de 100 elementos por cada consulta. Realizamos la siguiente query:

db.SofiaSolarStation0.find(
Query tipo ‘find’
   { 'PlantillaBase.timestamp': 
      { '$lte':ISODate('2014-09-26T11:30:00.000Z') } },
Criterio de busqueda (fecha menor o igual que el 26-Septiembre)
   { 'PlantillaBase.timestamp':1, 'PlantillaBase.luminosidad.lux':1}
Proyeccion: Devolver campos ‘timestamp’ y ‘lux’
).sort(
Modificador ‘ordenar’
   { 'PlantillaBase.timestamp':-1 }
Criterio de ordenacion: por ‘timestamp’ de mayor a menor
)

Y la respuesta tiene la siguiente representación :

(fuentes disponibles en http://sofia2.com/examples2/sol1.html)

El problema es que 15 minutos de tomas de luminosidad no ofrecen datos suficientemente relevantes si lo que queremos es deducir una tendencia de luminosidad entre varios días.

Para obtener algo más representativo deberíamos mostrar un día completo de datos. Solo un día suponen 6 * 60 *24 = 8640 datos.

  • Podría pasarse por nuestra cabeza acumular varias peticiones paginadas y componer los resultados. Esto resultaría muy poco eficiente: de 100 en 100 un solo día serian 86 querys.
  • Saltar la restricción de 100 elementos devueltos tampoco saldría barato. 8000 datos podrían ser viables, pero imaginemos que queremos mostrar un mes o un año de datos. La respuesta sería tan voluminosa que la penalización de la transmisión por internet la haría muy lenta.
  • Cualquier componente gráfico de javascript sufre severas pérdidas de rendimiento cuando manejamos cantidades de varias decenas de miles de datos.

LA SOLUCION

Si nos vamos a la documentación de mongodb, nos encontramos los agregados de datos.

Los agregados nos permiten hacer un tratamiento en varias fases de una colección para generar un resultado que agrupa y ordena los documentos, un poco al estilo “group by” y “order by” de una base de datos SQL.

Queremos mostrar la media de luminosidad agrupada por horas.

Después de leer, probar y experimentar, encontramos que esta forma de realizar los aggregates que nos resulta adecuada:

db.SofiaSolarStation0.aggregate([
Query tipo ‘aggregate’
{ $match: {
Fase de Filtrado
   'PlantillaBase.timestamp':{
      '$lte':{'$date':'2014-09-26T11:30:00.000Z'}
   }
} },
condicion de filtrado fecha menor o igual que el 26-SeptiembreNota: si en vez del canal javascript usamos la consola, se debe utilizar el ISODate de esta manera:
‘$lte’:ISODate(‘2014-09-26T11:30:00.000Z’)
{ $project:{
Fase proyección
   hora: {
      0: {'$year': '$PlantillaBase.timestamp'},
      1: {'$month': '$PlantillaBase.timestamp'},
      2: {'$dayOfMonth': '$PlantillaBase.timestamp'},
      3: {'$hour': '$PlantillaBase.timestamp'}
   },
Valor proyectado ‘hora’ compuesto de

  • campo 0:año
  • campo 1:mes
  • campo 2:diames
  • campo 3:hora
   base:'$PlantillaBase'
Valor proyectado: lo llamamos ‘base’
}},
{ $group:{
Fase agrupación
   _id:'$hora',
      'assetId':{$first:'$base.assetId'},
      'avg_lux':{$avg:'$base.luminosidad.lux'},
      'sum_ir':{$sum:'$base.luminosidad.ir'}
Criterio de ordenación (_id): proyección ‘hora’

  • Primer valor assetId del grupo
  • Media de valores ‘luminosidad.lux’ del grupo
  • Suma de valores ‘luminosidad.ir’ del grupo
} },
   { $sort:{
      '_id':-1
   } }
Fase sort: Ordenar por el _id
])

La respuesta horaria es mucho más adecuada para ver las tendencias diarias:

(fuentes disponibles en http://sofia2.com/examples2/sol2.html)

AGREGADO POR FECHAS CON MONGODB

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s