2016年4月21日 星期四

mongoDB .Net Driver 使用 MapReduce

目前有一個需求,在 mongoDB的一個 collection,有一個屬性 created_date記錄資料建立時的日期,我希望利用這個屬性,以30天為一個單位找出每個間隔日期,在關連式資料庫會使用如下 SQL來完成需求:

SELECT CONVERT(varchar(10), DATEADD(d, (datediff(d,GETDATE(),created_date) /30)*30,GETDATE()),111)
 FROM [DATA].[dbo].[expense]
 group by CONVERT(varchar(10), DATEADD(d, (datediff(d,GETDATE(),created_date) /30)*30,GETDATE()),111)

這樣捉出來的資料如下:
2015/12/23
2015/10/24
2015/11/23

而我的專案是 ASP.Net MVC 5,所以有使用 mongoDB .Net Driver 2.2,一開始利用 LINQ寫法如下:
 var query = fRepo.GetAll().GroupBy(x =>
             DateTime.Now.AddDays(((int)(DateTime.Now.Subtract(DateTime.ParseExact(x.Date, "yyyy/MM/dd", System.Globalization.CultureInfo.InvariantCulture)).TotalDays) / Day30) * (-1 * Day30)).ToString("yyyy/MM/dd")
      
           ).Select(x => new DayPeriod { date1 =x.Key) }).OrderBy(x => x);



在執行時就會出現 *** is not supported in the expression tree的錯誤訊息,後來又把寫法改為先使用 Select再作  GroupBy,程式如下:
var query = fRepo.GetAll().Select(x => new DayPeriod { date1 = DateTime.Now.AddDays(((int)(DateTime.Now.Subtract(DateTime.ParseExact(x.Date, "yyyy/MM/dd", System.Globalization.CultureInfo.InvariantCulture)).TotalDays) / Day30) * (-1 * Day30)).ToString("yyyy/MM/dd"), count = 1 }).GroupBy(x => x.date1).Select(x => new DayPeriod { date1 = x.Key, count = x.Count() });

但還是一樣的錯誤,
可能在 Where內容的寫法不是 db的語法時可能就會出錯吧,之前在用 Entity Framework時都會先把比較複雜的 SQL Script先寫成 View, 所以就沒碰到這個問題。

所以要解決這個問題,我的方式是用 MapReduce,參考文件 MongoDB C# driver 2.0: How to get the result from MapReduceAsync

程式碼如下:

 var map = new BsonJavaScript
                ( @"function(){
                     var log1 = this;
                     var currDate = new Date();
                     var diff = Math.floor(Math.abs(currDate - log1.createDate) / 30/(1000*60*60*24));
                    
                     var segDate = new Date() ;
                     segDate.setDate(segDate.getDate() + (diff*30*-1))
                     var monStr = (segDate.getMonth() + 1);
                     if(monStr < 10) { monStr = '0'+monStr; }
                     var segDateStr = segDate.getFullYear()  + '/' + monStr + '/' + segDate.getDate() ;
                     emit(segDateStr,1);           
                };" );
            var reduce = new BsonJavaScript(
                @"function(key,values){
                      var sum = 0;
                      values.forEach(function(value){              
                        sum += value;
                     
                    }); 
                    return sum;
                };" );
            var options = new MapReduceOptions< ExternalReviewLog, SimpleResult <int>>();
            options.OutputOptions = MapReduceOutputOptions.Inline;

            var result1 = fRepo.getCollection().MapReduceAsync(map, reduce, options).Result.ToList().AsQueryable().OrderByDescending(x =>x.Id).ToPagedList(page, DefaultPageSize);


以下就是利用 mapreduce找出區間日期後,一個區間日期就一個分頁,一個分頁就顯示30天區間資訊的圖表

沒有留言:

張貼留言