mongodb常用的兩種group方法,以及對結果排序

小迷妹大米姐發表於2016-05-05

mongodb作為no-sql資料庫的典型代表,擁有著儲存海量資料的效能,在插入資料和查詢資料方便也有著相對於其他關係型資料庫明顯的優勢,最近學習了mongodb,發現mongodb中沒有mysql中的group關鍵字,但是同樣的以其他形式實現了對應的功能,下面總結了兩種mongdb常用的group方法,介紹給大家。
第一種方法是利用管道來進行,管道是由一系列的功能節點組成的,當文件從一個操作節點流向下一個操作節點的時候,每個操作節點就會對文件做出相應的操作。主要是有兩個功能,1,進行過濾,2,變換,也就是改變文件的輸出形式。
主要是通過group,

group,
sum,avg,
avg,
group來進行聚合求平均值以及進行求和操作。
Aggreagtion管道操作符主要有:
match:用於對文件集合進行篩選,之後就可以在篩選得到的文件子集中做聚合。
project:管道的投射,可以從子文件中提取欄位,可以重新命名欄位
group:將文件根據特定的欄位的不同值進行分組
unwind:可以將陣列中的每一個值拆分為單獨的文件。
這裡舉一個mongodb權威指南上的例子
一篇擁有多條評論的部落格,利用unwind可以將每條評論都拆分為一個獨立的文件。
sort:根據任何欄位或者是多個欄位可以進行排序,如果是大量的文件需要排序,建議在管道的第一階段排序。
limit:接受一個數字n,返回結果集的前n個文件。
skip:接受一個數字n,丟棄結果集中的前n個文件,將剩餘文件作為結果返回。
下面是利用管道的group使用以及mapreduce在mongodb中的使用

Mongo m = new Mongo(“localhost”:27017);
DB db = m.getDB(“test”);//test為資料庫的名稱
DBCollection coll = db.getCollection(“test”)//test為集合名稱
//下面則就行構造管道中操作節點的操作符,主要用到的物件就是DBObject
DBObject match = new BasicDBObject("$match", new BasicDBObject("欄位名", "欄位值"));//限定查詢條件,相當於Query,規定某個欄位的值進行groupby
DBObject groupFields = new BasicDBObject(“_id”,”$欄位名”);//也就是說groupby這個欄位名
groupFields.put(“SumElectricty”,new BasicDBObject(“$sum”,”$欄位名”));//對這個欄位名的值進行求和,並且把這個和值生成一個名為SumElectricty的欄位。
DBObject group = new BasicDBObject(“$group”,groupFields);
//放到管道中將這些節點運算子運算起來
AggregationOutput output = coll.aggregate(match,group);
//AggregationOutput 類有getCommandResult(),返回執行結果,結果是CommandResult,可以檢視到。

mapreduce在mongodb中同樣可以聚類,採用的是javascript作為查詢語言,但是不得不承認的是,mapreduce非常慢,一般是不會用在實時的資料分析中的。這裡做的是以在一個時間段內,對mac_id進行聚合,求欄位electrity_quantity的和,並且排序顯示出前n名。
起初我用的是比較笨的方法,並沒有注意到query自身就可以進行排序並且還可以發揮前n個最大的結果集的能力。下面是這兩個方法的程式碼。。

public Map groupbyId(Date d1,Date d2){
         double total = 0; 
         String reduce = "function(doc, aggr){" +  
 "            aggr.total += doc.electric_quantity;" +  
"        }"; //doc代表的是當前的文件,而aggr為之前所處理的文件的集合
        Query query = new Query();
        Criteria criteria = Criteria.where("record_time").gte(d1);//欄位record_time的值大於等於d1
        criteria.and("record_time").lte(d2);//record_time的值小於等於d2
        query.addCriteria(criteria);//將條件插入到查詢中
         DBCollection coll = mongoTemplate.getCollection("smartsocketer");//獲得名為smartsocketer的集合
         DBObject result = coll.group(new BasicDBObject("mac_id", 1), query.getQueryObject(), new BasicDBObject("total", total), reduce);//groupby mac_id,在剛剛的查詢條件下,執行reduce函式,並且將獲得值放在名為total的欄位中
         Map<String,BasicDBObject> map = result.toMap(); //將結果轉換為k-v的map
        return map;


    }
public List<BasicDBObject> sortMapByValue(Map<String, BasicDBObject> oriMap,int n) {  
        List<BasicDBObject>  mappingList = new ArrayList<BasicDBObject>(); 
        if (oriMap != null && !oriMap.isEmpty()) {  
        Iterator it = oriMap.entrySet().iterator();//獲得是文件
            while(it.hasNext()){
                Map.Entry<String,BasicDBObject> map = (Entry<String, BasicDBObject>) it.next();
                BasicDBObject dbob = map.getValue();
                dbob.entrySet();
                mappingList.add(dbob);
                }
       Collections.sort(mappingList, new Comparator<BasicDBObject>() {  
             public int compare(BasicDBObject entry1,  BasicDBObject entry2) {  
                            double value1 = 0, value2 = 0;  
                            try {  
  value1 = Double.parseDouble(entry1.getString("total"));
  value2 = Double.parseDouble(entry2.getString("total"));
                            } catch (NumberFormatException e) {  
                                value1 = 0;  
                                value2 = 0;  
                            }  
                           if(value1-value2<0)
                           {
                               return 1;
                           }
                           if(value1-value2>0)
                           {
                               return -1;
                           }
                           return 0;
                        }


                    }); 
                     }  
   return mappingList.subList(0, n);  //擷取mappingList,而mappingList是排好序的list,直接擷取就可以獲得top; 
    }  

剛開始一直在困擾的是我在進行map排序的時候,實際上將map.entry放到list中去然後定義比較器比較entry的value就可以了,後來我發現group後發揮的map是下面這種形式的。
{1,{01,23.5}}
也就是說map的key值完全是索引,是自動生成的,並不是我所想到的mac_id欄位值。
實際上就是BasicDBObject,因此直接定義這個物件的比較器就可以了。
當然更為簡單的方法是利用query來做,在查詢的時候就
query.with(new Sort(Direction.DESC, “欄位名”));
上面就是兩種方法,還有很多待補充的地方。挖坑第一天,一定要堅持下去>:<

相關文章