MongoDB中的數據聚合工具Aggregate和Group


周煦辰 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中的管道聚合可以有非常實用的玩法,比如對聚合的結果進行排序

那么,如何使用呢

img

借用一下官方的圖,這里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的性能,因此個中取舍還需要自己定奪。

那么,怎么用呢

從我個人來說,用得最多的是keycond$reduceinital這四個命令。

使用上面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的文檔進行嘗試了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM