一,mongodb聚合介紹
db.collection.aggregate()是基于資料處理的聚合管道,每個文檔通過一個由多個階段(stage)組成的管道,可以對每個階段的管道進行分組、過濾等功能,然後經過一系列的處理,輸出相應的結果。
通過這張圖,可以了解Aggregate處理的過程。
1、db.collection.aggregate() 可以用多個構件建立一個管道,對于一連串的文檔進行處理。這些構件包括:篩選操作的match、映射操作的project、分組操作的group、排序操作的sort、限制操作的limit、和跳過操作的skip。
2、db.collection.aggregate()使用了MongoDB内置的原生操作,聚合效率非常高,支援類似于SQL Group By操作的功能,而不再需要使用者編寫自定義的JavaScript例程。
3、 每個階段管道限制為100MB的記憶體。如果一個節點管道超過這個極限,MongoDB将産生一個錯誤。為了能夠在處理大型資料集,可以設定allowDiskUse為true來在聚合管道節點把資料寫入臨時檔案。這樣就可以解決100MB的記憶體的限制。
4、db.collection.aggregate()可以作用在分片集合,但結果不能輸在分片集合,MapReduce可以 作用在分片集合,結果也可以輸在分片集合。
5、db.collection.aggregate()方法可以傳回一個指針(cursor),資料放在記憶體中,直接操作。跟Mongo shell 一樣指針操作。
6、db.collection.aggregate()輸出的結果隻能儲存在一個文檔中,BSON Document大小限制為16M。可以通過傳回指針解決,版本2.6中後面:DB.collect.aggregate()方法傳回一個指針,可以傳回任何結果集的大小。
二,mongodb和mysql的聚合的參照
為了便于了解,先将常見的mongo的聚合操作和mysql的查詢做下類比:
下面舉了一些常用的mongo聚合例子和mysql對比,假設有一條如下的資料庫記錄(表名:orders)作為例子:
{
cust_id: "abc123",
ord_date: ISODate("2012-11-02T17:04:11.102Z"),
status: 'A',
price: 50,
items: [ { sku: "xxx", qty: 25, price: 1 },
{ sku: "yyy", qty: 25, price: 1 } ]
}
1. 統計orders表所有記錄
db.orders.aggregate( [
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
] )
類似mysql:
SELECT COUNT(*) AS count FROM orders
2.對orders表計算所有price求和
db.orders.aggregate( [
{
$group: {
_id: null,
total: { $sum: "$price" }
}
}
] )
類似mysql;
SELECT SUM(price) AS total FROM orders
3.對每一個唯一的cust_id, 計算price總和
db.orders.aggregate( [
{
$group: {
_id: "$cust_id",
total: { $sum: "$price" }
}
}
] )
類似mysql:
SELECT cust_id,
SUM(price) AS total
FROM orders
GROUP BY cust_id
4.對每一個唯一對cust_id和ord_date分組,計算price總和,不包括日期的時間部分
db.orders.aggregate( [
{
$group: {
_id: {
cust_id: "$cust_id",
ord_date: {
month: { $month: "$ord_date" },
day: { $dayOfMonth: "$ord_date" },
year: { $year: "$ord_date"}
}
},
total: { $sum: "$price" }
}
}
] )
類似mysql:
SELECT cust_id,
ord_date,
SUM(price) AS total
FROM orders
GROUP BY cust_id,
ord_date
5.對于有多個記錄的cust_id,傳回cust_id和對應的數量
db.orders.aggregate( [
{
$group: {
_id: "$cust_id",
count: { $sum: 1 }
}
},
{ $match: { count: { $gt: 1 } } }
] )
類似mysql:
SELECT cust_id,
count(*)
FROM orders
GROUP BY cust_id
HAVING count(*) > 1
6.對每個唯一的cust_id和ord_date分組,計算價格總和,并隻傳回price總和大于250的記錄,且排除日期的時間部分
db.orders.aggregate( [
{
$group: {
_id: {
cust_id: "$cust_id",
ord_date: {
month: { $month: "$ord_date" },
day: { $dayOfMonth: "$ord_date" },
year: { $year: "$ord_date"}
}
},
total: { $sum: "$price" }
}
},
{ $match: { total: { $gt: 250 } } }
] )
類似mysql:
SELECT cust_id,
ord_date,
SUM(price) AS total
FROM orders
GROUP BY cust_id,
ord_date
HAVING total > 250
7.對每個唯一的cust_id且status=A,計算price總和
db.orders.aggregate( [
{ $match: { status: 'A' } },
{
$group: {
_id: "$cust_id",
total: { $sum: "$price" }
}
}
] )
類似mysql:
SELECT cust_id,
SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
8.對每個唯一的cust_id且status=A,計算price總和并且隻傳回price總和大于250的記錄
db.orders.aggregate( [
{ $match: { status: 'A' } },
{
$group: {
_id: "$cust_id",
total: { $sum: "$price" }
}
},
{ $match: { total: { $gt: 250 } } }
] )
類似mysql:
SELECT cust_id,
SUM(price) as total
FROM orders
WHERE status = 'A'
GROUP BY cust_id
HAVING total > 250
9.對于每個唯一的cust_id,将與orders相關聯的相應訂單項order_lineitem的qty字段進行總計
db.orders.aggregate( [
{ $unwind: "$items" },
{
$group: {
_id: "$cust_id",
qty: { $sum: "$items.qty" }
}
}
] )
類似mysql:
SELECT cust_id,
SUM(li.qty) as qty
FROM orders o,
order_lineitem li
WHERE li.order_id = o.id
GROUP BY cust_id
10.統計不同cust_id和ord_date分組的數量,排除日期的時間部分
db.orders.aggregate( [
{
$group: {
_id: {
cust_id: "$cust_id",
ord_date: {
month: { $month: "$ord_date" },
day: { $dayOfMonth: "$ord_date" },
year: { $year: "$ord_date"}
}
}
}
},
{
$group: {
_id: null,
count: { $sum: 1 }
}
}
] )
類似mysql:
SELECT COUNT(*)
FROM (SELECT cust_id, ord_date
FROM orders
GROUP BY cust_id, ord_date)
as DerivedTable