周煦辰 2016-01-16
來說說MongoDB中的數據聚合工具。
Aggregate是MongoDB提供的眾多工具中的比較重要的一個,類似於SQL語句中的GROUP BY。聚合工具可以讓開發人員直接使用MongoDB原生的命令操作數據庫中的數據,並且按照要求進行聚合。聚合不僅極大提升了開發的效率,更重要的是,原生的工具運行效率比自己寫聚合的方法高到不知道哪里去了。
下面簡單說一下PHP開發環境下如何使用MongoDB的數據聚合工具 Aggregation Pipleline和Group。其實PHP下的MongoDB使用和原生差不多,無非是語法從JavaScript變成了PHP而已,大致的操作流程是差不多,命令的格式也是非常相像的。基本上只要會看MongoDB的文檔,就能通過PHP操作MongoDB了。
Aggregation Pipleline
關於Aggregation,官方文檔在這里。我這里就半翻譯半扯淡(當然大部分是扯淡)說一下Aggregation Pipleline。
管道的概念
管道在*nix中將上一個命令輸出的數據作為下一個命令的參數。MongoDB中的管道操作是可以重復的,基於這個概念,MongoDB中的管道聚合可以有非常實用的玩法,比如對聚合的結果進行排序。
那么,如何使用呢

借用一下官方的圖,這里aggregate的命令為:
[
{$match: {status: "A"}},
{$group: {_id: "$cust_id", total: {$sum: "$amount"}}}
]
aggreagte是一個數組,其中包含多個對象(命令),通過遍歷Pipleline數組對collection中的數據進行操作。
解釋一下例子中的配置項意思。
$match:查詢條件
- 經常使用正則表示的人肯定對match不陌生,很明顯
$match是配置查詢數據時的條件。這里的語法和查詢數據庫的時候一毛一樣,也就不再贅述了,看一下MongoDB的CRUD文檔即可。
$group:聚合的配置
_id代表你想聚合的數據的主鍵,例如上述數據中,你想聚合所有cust_id相同的條目的amount的總和,那_id即被設置為cust_id。_id為必須,但是你可以填寫一個空值。total代表你最后想輸出的數據之一,這里total是每條結果中amount的總和。$sum是一個聚合的操作符,另外的操作符你可以在官方文檔中找到。上圖中的命令表示對相同主鍵(_id)下的amount進行求和。如果你想要計算主鍵出現的次數,可以把命令寫成如下的形式
{$sum: 1}
聚合的過程
看一下圖例,所有的數據先經過$match命令,只留下了status為A的數據,接着,對篩選出的數據進行聚合操作,對相同cust_id的數據進行計算amount總和的操作,最后輸出結果。
其他管道表達式與操作符
這里說一下$sort操作符。前面說過,利用管道的特性,可以做到對結果進行排序。所以只需要在$group操作之后使用$sort操作即可,這點在報表的制作上十分實用。況且使用MongoDB對結果進行排序也可以盡量優化性能。
Group
MongoDB另一個重要的聚合工具就是Group,所不同的是,Aggregate操作中,傳入的Pipleline是一個包含多個對象的數組,每一個對象代表了一個命令。而Group有傳入的命令中共有六個參數,其中三個……是JavaScript函數,因此每次查詢到匹配的數據,都會被轉換為對象傳入函數。從運行效率上來說,Group肯定比Aggregate差一大截。但是Group的優勢在於靈活,因為配置項可以通過自己編寫函數來實現。但是需要注意的是,盡管這樣做看起來非常靈活方便,但是一旦函數復雜度過大,將大大影響Group的性能,因此個中取舍還需要自己定奪。
那么,怎么用呢
從我個人來說,用得最多的是key、cond、$reduce、inital這四個命令。
使用上面Aggregation Pipleline中的數據為例。
key:其實和上邊說的Aggregation Pipleline中的_id是一樣的。假設這里是{cust_id: 1}cond:和上邊說的$match是一樣的。$reduce:一個函數,對匹配到的數據進行操作,這個放在后面說。initial:初始數據,假設我們這里是{count: 0}。
$reduce:
function(obj, prev) {
prev.count += obj.amount;
}
$reduce的函數(還是匿名的)可以傳入兩個參數,第一個是被轉換為對象的條目,第二個是被實例化的initial對象。
最后的結果和Aggregate操作的一樣:
{
retval: [
{
cust_id: "A123",
count: 750
},
{
cust_id: "B212",
count: 200
}
],
count: 3,
keys: 2,
ok: 1
}
那么,PHP里面應該怎么用呢
PHP中的Mongo操作和原生的使用JavaScript操作非常像。例如第一個例子的代碼如下:
<?php
$mongo = new MongoClient(DB_CONNECT);
$db = $mongo->db;
$collection = $db->selectCollection('orders');
$pipleline = array(
array(
'$match' => array(
'status' => array(
'$eq' => 'A',
),
),
),
array(
'$group' => array(
'_id' => '$cust_id',
'total' => array(
'$sum' => '$amount',
),
),
),
);
$a = $collection->aggregate($pipleline);
$result = isset($a['result']) ? $a['result'] : array();
第二個例子的代碼如下
<?php
$mongo = new MongoClient(DB_CONNECT);
$db = $mongo->db;
$collection = $db->selectCollection('orders');
$keys = array('cust_id' => 1);
$initial = array('count' => 0);
$reduce = 'function(obj, prev) {prev.count += obj.amount;}';
$cond = array('condition' => array('status' => 'A'));
$g = $collection->group($keys, $initial, $reduce, $cond);
$retval = isset($g['retval']) ? $g['retval'] : array();
其他的功能,需要自己多參考PHP官方文檔和Mongo的文檔進行嘗試了。
