2012-08-31 17 views
11

Sto cercando di fare qualcosa che ritengo sia abbastanza semplice. Supponiamo che io abbia una serie di record in mongo che hanno una chiave comune e un numero variabile di attributi. Voglio selezionare tutti gli attributi e raggruppare per nome tra i record. Per esempioSeleziona * group by in mongo aggregation

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

vorrei produrre un CSV che assomiglia a questo:

Name  X Y Z 
George 5 3 9 
Rob  12 2 

provato

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

Purtroppo torno tutti i nomi come record, ma non l'unione di tutti gli attributi possibili.

risposta

11

Se si desidera combinare gli attributi, è necessario aggiungerli allo group. Ad esempio, utilizzando $addToSet per trovare i valori unici delle x, y, attributi z raggruppati per ogni nome:

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

Returns:

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

Grazie, ho fatto una cosa simile usando $ push e sembra funzionare. La mia domanda successiva sarebbe se da qui quale sia il modo migliore per esportare i dati su CSV piatto, srotolando gli array interni nel set di risultati? –

+0

Sto usando pymongo e python per creare il csv. Il problema rimanente è che quando uso $ addToSet sto creando matrici di risultati per ogni chiave, anche se esiste un solo valore distinto per ogni coppia di valori chiave. Questo rende quindi il processo di appiattimento a csv molto ingombrante. C'è un modo per evitare di creare gli array di valori chiave? –

+1

@RogerSanchez: '$ addToSet' o' $ push' restituirà valori di array, quindi dovrai eseguire un po 'di massaggio nell'esportazione CSV o prendere in considerazione una funzione di aggregazione diversa. Ad esempio, se tutti i valori sono numerici e hai solo un valore univoco per campo potresti riuscire a farla franca utilizzando ['$ max'] (http://docs.mongodb.org/manual/reference/aggregation/ #_S_max). Se i valori dei risultati sono * a volte * array, dovrai digitare il tuo codice. Ecco un esempio di esempio di Python che può essere d'aiuto: [appiattire gli array agg per le stringhe tra virgolette in CSV] (https://gist.github.com/a39b087da394b746e4fe). – Stennie

0

Ecco l'altro modo di farlo:

$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); 

Si otterrà la risposta qualcosa di simile (non l'uscita esatta):

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

Mentre 'group' è un'opzione praticabile se la tua raccolta non è sharded, non dovresti t utilizzare esempi PHP in domande non PHP. – JohnnyHK

+1

@JohnnyHK: Lo stavo cercando da molto tempo, ho ottenuto questo link nello stack, ma non mi ha dato una risposta adeguata, quindi quando ho trovato la risposta l'ho postato qui, qualcuno potrebbe trovarlo utile, se tu voglio davvero che cancelli posso farlo. –

+0

Fino a voi, ma "aggregare" è una soluzione migliore in questo caso e gli esempi dovrebbero essere in JavaScript se possibile in quanto è la lingua mongo "nativa". Nessun problema, ti sto solo informando. – JohnnyHK

-1

uso $addToSet al gruppo, funzionerà

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

La soluzione da Stennie richiede di sapere esattamente quali attributi che si desidera tornare da ogni voce corrispondente nella collezione si sta interrogando. Questo non è sempre il caso.

Abbiamo dovuto risolvere questo problema all'interno di un'applicazione Groovy on Grails che stiamo scrivendo.

abbiamo scritto un metodo come questo per gestire la "trovare da X" richieste:

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 } 
} 

E poi chiamare all'interno del nostro codice come segue:

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

Questo funziona facendo passare i risultati della query iniziale alla funzione javascript alla fine di GroupCommand. Mongo restituisce solo gli attributi che hai specificato nella query iniziale e nient'altro, quindi devi eseguire un'iterazione sui risultati una seconda volta, popolandoli con il resto dei dati di mongo.