2012-08-31 30 views
11

Estoy tratando de hacer algo que creo que es bastante simple. Supongamos que tengo una serie de registros en mongo que tienen una clave común y un número variable de atributos. Quiero seleccionar todos los atributos y agrupar por nombre en todos los registros. Por ejemploSeleccione * group by in mongo aggregation

{ Name: George, x: 5, y: 3 } 
{ Name: George, z: 9 } 
{ Name: Rob, x: 12, y: 2 } 

me gustaría producir un CSV que tiene este aspecto:

Name  X Y Z 
George 5 3 9 
Rob  12 2 

Probamos

DB.data.aggregate({ $group : { _id : "$Name" } }) 

Por desgracia, recuperar todos los nombres como los registros, pero no la unión de todos los atributos posibles

Respuesta

11

Si desea combinar los atributos, deberá agregarlos al group. Por ejemplo, usando $addToSet para encontrar los valores únicos de las x, y, atributos z agrupados por cada nombre:

db.data.aggregate(
    { $group : { 
      _id : "$Name", 
      x: { $addToSet: "$x" }, 
      y: { $addToSet: "$y" }, 
      z: { $addToSet: "$z" }, 
    }} 
) 

Devuelve:

{ 
    "result" : [ 
     { 
      "_id" : "Rob", 
      "x" : [ 
       12 
      ], 
      "y" : [ 
       2 
      ], 
      "z" : [ ] 
     }, 
     { 
      "_id" : "George", 
      "x" : [ 
       5 
      ], 
      "y" : [ 
       3 
      ], 
      "z" : [ 
       9 
      ] 
     } 
    ], 
    "ok" : 1 
} 
+0

Gracias, hice algo similar usando $ push y parece que funciona. Mi siguiente pregunta sería si a partir de aquí, ¿cuál es la mejor manera de exportar los datos a CSV plano, desenrollando los arreglos internos en el conjunto de resultados? –

+0

Estoy usando pymongo y python para crear el csv. El único problema que queda es que cuando uso $ addToSet estoy creando matrices de resultados para cada clave, incluso si solo hay un valor distinto para cada par de valores clave. Esto hace que el proceso de aplanamiento a csv sea muy engorroso. ¿Hay alguna manera de evitar crear las matrices de valores clave? –

+1

@RogerSanchez: '$ addToSet' o' $ push' devolverá valores de matriz, por lo que tendrá que hacer algunos ajustes en su exportación CSV o considerar una función agregada diferente. Por ejemplo, si todos los valores son numéricos y usted solo tiene un valor único por campo, puede salirse con la suya usando ['$ max'] (http://docs.mongodb.org/manual/reference/aggregation/ #_S_max) en su lugar. Si los valores resultantes son * a veces * matrices, tendrá que disputar su código. Aquí hay un ejemplo de Python que puede ayudar: [aplanamiento de matrices agg a cadenas entre comillas en CSV] (https://gist.github.com/a39b087da394b746e4fe). – Stennie

0

Esta es la otra forma de hacerlo:

$connection = 'mongodb://localhost:27017'; 
$con  = new Mongo($connection); // mongo connection 

$db   = $con->test; /// database 
$collection = $db->prb; // table 

$keys  = array("Name" => 1,"x"=>1,"y"=>1,"z"=>1); 

// set intial values 
$initial = array("count" => 0); 

// JavaScript function to perform 
$reduce  = "function (obj, prev) { prev.count++; }"; 

$g   = $collection->group($keys, $initial, $reduce); 

echo "<pre>"; 
print_r($g); 

Usted obtendrá la respuesta algo como esto (no la salida exacta):

Array 
(
    [retval] => Array 
     (
      [0] => Array 
       (
        [Name] => George 
        [x] => 
        [y] => 
        [z] => 
        [count] => 2 
       ) 

      [1] => Array 
       (
        [Name] => Rob 
        [x] => 
        [y] => 
        [z] => 
        [count] => 1 
       ) 

     ) 

    [count] => 5 
    [keys] => 3 
    [ok] => 1 
) 
+1

Si bien 'group' es una opción viable siempre que su colección no esté fragmentada, no debería t utilizar ejemplos PHP en preguntas que no sean PHP. – JohnnyHK

+1

@JohnnyHK: Estuve buscándolo durante mucho tiempo, tengo este enlace en la pila, pero no me dio la respuesta adecuada, así que cuando encontré la respuesta lo publiqué aquí, a algunos les puede resultar útil, si Realmente quiero que borre puedo hacerlo. –

+0

Depende de usted, pero 'aggregate' es una mejor solución en este caso y los ejemplos deben estar en JavaScript si es posible, ya que ese es el idioma 'nativo' de mongo. No te preocupes, solo dejándote saber. – JohnnyHK

-1

uso $addToSet al grupo, que trabajará

db.data.aggregate(
    { $group : { 
      _id : "$Name", 
      x: { $addToSet: "$x" }, 
      y: { $addToSet: "$y" }, 
      z: { $addToSet: "$z" }, 
    }} 
) 
0

La solución de Stennie se requiere saber exactamente qué atributos que desea devolver a cada elemento coincidente en la colección que está consultando. Este no es siempre el caso.

Tuvimos que resolver este problema dentro de una aplicación de Groovy on Grails que estamos escribiendo.

escribimos un método como este para manejar la "encontrar por X" solicitudes:

private List<DBObject> findDistinctPages(Map by) { 
    def command = 
     new GroupCommand(
       (DBCollection) db.pages, 
       new BasicDBObject(['url': 1]), 
       new BasicDBObject(by), 
       new BasicDBObject([:]), 
       'function (current, result) { for(i in current) { result[i] = current[i] } }', 
       '' 
     ) 
    db.pages.group(command).sort { it.title } 
} 

Y luego llamarlo dentro de nuestro código como sigue:

def pages = findDistinctPages([$or: [[type: 'channel'], [type: 'main']]]) 

Esto funciona haciendo pasar los resultados de la consulta inicial a la función javascript al final de GroupCommand. Mongo solo devuelve los atributos que especifiques en la consulta inicial y nada más, por lo que debes iterar sobre los resultados una segunda vez, rellenándolos con el resto de los datos de mongo.