摘要:作者聲明:本文是學習Mongodb過程中的副產品,因為接觸時間並不長,難免有理解上的偏差,希望藉此文與感興趣的朋友討論切磋,呵呵。 去年年底,開始接觸並學習Mapreduce模型。因為工作上的
作者聲明:本文是學習Mongodb過程中的副產品,因為接觸時間並不長,難免有理解上的偏差,希望藉此文與感興趣的朋友討論切磋,呵呵。
去年年底,開始接觸並學習Mapreduce模型。因為工作上的關係,最近開始研究Mongodb,其中對其新特性(2010年四月)reduce模型實現產生的興趣,因為特別留意了一下。當然網上關於該方面的內容並不是很多,且多為EN文,所以我想有必要將學習使用過程中的一些問題作一下記錄並加以整理,因為就有了此文。
廢話不多說了,開始本文吧。
目前支援Mongodb的C#用戶端應該就是Samuel Corder 開源的這個項目了,連結:http://github.com/samus/mongodb-csharp。
其中在它的源碼包中的MongoDB.Net-Tests目錄下有對TestMapReduce和TestMapReduceBuilder相應測試案例,因為我本地沒安裝NUnit,所以接下來的內容我是在一個建立的web項目中直接Copy其中的部分代碼做的測試(註:有關Mapreduce模型的內容請查閱相關資料)。
首先我們要先載入測試資料,這裡我們以DNT中的線上使用者列表的(結構)作為依據,批量倒入10條記錄,代碼如下:
Mongo db = new Mongo("Servers=10.0.4.66:27017;ConnectTimeout=300000;ConnectionLifetime=300000;MinimumPoolSize=25;MaximumPoolSize=25;Pooled=true"); db.Connect(); Database test = db.GetDatabase("test"); IMongoCollection things = test["things"]; for (int i = 1; i <= 10;i++) { Document record = new Document(); record["_id"] = i; record["userid"] = i; record["ip"] = "10.0.7." + i; record["username"] = "使用者" + i; record["nickname"] = "使用者" + i; record["password"] = ""; record["groupid"] = i;//下面將就該欄位使用MAPREDUCE方式進行分組統計 record["olimg"] = ""; record["adminid"] = 0; record["invisible"] = 0; record["action"] = 0; record["lastactivity"] = 1; record["lastposttime"] = DateTime.Now.ToString(); record["lastpostpmtime"] = DateTime.Now.ToString(); record["lastsearchtime"] = DateTime.Now.ToString(); record["lastupdatetime"] = "1212313221231231213321"; record["forumid"] = 0; record["forumname"] = ""; record["titleid"] = 0; record["title"] = ""; record["verifycode"] = ""; record["newpms"] = 0; record["newnotices"] = 0; things.Insert(record); } db.Disconnect(); |
假定目前我們有這樣一個需求,就是找出該表中使用者組(groupid)欄位為5的使用者數,當然這裡我們不會使用普通的查詢方法,而是使用MAPREDUCE方式,其工作過程分為兩個階段:map階段和reduce階段。每個階段都有鍵/值對作為輸入和輸出,並且它們的類型可由程式員選擇。下面是其實現方式:
首先是map方法:
string mapfunction = "function() { if(this.groupid==5) {emit({groupid : 5}, 1);} }"; |
然後是reduce方法:
string reducefunction = "function(key, current ){" + " var count = 0;" + " for(var i in current) {" + " count+=current[i];" + " }" + " return { groupcount : count };" + //注意這裡的返回方式 "};"; |
最後我們使用下面代碼實現對上面MAP,REDUCE的相應代碼綁定和MapReduce類的聲明:
MapReduce mr = mrcol.MapReduce(); mr.Map = new Code(mapfunction); mr.Reduce = new Code(reducefunction4); mr.Execute(); foreach (Document doc in mr.Documents) { int groupCount = Convert.ToInt32(doc["value"]); } mr.Dispose(); |
運行上面代碼,顯示結果如下:
當前上面監看式視窗中的"id:"{"groupid":5},即是mapfunction中的定義,當然如果要統計所有使用者組(10個使用者組)中各自的使用者數,只把將mapfunction改寫成:
string mapfunction = "function() { emit(this.groupid, 1); }";
這樣,它就會按目前使用者所屬的groupid來作為鍵(確保不重複),凡是同一組的使用者就作為輸出進行發送(emit),emit可以理解為調用reduce方法,這裡參數為1[即累加1操作])。
目前我在網上打到mongodb樣本基本上都是圍繞分組統計功能展開的。
當然就其傳參和傳回值都可以使用類似元組的方式,記得上面的“emit({groupid : 5}, 1)”代碼嗎。傳回值這裡也可以使用下面的方式:
string reducefunction = "function(key, current ){" + " var count = 0;" + " for(var i in current) {" + " count+=current[i];" + " }" + " return { groupcount : count };" + //注意這裡的返回方式 "};"; |
傳回型別變了,取值的方式也要發生變成:
int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString()); |
當然,上面的MapReduce 類的聲明使用方式過於拘謹,下面使用鏈式調用的方式:
using (MapReduceBuilder mrb = mrcol.MapReduceBuilder().Map(mapfunction).Reduce(reducefunction)) { using (MapReduce mr = mrb.Execute()) { foreach (Document doc in mr.Documents) { int groupCount = int.Parse(((Document)doc["value"])["groupcount"].ToString()); } } } |