2012-05-21 45 views
2

我有一個蒙戈文檔的結構如下:如何用php統計mongo集合中的文檔元素?

{ 
"_id": ObjectId("4fba2558a0787e53320027eb"), 
"replies": { 
    "0": { 
     "email": ObjectId("4fb89a181b3129fe2d000000"), 
     "sentDate": "2012-05-21T11: 22: 01.418Z" 
    } 
    "1": { 
    "email": ObjectId("4fb89a181b3129fe2d000000"), 
    "sentDate": "2012-05-21T11: 22: 01.418Z" 
    } 
    "2" .... 
} 

} 

如何統計所有從集合中的所有文檔的答覆? 謝謝!

回答

5

在下面的答案,我用一個簡單的數據與整個集合五份答覆設置工作:

> db.foo.find() 
{ "_id" : ObjectId("4fba6b0c7c32e336fc6fd7d2"), "replies" : [ 1, 2, 3 ] } 
{ "_id" : ObjectId("4fba6b157c32e336fc6fd7d3"), "replies" : [ 1, 2 ] } 

,由於我們不是簡單地計算文件,db.collection.count()不會幫助我們。我們需要使用MapReduce來掃描每個文檔並聚合回覆數組的長度。考慮以下幾點:

db.foo.mapReduce(
    function() { emit('totalReplies', { count: this.replies.length }); }, 
    function(key, values) { 
     var result = { count: 0 }; 
     values.forEach(function(value) { 
      result.count += value.count; 
     }); 
     return result; 
    }, 
    { out: { inline: 1 }} 
); 

地圖功能(第一個參數)在整個收集運行和不變的密鑰下發射每個文檔中的回覆的數量。然後,Mongo會考慮所有發出的值並多次運行reduce函數(第二個參數)以合併(逐字減少)結果。希望這裏的代碼很簡單。如果您是映射/縮減新手,則需要注意的一點是reduce方法必須能夠處理自己的輸出。這在上面鏈接的MapReduce文檔中有詳細解釋。注意:如果你的收藏非常大,你可能不得不使用另一種輸出模式(例如收集輸出)。然而,inline適用於小數據集。

最後,如果你使用的MongoDB 2.1+,我們可以利用Aggregation Framework,以避免寫JS的功能和更輕鬆地進行:

db.foo.aggregate(
    { $project: { replies: 1 }}, 
    { $unwind: "$replies" }, 
    { $group: { 
     _id: "result", 
     totalReplies: { $sum: 1 } 
    }} 
); 

三件事都發生在這裏。首先,我們告訴Mongo我們對replies字段感興趣。其次,我們想展開數組,以便我們可以遍歷投影中各個字段的所有元素。最後,我們將在「結果」桶下(任何常量都可以)統計結果,將1添加到每個迭代的totalReplies結果中。執行該查詢將產生以下結果:

{ 
    "result" : [{ 
     "_id" : "result", 
     "totalReplies" : 5 
    }], 
    "ok" : 1 
} 

雖然我寫了關於蒙戈客戶端上面的答案,你應該沒有問題,將它們轉換到PHP。您需要使用MongoDB::command()來運行MapReduce或聚合查詢,因爲PHP驅動程序目前沒有輔助方法。目前PHP文檔中有一個MapReduce示例,您可以參考this Google group post以通過相同的方法執行聚合查詢。

+0

您好,只是想知道怎麼會這樣應用於收集 – troy

+0

您可以使用類似於我的例子聚合管道中的所有答覆從一個單一的文件算起,開始與['$ match'](http://docs.mongodb.org/manual/reference/aggregation/match/)一起縮小爲單個文檔;不過,我認爲最簡單的方法是簡單地選擇文檔並在應用程序中計算數組長度。如果您關心數據大小,可以從嵌入式文檔數組(例如'{respond.author}')投影單個字段,或者甚至丟失一個空字段以返回一個空對象數組(仍可計數) 。 – jmikola

0

我還沒有檢查過你的代碼,可能也適用。我做了以下,它只是工作:

$replies = $db->command(
    array(
     "distinct" => "foo", 
     "key" => "replies" 
     ) 
    ); 
$all = count($replies['values']); 
+0

只要每個回覆元素都是唯一值,'distinct()'應該產生預期的答案。我想這可能效率較低(特別是對於較大的值和數據集),因爲區別比總結數組長度更復雜。 – jmikola

+0

我會嘗試你的解決方案,你是正確的性能...你有任何想法如何檢查從MongoDB的性能從PHP? –

+1

查看我在[這個答案](http://stackoverflow.com/a/10655998/162228)中鏈接的要點,瞭解一個簡單的時序示例。對於實際的MongoDB查詢分析,您可能需要查看:http://www.mongodb.org/display/DOCS/Database+Profiler – jmikola

0

我再次使用PHP Mongo驅動程序的組命令。它與MapReduce命令類似。

$keys = array("replies.type" => 1); //keys for group by 
$initial = array("count" => 0); //initial value of the counter 
$reduce = "function (obj, prev) { prev.count += obj.replies.length; }"; 
$condition = array('replies' => array('$exists' => true), 'replies.type' => 'follow'); 
$g = $db->foo->group($keys, $initial, $reduce, $condition); 
echo $g['count']; 

感謝jmikola給予Mongo的鏈接。

0

JSON應該是

{ 
    "_id": ObjectId("4fba2558a0787e53320027eb"), 
    "replies":[ 
      { 
      0: { 
        "email": ObjectId("4fb89a181b3129fe2d000000"), 
        "sentDate": "2012-05-21T11: 22: 01.418Z" 
       }, 
      1: { 
        "email": ObjectId("4fb89a181b3129fe2d000000"), 
        "sentDate": "2012-05-21T11: 22: 01.418Z" 
       }, 
      2: {....} 
      ] 

} 
相關問題