2012-08-31 73 views
11

我想要做一些我認爲很簡單的事情。假設我在mongo中有一系列具有共同關鍵字和可變數量屬性的記錄。我想在記錄中選擇所有屬性並按名稱分組。例如在mongo聚合中選擇* group

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

我想產生一個CSV,看起來像這樣:

Name  X Y Z 
George 5 3 9 
Rob  12 2 

試圖

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

不幸的是我回來所有的名字爲記錄,但不工會所有可能的屬性。

回答

11

如果要合併屬性,則需要將這些屬性添加到group。例如,使用$addToSet找到X,Y,Z每個名稱分組屬性的獨特的價值觀:

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

返回:

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

謝謝,我做了一個類似的事情使用$推,它似乎工作。我的後續問題將是從這裏是否最好的方式是將數據導出到平面CSV,展開結果集中的內部數組? –

+0

我使用pymongo和python來創建csv。剩下的一個問題是,當我使用$ addToSet時,我將爲每個鍵創建結果數組,即使每個鍵值對只有一個不同的值。然後這使得csv變扁的過程非常繁瑣。有沒有辦法避免創建關鍵值的數組? –

+1

@RogerSanchez:'$ addToSet'或'$ push'將返回數組值,所以你將不得不在CSV導出中做一些按摩或者考慮一個不同的聚合函數。例如,如果所有值都是數字,並且每個字段只有一個唯一值,那麼您可以使用['$ max'](http://docs.mongodb.org/manual/reference/aggregation/代替#_S_max)。如果結果值是*有時是*數組,則必須在代碼中進行爭論。下面是一個可能有所幫助的示例Python要點:[將數組展開爲CSV格式的引用字符串](https://gist.github.com/a39b087da394b746e4fe)。 – Stennie

0

這裏是做它的另一種方式:

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

你會得到的答案是這樣的(沒有確切的輸出):

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

雖然'group'是一個可行的選擇,只要你的收藏不分片,你不應該'在非PHP問題中使用PHP示例。 – JohnnyHK

+1

@JohnnyHK:我一直在尋找它,我把這個鏈接放在堆棧中,但它沒有給我正確的答案,所以當我找到答案時,我發佈它在這裏,有人可能會覺得它有用,如果你真的想讓我刪除我可以做到這一點。 –

+0

由你決定,但'aggregate'在這種情況下是更好的解決方案,如果可能的話,示例應該使用JavaScript,因爲這是'native'mongo語言。不用擔心,只是讓你知道。 – JohnnyHK

-1

使用$addToSet到組,它將工作

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

從Stennie解決問題需要你確切地知道你想從每個匹配項要查詢返回集合中的哪些屬性。情況並非總是如此。

我們必須在我們正在編寫的Groovy on Grails應用程序中解決這個問題。

我們寫這樣的方法來處理請求「通過X找到」:

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

然後如下我們的代碼中調用它:

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

這是通過傳遞結果在GroupCommand結尾對javascript函數進行初始查詢。 Mongo只返回您在初始查詢中指定的屬性,而不是其他任何內容,因此您必須第二次遍歷結果,並使用mongo中的其餘數據填充它們。