MongoDB 作為 NoSql 文檔型資料庫,在全球範圍得到廣泛的支援與應用。在比較常用的資料庫功能中,相對于普通的增删改查,使用 group 聚合分組統計有些複雜,而 MongoDB 也給予了支援。本文将對MongoDb分組的實作方法及示例進行分析,通過在 MongoDB 腳本中操作、使用集算器 SPL 語言操作兩種操作途徑,進行簡單的歸納總結。具體的問題場景包括以下幾個方面:
- 内嵌數組結構的統計........................................................................... 1
- 内嵌文檔求和..................................................................................... 2
- 分段分組結構統計.............................................................................. 4
- 多字段分組統計................................................................................. 6
-
内嵌數組結構的統計
對嵌套數組結構中的資料進行統計處理例如查詢考試科目的平均分及每個學生的總成績:
測試資料:

由于各科分數 scroe 是按課目、成績記錄的數組結構,統計前需要将它拆解,将每科成績與學生對應,然後再實作分組計算。這需要熟悉 unwind 與 group 組合的應用。
腳本說明:
A1:連接配接 mongodb 資料庫。
A2:擷取 student 表中的資料。
A3:将 scroe 資料合并成序表,再按課程分組,計算平均分。
A4:統計每個學生的成績後傳回列名為 NAME、TOTAL 的序表。new 函數表示生成新序表。
A5:關閉資料庫連接配接。
這個嵌套結構統計的例子比較常見,相信很多人都遇到過,需要先拆解再分組計算,主要是熟悉 mongodb 對嵌套資料結構的處理。
-
内嵌文檔求和
對内嵌文檔中的資料求和處理, 例如統計下面每條記錄中 income,output 的數量和。
Mongodb腳本:
var fields = [ "income", "output"];
db.computer.aggregate([
{
$project:{
"values":{
$filter:{
input:{
"$objectToArray":"$$ROOT"
},
cond:{
$in:[
"$$this.k",
fields
]
}
}
}
}
},
$unwind:"$values"
key:"$values.k",
values:{
"$sum":{
"$let":{
"vars":{
"item":{
"$objectToArray":"$values.v"
}
},
"in":"$$item.v"
}
{$sort: {"_id":-1}},
{ "$group": {
"_id": "$_id",
'income':{"$first": "$values"},
"output":{"$last": "$values"}
}},
]);
oe 是按課目、成績記錄的數組結構,統計前需要将它拆解,将每科成績與學生對應,然後再實作分組計算。這需要熟悉 unwind 與 group 組合的應用。
SPL 腳本 (student.dfx):
filter将income,output 部分資訊存放到數組中,用 unwind 拆解成記錄,再累計各項值求和,按 _id 分組合并資料。
SPL腳本:
A1:連接配接資料庫
A2:擷取 computer 表中的資料
A3:将 income、output 字段中的資料分别轉換成序列求和,再與 ID 組合生成新序表
A4:關閉資料庫連接配接。
擷取子記錄的字段值,然後求和,相對于 mongo 腳本簡化了不少。這個内嵌文檔與内嵌數組在組織結構上有點類似,不小心容易混淆,是以需要特别注意與上例中的 scroe 數組結構比較,寫出的腳本有所不同。
-
分段分組結構統計
統計各段内的記錄數量。例如下面按銷售量分段,統計各段内的資料量,資料如下:
Mongo 腳本
var a_count=0;
var b_count=0;
var c_count=0;
var d_count=0;
var e_count=0;
db.sales.find({
}).forEach(
function(myDoc) {
if (myDoc.SALES <3000) {
a_count += 1;
}
else if (myDoc.SALES <5000) {
b_count += 1;
else if (myDoc.SALES <7500) {
c_count += 1;
else if (myDoc.SALES <10000) {
d_count += 1;
else {
e_count += 1;
}
}
);
print("a_count="+a_count)
print("b_count="+b_count)
print("c_count="+c_count)
print("d_count="+d_count)
print("e_count="+e_count)
這個需求按條件分段分組,mongodb 沒有提供對應的 api,實作起來有點繁瑣,上面的程式是其中實作的一個例子參考,當然也可以寫成其它實作形式。下面看看集算器腳本的實作。
A1:定義 SALES 分組區間。
A2:連接配接 mongodb 資料庫。
A3:擷取 sales 表中的資料。
A4:根據 SALES 區間分組統計員工數。其中函數 pseg()表示傳回成員在序列中的區段序号,int() 表示轉換成整數。
Mongodb腳本與 SPL 腳本都實作了預期的結果,但函數pseg 的使用讓 SPL 腳本精簡了不少。
-
多字段分組統計
統計分類項下的總數及各子項數。下面統計按 addr 分類的 book 的數量以及其下不同 book 類型的數量。
Mongo腳本
db.books.aggregate([
{ "$group": {
"_id": {
"addr": "$addr",
"book": "$book"
},
"bookCount": {"$sum": 1}
"_id": "$_id.addr",
"books": {
"$push": {
"book": "$_id.book",
"count": "$bookCount"
},
},
"count": {"$sum": "$bookCount"}
{"$sort": { "count": -1} },
{ "$project": {
"books": {"$slice": [ "$books", 2] },
"count": 1
}}
]).pretty()
先按 addr,book 分組統計 book 數,再按 addr 分組統計 book 數,調整顯示順序。
SPL腳本 (books.dfx):
A1:連接配接 mongodb 資料庫。
A2:擷取 books 表中的資料。
A3:按 addr,book 分組統計 book 數顧。
A4:再按 addr 分組統計 book 數。
A5:将 A4 中的 Total 按 addr 關聯後合并到序表中。
B5: 傳回序表 A5。
A6:關閉資料庫連接配接。
這個例子中的 SPL 腳本除了一如既往的精簡清晰外,還顯示了如何簡單友善地與 Java 程式內建。
在 Java 程式中如果要對 MongoDB 實作上面的分組統計功能,需要根據不同的需求重新一五一十地實作,比較麻煩的同時也不通用。而如果用集算器來實作就容易多了,集算器提供了 JDBC 驅動程式,支援在 Java 程式中用 JDBC 存儲過程方式通路計算結果,調用方法與調用存儲過程相同。(JDBC 具體配置參考《集算器教程》中的“JDBC基本使用”章節)
Java 調用主要過程如下:
public void testStudent (){
Connection con = null;
com.esproc.jdbc.InternalCStatement st;
try{
// 建立連接配接
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
//調用存儲過程,其中books是 dfx 的檔案名
st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call books ()");
//執行存儲過程
st.execute();
// 擷取結果集
ResultSet rs = st.getResultSet();
。。。。。。。
catch(Exception e){
System.out.println(e);
}
可以看到,集算器的計算結果能夠很友善地供 Java 應用程式使用。除了上面的調用方式,程式也可以修改成直接加載 SPL 腳本的函數,用 SPL 腳本檔案名當參數來實作。同時,集算器也支援 ODBC 驅動,與其它支援 ODBC 的語言內建也與此類似。
簡單總結一下,MongoDB 的聚合分組計算的操作與存儲文檔的結構息息相關,豐富的文檔結構一方面有利于存儲,同時資料查詢展示也可以做到多樣化,但另一方面也帶來了 shell 腳本操作的複雜性,寫起來比較不容易, 需要考慮的細節、步驟也比較多。通過上面這幾個簡單案例的分析比較,可以看到集算器 SPL 在實作分組統計方面能簡化操作,降低難度,進而有效地幫助我們解決問題。