您好,登錄后才能下訂單哦!
MongoDB 在進(jìn)行分組統(tǒng)計(jì)時(shí)如果面對(duì)一些比較復(fù)雜的計(jì)算情況,往往會(huì)遇到 shell 腳本過(guò)于復(fù)雜的問(wèn)題。而集算器 SPL 語(yǔ)言,則因其有豐富的函數(shù)庫(kù)及易用性恰好能彌補(bǔ) Mongo 這方面的不足。
MongoDB 作為 NoSql 文檔型數(shù)據(jù)庫(kù),在全球范圍得到廣泛的支持與應(yīng)用。在比較常用的數(shù)據(jù)庫(kù)功能中,相對(duì)于普通的增刪改查,使用 group 聚合分組統(tǒng)計(jì)有些復(fù)雜,而 MongoDB 也給予了支持。本文將對(duì)MongoDb分組的實(shí)現(xiàn)方法及示例進(jìn)行分析,通過(guò)在 MongoDB 腳本中操作、使用集算器 SPL 語(yǔ)言操作兩種操作途徑,進(jìn)行簡(jiǎn)單的歸納總結(jié)。具體的問(wèn)題場(chǎng)景包括以下幾個(gè)方面:
1. 內(nèi)嵌數(shù)組結(jié)構(gòu)的統(tǒng)計(jì)........................................................................... 1
2. 內(nèi)嵌文檔求和..................................................................................... 2
3. 分段分組結(jié)構(gòu)統(tǒng)計(jì).............................................................................. 4
4. 多字段分組統(tǒng)計(jì)................................................................................. 6
對(duì)嵌套數(shù)組結(jié)構(gòu)中的數(shù)據(jù)進(jìn)行統(tǒng)計(jì)處理例如查詢(xún)考試科目的平均分及每個(gè)學(xué)生的總成績(jī):
測(cè)試數(shù)據(jù):
_id | name | sex | Scroe |
1 | Tom | F | [{"lesson":" Physics","mark":60 }, {"lesson":" Chemical","mark":72 }] |
2 | Jerry | M | [{"lesson":" Physics","mark":92 }, {"lesson":" Math","mark":81 }] |
期待統(tǒng)計(jì)結(jié)果:
Physics | 76 | Tom | 132 | |
Chemical | 72 | Jerry | 173 | |
Math | 81 |
Mongodb腳本:
db.student.aggregate( [ | db.student.aggregate( [ |
由于各科分?jǐn)?shù) scroe 是按課目、成績(jī)記錄的數(shù)組結(jié)構(gòu),統(tǒng)計(jì)前需要將它拆解,將每科成績(jī)與學(xué)生對(duì)應(yīng),然后再實(shí)現(xiàn)分組計(jì)算。這需要熟悉 unwind 與 group 組合的應(yīng)用。
SPL 腳本 (student.dfx):
A | B | |
1 | =mongo_open("mongodb://127.0.0.1:27017/raqdb") | |
2 | =mongo_shell(A1,"student.find()").fetch() | |
3 | =A2.conj(scroe).groups(lesson:LESSON;avg(mark):AVG) | |
4 | =A2.new(name:NAME,scroe.sum(mark):TOTAL) | |
5 | >A1.close() |
按課目統(tǒng)計(jì)的總分?jǐn)?shù):
LESSON | AVG |
Chemical | 72.0 |
Math | 81.0 |
Physics | 76.0 |
每個(gè)學(xué)生的總成績(jī):
NAME | TOTAL |
Tom | 132 |
Jerry | 173 |
腳本說(shuō)明:
A1:連接 mongodb 數(shù)據(jù)庫(kù)。
A2:獲取 student 表中的數(shù)據(jù)。
A3:將 scroe 數(shù)據(jù)合并成序表,再按課程分組,計(jì)算平均分。
A4:統(tǒng)計(jì)每個(gè)學(xué)生的成績(jī)后返回列名為 NAME、TOTAL 的序表。new 函數(shù)表示生成新序表。
A5:關(guān)閉數(shù)據(jù)庫(kù)連接。
這個(gè)嵌套結(jié)構(gòu)統(tǒng)計(jì)的例子比較常見(jiàn),相信很多人都遇到過(guò),需要先拆解再分組計(jì)算,主要是熟悉 mongodb 對(duì)嵌套數(shù)據(jù)結(jié)構(gòu)的處理。
對(duì)內(nèi)嵌文檔中的數(shù)據(jù)求和處理, 例如統(tǒng)計(jì)下面每條記錄中 income,output 的數(shù)量和。
測(cè)試數(shù)據(jù):
_id | income | output |
1 | {"cpu":1000, "mem":500, "mouse":"100"} | {"cpu":1000, "mem":600 ,"mouse":"120"} |
2 | {"cpu":2000, "mem":1000, "mouse":"50","mainboard":500 } | {"cpu":1500, "mem":300} |
期待統(tǒng)計(jì)結(jié)果:
_id | income | output |
1 | 1600 | 1720 |
2 | 3550 | 1800 |
Mongodb腳本:
var fields = [ "income", "output"]; |
filter將income,output 部分信息存放到數(shù)組中,用 unwind 拆解成記錄,再累計(jì)各項(xiàng)值求和,按 _id 分組合并數(shù)據(jù)。
SPL腳本:
A | B | |
1 | =mongo_open("mongodb://127.0.0.1:27017/raqdb") | |
2 | =mongo_shell(A1,"computer.find()").fetch() | |
3 | =A2.new(_id:ID,income.array().sum():INCOME,output.array().sum():OUTPUT) | |
4 | >A1.close() |
統(tǒng)計(jì)結(jié)果
ID | INCOME | OUTPUT |
1 | 1600.0 | 1720.0 |
2 | 3550.0 | 1800.0 |
腳本說(shuō)明:
A1:連接數(shù)據(jù)庫(kù)
A2:獲取 computer 表中的數(shù)據(jù)
A3:將 income、output 字段中的數(shù)據(jù)分別轉(zhuǎn)換成序列求和,再與 ID 組合生成新序表
A4:關(guān)閉數(shù)據(jù)庫(kù)連接。
獲取子記錄的字段值,然后求和,相對(duì)于 mongo 腳本簡(jiǎn)化了不少。這個(gè)內(nèi)嵌文檔與內(nèi)嵌數(shù)組在組織結(jié)構(gòu)上有點(diǎn)類(lèi)似,不小心容易混淆,因此需要特別注意與上例中的 scroe 數(shù)組結(jié)構(gòu)比較,寫(xiě)出的腳本有所不同。
統(tǒng)計(jì)各段內(nèi)的記錄數(shù)量。例如下面按銷(xiāo)售量分段,統(tǒng)計(jì)各段內(nèi)的數(shù)據(jù)量,數(shù)據(jù)如下:
_id | NAME | STATE | SALES |
1 | Ashley | New York | 11000 |
2 | Rachel | Montana | 9000 |
3 | Emily | New York | 8800 |
4 | Matthew | Texas | 8000 |
5 | Alexis | Illinois | 14000 |
分段方法:0-3000;3000-5000;5000-7500;7500-10000;10000 以上。
期望結(jié)果:
Segment | number |
3 | 3 |
4 | 2 |
Mongo 腳本
var a_count=0; |
這個(gè)需求按條件分段分組,mongodb 沒(méi)有提供對(duì)應(yīng)的 api,實(shí)現(xiàn)起來(lái)有點(diǎn)繁瑣,上面的程序是其中實(shí)現(xiàn)的一個(gè)例子參考,當(dāng)然也可以寫(xiě)成其它實(shí)現(xiàn)形式。下面看看集算器腳本的實(shí)現(xiàn)。
SPL腳本:
A | B | |
1 | [3000,5000,7500,10000,15000] | |
2 | =mongo_open("mongodb://127.0.0.1:27017/raqdb") | |
3 | =mongo_shell(A2,"sales.find()").fetch() | |
4 | =A3.groups(A1.pseg(int(~.SALES)):Segment;count(1): number) | |
5 | >A2.close() |
腳本說(shuō)明:
A1:定義 SALES 分組區(qū)間。
A2:連接 mongodb 數(shù)據(jù)庫(kù)。
A3:獲取 sales 表中的數(shù)據(jù)。
A4:根據(jù) SALES 區(qū)間分組統(tǒng)計(jì)員工數(shù)。其中函數(shù) pseg()表示返回成員在序列中的區(qū)段序號(hào),int() 表示轉(zhuǎn)換成整數(shù)。
A5:關(guān)閉數(shù)據(jù)庫(kù)連接。
Mongodb腳本與 SPL 腳本都實(shí)現(xiàn)了預(yù)期的結(jié)果,但函數(shù)pseg 的使用讓 SPL 腳本精簡(jiǎn)了不少。
統(tǒng)計(jì)分類(lèi)項(xiàng)下的總數(shù)及各子項(xiàng)數(shù)。下面統(tǒng)計(jì)按 addr 分類(lèi)的 book 的數(shù)量以及其下不同 book 類(lèi)型的數(shù)量。
addr | book |
address1 | book1 |
address2 | book1 |
address1 | book5 |
address3 | book9 |
address2 | book5 |
address2 | book1 |
address1 | book1 |
address15 | book1 |
address4 | book3 |
address5 | book1 |
address7 | book11 |
address1 | book1 |
期望結(jié)果:
_id | Total | books | Count |
address1 | 4 | book1 | 3 |
book5 | 1 | ||
address15 | 1 | book1 | 1 |
address2 | 3 | book1 | 2 |
book5 | 1 | ||
address3 | 1 | book9 | 1 |
address4 | 1 | book3 | 1 |
address5 | 1 | book1 | 1 |
address7 | 1 | book11 | 1 |
Mongo腳本
db.books.aggregate([ |
先按 addr,book 分組統(tǒng)計(jì) book 數(shù),再按 addr 分組統(tǒng)計(jì) book 數(shù),調(diào)整顯示順序。
SPL腳本 (books.dfx):
A | B | |
1 | =mongo_open("mongodb://127.0.0.1:27017/raqdb") | |
2 | =mongo_shell(A1,"books.find()") | |
3 | =A2.groups(addr,book;count(book): Count) | |
4 | =A3.groups(addr;sum(Count):Total) | |
5 | =A3.join(addr,A4:addr,Total) | return A5 |
6 | >A1.close() |
計(jì)算結(jié)果:
Address | book | Count | Total |
address1 | book1 | 3 | 4 |
address1 | book5 | 1 | 4 |
address15 | book1 | 1 | 1 |
address2 | book1 | 2 | 3 |
address2 | book5 | 1 | 3 |
address3 | book9 | 1 | 1 |
address4 | book3 | 1 | 1 |
address5 | book1 | 1 | 1 |
address7 | book11 | 1 | 1 |
腳本說(shuō)明:
A1:連接 mongodb 數(shù)據(jù)庫(kù)。
A2:獲取 books 表中的數(shù)據(jù)。
A3:按 addr,book 分組統(tǒng)計(jì) book 數(shù)顧。
A4:再按 addr 分組統(tǒng)計(jì) book 數(shù)。
A5:將 A4 中的 Total 按 addr 關(guān)聯(lián)后合并到序表中。
B5: 返回序表 A5。
A6:關(guān)閉數(shù)據(jù)庫(kù)連接。
這個(gè)例子中的 SPL 腳本除了一如既往的精簡(jiǎn)清晰外,還顯示了如何簡(jiǎn)單方便地與 Java 程序集成。
在 Java 程序中如果要對(duì) MongoDB 實(shí)現(xiàn)上面的分組統(tǒng)計(jì)功能,需要根據(jù)不同的需求重新一五一十地實(shí)現(xiàn),比較麻煩的同時(shí)也不通用。而如果用集算器來(lái)實(shí)現(xiàn)就容易多了,集算器提供了 JDBC 驅(qū)動(dòng)程序,支持在 Java 程序中用 JDBC 存儲(chǔ)過(guò)程方式訪問(wèn)計(jì)算結(jié)果,調(diào)用方法與調(diào)用存儲(chǔ)過(guò)程相同。(JDBC 具體配置參考《集算器教程》中的“JDBC基本使用”章節(jié))
Java 調(diào)用主要過(guò)程如下:
public void testStudent (){
Connection con = null;
com.esproc.jdbc.InternalCStatement st;
try{
// 建立連接
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
//調(diào)用存儲(chǔ)過(guò)程,其中books是 dfx 的文件名
st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call books ()");
//執(zhí)行存儲(chǔ)過(guò)程
st.execute();
// 獲取結(jié)果集
ResultSet rs = st.getResultSet();
。。。。。。。
catch(Exception e){
System.out.println(e);
}
可以看到,集算器的計(jì)算結(jié)果能夠很方便地供 Java 應(yīng)用程序使用。除了上面的調(diào)用方式,程序也可以修改成直接加載 SPL 腳本的函數(shù),用 SPL 腳本文件名當(dāng)參數(shù)來(lái)實(shí)現(xiàn)。同時(shí),集算器也支持 ODBC 驅(qū)動(dòng),與其它支持 ODBC 的語(yǔ)言集成也與此類(lèi)似。
簡(jiǎn)單總結(jié)一下,MongoDB 的聚合分組計(jì)算的操作與存儲(chǔ)文檔的結(jié)構(gòu)息息相關(guān),豐富的文檔結(jié)構(gòu)一方面有利于存儲(chǔ),同時(shí)數(shù)據(jù)查詢(xún)展示也可以做到多樣化,但另一方面也帶來(lái)了 shell 腳本操作的復(fù)雜性,寫(xiě)起來(lái)比較不容易, 需要考慮的細(xì)節(jié)、步驟也比較多。通過(guò)上面這幾個(gè)簡(jiǎn)單案例的分析比較,可以看到集算器 SPL 在實(shí)現(xiàn)分組統(tǒng)計(jì)方面能簡(jiǎn)化操作,降低難度,從而有效地幫助我們解決問(wèn)題。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。