2012-08-31 16 views
11

Próbuję zrobić coś, co moim zdaniem jest dość proste. Załóżmy, że mam serię rekordów w Mongo, które mają wspólny klucz i zmienną liczbę atrybutów. Chcę wybrać wszystkie atrybuty i grupować według nazw w rekordach. Na przykładWybierz grupę * według agregacji mongo

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

chciałbym produkować CSV, który wygląda tak:

Name  X Y Z 
George 5 3 9 
Rob  12 2 

Tried

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

Niestety wrócę wszystkie nazwy jak rekordy, ale nie unię wszystkie możliwe atrybuty.

Odpowiedz

11

Jeśli chcesz połączyć atrybuty, musisz je dodać do group. Na przykład za pomocą $addToSet znaleźć unikatowe wartości dla x, y, atrybutów Z zgrupowane według nazwy każdego:

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

Powroty:

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

Dzięki, zrobiłem coś podobnego za pomocą $ push i wygląda na to, że działa. Moje pytanie uzupełniające brzmi: czy stąd najlepiej jest eksportować dane do płaskiego pliku CSV, odwijać wewnętrzne tablice w zestawie wyników? –

+0

Używam pymongo i Pythona do tworzenia csv. Jedynym pozostającym problemem jest to, że kiedy używam $ addToSet, tworzę tablice wyników dla każdego klucza, nawet jeśli istnieje tylko jedna odrębna wartość dla każdej pary wartości klucza. To sprawia, że ​​proces spłaszczania csv jest bardzo uciążliwy. Czy istnieje sposób na uniknięcie tworzenia tablic kluczowych wartości? –

+1

@RogerSanchez: '$ addToSet' lub' $ push' zwróci wartości tablicowe, więc będziesz musiał wykonać masowanie w eksporcie CSV lub rozważyć inną funkcję agregującą. Na przykład, jeśli wszystkie wartości są liczbami i masz tylko jedną unikalną wartość na pole, możesz być w stanie uciec za pomocą ['$ max'] (http://docs.mongodb.org/manual/reference/aggregation/ #_S_max). Jeśli wartości wyniku są * czasami * tablice, będziesz musiał w swoim kodzie zadzierać. Oto przykładowy wątek Pythona, który może pomóc: [spłaszczyć ag arrays do cytowanych ciągów w CSV] (https://gist.github.com/a39b087da394b746e4fe). – Stennie

0

Oto inny sposób to zrobić:

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

Dostaniesz odpowiedź coś takiego (nie dokładna wyjściowy):

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

Chociaż 'group' jest realną opcją, o ile twoja kolekcja nie jest uszkodzona, nie powinieneś ' • Używaj przykładów PHP w pytaniach innych niż PHP. – JohnnyHK

+1

@JohnnyHK: Szukałem go przez długi czas, mam ten link w stosie, ale nie dało mi to właściwej odpowiedzi, więc kiedy znalazłem odpowiedź, zamieściłem ją tutaj, niektórzy mogą uznać ją za przydatną, jeśli naprawdę chcę, żebym usunął, mogę to zrobić. –

+0

Aż do ciebie, ale "agregat" jest lepszym rozwiązaniem w tym przypadku, a przykłady powinny być w JavaScript, jeśli to możliwe, ponieważ jest to "natywny" język mongo. Bez obaw, po prostu cię informuję. – JohnnyHK

-1

użycie $addToSet do grupy, to będzie działać

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

Rozwiązanie z Stennie wymaga, aby dokładnie wiedzieć, które atrybuty chcesz powrócić z każdego elementu dopasowującego w kolekcji jesteś zapytaniem. Nie zawsze tak jest.

Musieliśmy rozwiązać ten problem w aplikacji Groovy on Grails, którą piszemy.

Pisaliśmy sposób tak, aby obsłużyć „znaleźć przez X” wniosków:

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

a następnie wywołać go w naszym kodzie następująco:

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

To działa przekazując wyniki pierwszego zapytania do funkcji javascript na końcu GroupCommand. Mongo zwraca tylko te atrybuty, które określono w początkowym zapytaniu i nic więcej, więc musisz powtórzyć wyniki po raz drugi, wypełniając je pozostałymi danymi z mongo.

Powiązane problemy