2012-10-08 15 views
13

¿Cuán flexible es la función de agregado para el formato de salida en MongoDB? Formato

datos:

{ 
     "_id" : ObjectId("506ddd1900a47d802702a904"), 
     "port_name" : "CL1-A", 
     "metric" : "772.0", 
     "port_number" : "0", 
     "datetime" : ISODate("2012-10-03T14:03:00Z"), 
     "array_serial" : "12345" 
} 

En este momento estoy usando esta función agregada para devolver una matriz de DateTime, un conjunto de métricas, y un recuento:

{$match : { 'array_serial' : array, 
          'port_name' : { $in : ports}, 
          'datetime' : { $gte : from, $lte : to} 
         } 
       }, 
       {$project : { port_name : 1, metric : 1, datetime: 1}}, 
       {$group : { _id : "$port_name", 
          datetime : { $push : "$datetime"}, 
          metric : { $push : "$metric"}, 
          count : { $sum : 1}}} 

cual es bueno, y muy rápido, pero ¿hay alguna manera de formatear el resultado para que haya una matriz por fecha/métrica? De esta manera:

[ 
    { 
     "_id" : "portname", 
     "data" : [ 
       ["2012-10-01T00:00:00.000Z", 1421.01], 
       ["2012-10-01T00:01:00.000Z", 1361.01], 
       ["2012-10-01T00:02:00.000Z", 1221.01] 
       ] 
    } 
] 

Esto simplificaría enormemente el front-end ya que es el formato del código gráfico de espera.

+0

Por el momento me estoy poniendo la salida y el bucle a través de los objetos y el uso de 'zip' función de guión para combinarlos, esto no parece añadir mucho por encima. –

Respuesta

16

Es posible combinar dos campos en una matriz de valores con el Framework de Agregación, pero definitivamente no es tan sencillo como podría ser (al menos como en MongoDB 2.2.0).

Aquí es un ejemplo:

db.metrics.aggregate(

    // Find matching documents first (can take advantage of index) 
    { $match : { 
     'array_serial' : array, 
     'port_name' : { $in : ports}, 
     'datetime' : { $gte : from, $lte : to} 
    }}, 

    // Project desired fields and add an extra $index for # of array elements 
    { $project: { 
     port_name: 1, 
     datetime: 1, 
     metric: 1, 
     index: { $const:[0,1] } 
    }}, 

    // Split into document stream based on $index 
    { $unwind: '$index' }, 

    // Re-group data using conditional to create array [$datetime, $metric] 
    { $group: { 
     _id: { id: '$_id', port_name: '$port_name' }, 
     data: { 
      $push: { $cond:[ {$eq:['$index', 0]}, '$datetime', '$metric'] } 
     }, 
    }}, 

    // Sort results 
    { $sort: { _id:1 } }, 

    // Final group by port_name with data array and count 
    { $group: { 
     _id: '$_id.port_name', 
     data: { $push: '$data' }, 
     count: { $sum: 1 } 
    }} 
) 
+0

¡Ah, no sabía que se podía llamar a $ group más de una vez. Lo probaré, gracias! –

+0

Gracias por ir al rescate, Stennie :) –

+0

¿Qué significa '$ const'? '¿hacer exactamente? No parece estar documentado. – maxdec

1

Construir arreglos en el marco de agregación sin $ push y $ addToSet es algo que parece no existir. Intenté hacer que esto funcione antes y fallé. Sería increíble si pudiera hacer:

data : {$push: [$datetime, $metric]} 

en el $group, pero eso no quiere trabajar.

Además, la construcción de objetos "literal" de este tipo no funciona:

data : {$push: {literal:[$datetime, $metric]}} 
or even data : {$push: {literal:$datetime}} 

espero que, finalmente, llegar a algunas formas mejores de masaje de este tipo de datos.

+0

Estos son los métodos exactos que probé, solo suponía que funcionaría. Supongo que no :( –

2

MongoDB 2,6 hizo esto mucho más fácil mediante la introducción de $map, lo que permite una forma más simple de la matriz de transposición:

db.metrics.aggregate([ 
    { "$match": { 
     "array_serial": array, 
     "port_name": { "$in": ports}, 
     "datetime": { "$gte": from, "$lte": to } 
    }}, 
    { "$group": { 
     "_id": "$port_name", 
     "data": { 
      "$push": { 
       "$map": { 
        "input": [0,1], 
        "as": "index", 
        "in": { 
         "$cond": [ 
          { "$eq": [ "$$index", 0 ] }, 
          "$datetime", 
          "$metric" 
         ] 
        } 
       } 
      } 
     }, 
     "count": { "$sum": 1 } 
    }} 
]) 

Donde al igual que el enfoque con $unwind, usted suministra una matriz como "entrada" a la operación del mapa que consta de dos valores y luego esencialmente reemplaza esos valores con los valores de campo que desea a través del $cond operación.

Esto realmente elimina todos los malabares necesarios para transformar el documento como se requería en lanzamientos anteriores y simplemente deja la agregación real al trabajo en cuestión, que básicamente se acumula por valor de "puerto_nombre", y la transformación a matriz es ya no es un área problemática

0

Puede utilizar $zip operador para producir la matriz de valores en 3,4

$zip con $arrayElemAt para crear una matriz de datetime y metrics.

Algo así como

db.collection.aggregate([ { 
    "$match": { 
     "array_serial": array, 
     "port_name": { 
     "$in": ports 
     }, 
     "datetime": { 
     "$gte": from, 
     "$lte": to 
     } 
    } 
    }, 
    { 
    "$group": { 
     "_id": "$port_name", 
     "data": { 
     "$push": { 
      "$arrayElemAt": [ 
      { 
       "$zip": { 
       "inputs": [ 
        [ 
        "$datetime" 
        ], 
        [ 
        "$metric" 
        ] 
       ] 
       } 
      }, 
      0 
      ] 
     } 
     }, 
     "count": { 
     "$sum": 1 
     } 
    } 
    } 
]) 
Cuestiones relacionadas