接上一篇... 見: http://my.oschina.net/zhzhenqin/blog/97949 javascript
爲了方便我仍是把個人表結構貼上來: java
和數據庫同樣group經常用於統計。MongoDB的group還有不少限制,如:返回結果集不能超過16M, group操做不會處理超過10000個惟一鍵,好像還不能利用索引[不很肯定]。 shell
Group大約須要一下幾個參數。 數據庫
下面我用Java對他們作一些測試。 數組
咱們以age年齡統計集合中存在的用戶。Spring Schema和上次的同樣。有了MongoTemplate對象咱們能夠作全部事的。以age統計用戶測試代碼如: 函數
@Test public void testGroupBy() throws Exception { String reduce = "function(doc, aggr){" + " aggr.count += 1;" + " }"; Query query = Query.query(Criteria.where("age").exists(true)); DBObject result = mongoTemplate.getCollection("person").group(new BasicDBObject("age", 1), query.getQueryObject(), new BasicDBObject("count", 0), reduce); Map map = result.toMap(); System.out.println(map); for (Map.Entry o : map.entrySet()) { System.out.println(o.getKey() + " " + o.getValue()); } }
key爲new BasicDBObject("age", 1) 測試
cond爲:Criteria.where("age").exists(true)。即用戶中存在age字段的。 ui
initial爲:new BasicDBObject("count", 0),即初始化reduce中人的個數爲count爲0。假如咱們想在查詢的時候給每一個年齡的人增長10個假用戶。咱們只須要傳入BasicDBObject("count", 10). spa
reduce爲:reduce的javascript函數 .net
上面的執行輸出如:
2 [age:23.0, count:1.0] 1 [age:25.0, count:1.0] 0 [age:24.0, count:1.0]
前面的是一個序號,是Mongo的java-driver加上去的。咱們能夠看到結果在後面。
不過你可能都以爲reduce這段代碼用Java寫的太繁瑣了,要是和Python同樣支持多行字符串多好啊。 我也煩。下面的例子我用Groovy寫,不過我儘可能寫的貼近Java。
一樣的reduce,用Groovy只需這樣:
def reduce = """ function(doc, aggr){ aggr.count += 1; } """;
用age統計用戶這是基本的需求了。下面我來幾個高級點的。
個人表結構中用戶的朋友[myFriends]是一個數組類型的,mongo提供的查詢中對數組查詢時數組長度$size只能用來判斷,卻不能用來輸出[至少我沒找到]。那麼咱們用group操做來統計一下每一個人有幾個朋友。測試代碼如:
@Test void testFriendGroupUserFriendCount() throws Exception { def reduce = """ function(doc, aggr){ aggr.manId = doc.manId; doc.myFriends.forEach(function(z){ aggr.count += 1; }) } """; Query query = Query.query(Criteria.where("myFriends").exists(true)); DBObject result = mongoTemplate.getCollection("person").group( new BasicDBObject("manId", 1), query.getQueryObject(), new BasicDBObject("count", 0), reduce); Map map = result.toMap(); for (Map.Entry o : map.entrySet()) { System.out.println(o.getKey() + " ==> " + o.getValue()); } }執行結果如:
2 ==> [manId:345678, count:2.0] 1 ==> [manId:234567, count:2.0] 0 ==> [manId:123456, count:4.0]
上面的reduce中遍歷文檔的數組用了forEach,我記得好像Javascript中不能這麼作吧? 我對js不熟,但願牛人解答下。
上面的例子一直都沒用到finallize,下面的測試我但願能用上。
咱們統計每一個人最喜歡水果是哪一個?每一個人都有n個本身的喜歡的水果,fruits.boost是每一個水果的權重。那麼咱們找出最喜歡的那個?
測試代碼:
@Test void testGroupByFruitFinallize() throws Exception { def reduce = """ function(doc, out) { out.name = doc.manName; for(i in doc.fruits) { if(doc.fruits[i] in out.fruits) { out.fruits[doc.fruits[i].fruitId]++; } else { out.fruits[doc.fruits[i].fruitId] = 1; } } } """; def finallizer = """ function(out) { var mostPopular = 0; for(i in out.fruits) { if(out.fruits[i] > mostPopular) { out.fruitId = i; mostPopular = out.fruits[i]; } } delete out.fruits; return out; } """; Query query = new BasicQuery("{}"); long time = System.currentTimeMillis(); DBObject result = mongoTemplate.getCollection("person").group(new BasicDBObject("fruits", true), query.getQueryObject(), new BasicDBObject("fruits", new BasicDBObject()), reduce, finallizer); System.out.println("use time: " + (System.currentTimeMillis() - time)); Map map = result.toMap(); for (Map.Entry o : map.entrySet()) { System.out.println(o.getKey() + " " + o.getValue()); } }它的測試輸出爲:
2 [name:ZhenZi, fruitId:www] 1 [name:YangYan, fruitId:www] 0 [name:ZhenQin, fruitId:aaa]
OK,他完成了。
關於keyf我找了不少資料,目前還沒發現怎麼使用。但願有人能解答下。