摘要:上一篇文章指南下一篇文章為在集合上執(zhí)行基本的聚合任務(wù)提供了一些命令。也可以給傳遞一個(gè)查詢文檔,會(huì)計(jì)算查詢結(jié)果的數(shù)量對(duì)分頁顯示來說總數(shù)非常必要共個(gè),目前顯示個(gè)。使用時(shí)必須指定集合和鍵。指定要進(jìn)行分組的集合。
上一篇文章:MongoDB指南---17、MapReduce
下一篇文章:
MongoDB為在集合上執(zhí)行基本的聚合任務(wù)提供了一些命令。這些命令在聚合框架出現(xiàn)之前就已經(jīng)存在了,現(xiàn)在(大多數(shù)情況下)已經(jīng)被聚合框架取代。然而,復(fù)雜的group操作可能仍然需要使用JavaScript,count和distinct操作可以被簡(jiǎn)化為普通命令,不需要使用聚合框架。
countcount是最簡(jiǎn)單的聚合工具,用于返回集合中的文檔數(shù)量:
> db.foo.count() 0 > db.foo.insert({"x" : 1}) > db.foo.count() 1
不論集合有多大,count都會(huì)很快返回總的文檔數(shù)量。
也可以給count傳遞一個(gè)查詢文檔,Mongo會(huì)計(jì)算查詢結(jié)果的數(shù)量:
> db.foo.insert({"x" : 2}) > db.foo.count() 2 > db.foo.count({"x" : 1}) 1
對(duì)分頁顯示來說總數(shù)非常必要:“共439個(gè),目前顯示0~10個(gè)”。但是,增加查詢條件會(huì)使count變慢。count可以使用索引,但是索引并沒有足夠的元數(shù)據(jù)供count使用,所以不如直接使用查詢來得快。
distinctdistinct用來找出給定鍵的所有不同值。使用時(shí)必須指定集合和鍵。
> db.runCommand({"distinct" : "people", "key" : "age"})
假設(shè)集合中有如下文檔:
{"name" : "Ada", "age" : 20} {"name" : "Fred", "age" : 35} {"name" : "Susan", "age" : 60} {"name" : "Andy", "age" : 35}
如果對(duì)"age"鍵使用distinct,會(huì)得到所有不同的年齡:
> db.runCommand({"distinct" : "people", "key" : "age"}) {"values" : [20, 35, 60], "ok" : 1}
這里還有一個(gè)常見問題:有沒有辦法獲得集合里面所有不同的鍵呢?MongoDB并沒有直接提供這樣的功能,但是可以用MapReduce(詳見7.3節(jié))自己寫一個(gè)。
group使用group可以執(zhí)行更復(fù)雜的聚合。先選定分組所依據(jù)的鍵,而后MongoDB就會(huì)將集合依據(jù)選定鍵的不同值分成若干組。然后可以對(duì)每一個(gè)分組內(nèi)的文檔進(jìn)行聚合,得到一個(gè)結(jié)果文檔。
如果你熟悉SQL,那么這個(gè)group和SQL中的GROUP BY差不多。
假設(shè)現(xiàn)在有個(gè)跟蹤股票價(jià)格的站點(diǎn)。從上午10點(diǎn)到下午4點(diǎn)每隔幾分鐘就會(huì)更新某只股票的價(jià)格,并保存在MongoDB中。現(xiàn)在報(bào)表程序要獲得近30天的收盤價(jià)。用group就可以輕松辦到。
股價(jià)集合中包含數(shù)以千計(jì)如下形式的文檔:
{"day" : "2010/10/03", "time" : "10/3/2010 03:57:01 GMT-400", "price" : 4.23} {"day" : "2010/10/04", "time" : "10/4/2010 11:28:39 GMT-400", "price" : 4.27} {"day" : "2010/10/03", "time" : "10/3/2010 05:00:23 GMT-400", "price" : 4.10} {"day" : "2010/10/06", "time" : "10/6/2010 05:27:58 GMT-400", "price" : 4.30} {"day" : "2010/10/04", "time" : "10/4/2010 08:34:50 GMT-400", "price" : 4.01}
注意,由于精度的問題,實(shí)際使用中不要將金額以浮點(diǎn)數(shù)的方式存儲(chǔ),這個(gè)例子只是為了簡(jiǎn)便才這么做。
我們需要的結(jié)果列表中應(yīng)該包含每天的最后交易時(shí)間和價(jià)格,就像下面這樣:
[ {"time" : "10/3/2010 05:00:23 GMT-400", "price" : 4.10}, {"time" : "10/4/2010 11:28:39 GMT-400", "price" : 4.27}, {"time" : "10/6/2010 05:27:58 GMT-400", "price" : 4.30} ]
先把集合按照"day"字段進(jìn)行分組,然后在每個(gè)分組中查找"time"值最大的文檔,將其添加到結(jié)果集中就完成了。整個(gè)過程如下所示:
> db.runCommand({"group" : { ... "ns" : "stocks", ... "key" : "day", ... "initial" : {"time" : 0}, ... "$reduce" : function(doc, prev) { ... if (doc.time > prev.time) { ... prev.price = doc.price; ... prev.time = doc.time; ... } ... }}})
把這個(gè)命令分解開看看。
"ns" : "stocks"
指定要進(jìn)行分組的集合。
"key" : "day"
指定文檔分組依據(jù)的鍵。這里就是"day"鍵。所有"day"值相同的文檔被分到一組。
"initial" : {"time" : 0}
每一組reduce函數(shù)調(diào)用中的初始"time"值,會(huì)作為初始文檔傳遞給后續(xù)過程。每一組的所有成員都會(huì)使用這個(gè)累加器,所以它的任何變化都可以保存下來。
"$reduce" : function(doc, prev) { ... }
這個(gè)函數(shù)會(huì)在集合內(nèi)的每個(gè)文檔上執(zhí)行。系統(tǒng)會(huì)傳遞兩個(gè)參數(shù):當(dāng)前文檔和累加器文檔(本組當(dāng)前的結(jié)果)。本例中,想讓reduce函數(shù)比較當(dāng)前文檔的時(shí)間和累加器的時(shí)間。如果當(dāng)前文檔的時(shí)間更晚一些,則將累加器的日期和價(jià)格替換為當(dāng)前文檔的值。別忘了,每一組都有一個(gè)獨(dú)立的累加器,所以不必?fù)?dān)心不同日期的命令會(huì)使用同一個(gè)累加器。
在問題一開始的描述中,就提到只要最近30天的股價(jià)。然而,我們?cè)谶@里迭代了整個(gè)集合。這就是要添加"condition"的原因,因?yàn)檫@樣就可以只對(duì)必要的文檔進(jìn)行處理。
> db.runCommand({"group" : { ... "ns" : "stocks", ... "key" : "day", ... "initial" : {"time" : 0}, ... "$reduce" : function(doc, prev) { ... if (doc.time > prev.time) { ... prev.price = doc.price; ... prev.time = doc.time; ... }}, ... "condition" : {"day" : {"$gt" : "2010/09/30"}} ... }})
有些參考資料提及"cond"鍵或者"q"鍵,其實(shí)和"condition"鍵是完全一樣的(就是表達(dá)力不如"condition"好)。
最后就會(huì)返回一個(gè)包含30個(gè)文檔的數(shù)組,其實(shí)每個(gè)文檔都是一個(gè)分組。每組都包含分組依據(jù)的鍵(這里就是"day" : string)以及這組最終的prev值。如果有的文檔不存在指定用于分組的鍵,這些文檔會(huì)被多帶帶分為一組,缺失的鍵會(huì)使用"day : null"這樣的形式。在"condition"中加入"day" : {"$exists" : true}就可以排除不包含指定用于分組的鍵的文檔。group命令同時(shí)返回了用到的文檔總數(shù)和"key"的不同值數(shù)量:
> db.runCommand({"group" : {...}}) { "retval" : [ { "day" : "2010/10/04", "time" : "Mon Oct 04 2010 11:28:39 GMT-0400 (EST)" "price" : 4.27 }, ... ], "count" : 734, "keys" : 30, "ok" : 1 }
這里每組的"price"都是顯式設(shè)置的,"time"先由初始化器設(shè)置,然后在迭代中進(jìn)行更新。"day"是默認(rèn)被加進(jìn)去的,因?yàn)橛糜诜纸M的鍵會(huì)默認(rèn)加入到每個(gè)"retval"內(nèi)嵌文檔中。要是不想在結(jié)果集中看到這個(gè)鍵,可以用完成器將累加器文檔變?yōu)槿魏蜗胍男螒B(tài),甚至變換成非文檔(例如數(shù)字或字符串)。
1. 使用完成器完成器(finalizer)用于精簡(jiǎn)從數(shù)據(jù)庫傳到用戶的數(shù)據(jù),這個(gè)步驟非常重要,因?yàn)間roup命令的輸出結(jié)果需要能夠通過單次數(shù)據(jù)庫響應(yīng)返回給用戶。為進(jìn)一步說明,這里舉個(gè)博客的例子,其中每篇文章都有多個(gè)標(biāo)簽(tag)。現(xiàn)在要找出每天最熱門的標(biāo)簽。可以(再一次)按天分組,得到每一個(gè)標(biāo)簽的計(jì)數(shù)。就像下面這樣:
> db.posts.group({ ... "key" : {"day" : true}, ... "initial" : {"tags" : {}}, ... "$reduce" : function(doc, prev) { ... for (i in doc.tags) { ... if (doc.tags[i] in prev.tags) { ... prev.tags[doc.tags[i]]++; ... } else { ... prev.tags[doc.tags[i]] = 1; ... } ... } ... }})
得到的結(jié)果如下所示:
[ {"day" : "2010/01/12", "tags" : {"nosql" : 4, "winter" : 10, "sledding" : 2}}, {"day" : "2010/01/13", "tags" : {"soda" : 5, "php" : 2}}, {"day" : "2010/01/14", "tags" : {"python" : 6, "winter" : 4, "nosql": 15}} ]
接著可以在客戶端找出"tags"文檔中出現(xiàn)次數(shù)最多的標(biāo)簽。然而,向客戶端發(fā)送每天所有的標(biāo)簽文檔需要許多額外的開銷——每天所有的鍵/值對(duì)都被傳送給用戶,而我們需要的僅僅是一個(gè)字符串。這也就是group有一個(gè)可選的"finalize"鍵的原因。"finalize"可以包含一個(gè)函數(shù),在每組結(jié)果傳遞到客戶端之前調(diào)用一次。可以使用"finalize"函數(shù)將不需要的內(nèi)容從結(jié)果集中移除:
> db.runCommand({"group" : { ... "ns" : "posts", ... "key" : {"day" : true}, ... "initial" : {"tags" : {}}, ... "$reduce" : function(doc, prev) { ... for (i in doc.tags) { ... if (doc.tags[i] in prev.tags) { ... prev.tags[doc.tags[i]]++; ... } else { ... prev.tags[doc.tags[i]] = 1; ... } ... }, ... "finalize" : function(prev) { ... var mostPopular = 0; ... for (i in prev.tags) { ... if (prev.tags[i] > mostPopular) { ... prev.tag = i; ... mostPopular = prev.tags[i]; ... } ... } ... delete prev.tags ... }}})
現(xiàn)在,我們就得到了想要的信息,服務(wù)器返回的內(nèi)容可能如下:
[ {"day" : "2010/01/12", "tag" : "winter"}, {"day" : "2010/01/13", "tag" : "soda"}, {"day" : "2010/01/14", "tag" : "nosql"} ]
finalize可以對(duì)傳遞進(jìn)來的參數(shù)進(jìn)行修改,也可以返回一個(gè)新值。
2. 將函數(shù)作為鍵使用有時(shí)分組所依據(jù)的條件可能會(huì)非常復(fù)雜,而不是單個(gè)鍵。比如要使用group計(jì)算每個(gè)類別有多少篇博客文章(每篇文章只屬于一個(gè)類別)。由于不同作者的風(fēng)格不同,填寫分類名稱時(shí)可能有人使用大寫也有人使用小寫。所以,如果要是按類別名來分組,最后“MongoDB”和“mongodb”就是兩個(gè)完全不同的組。為了消除這種大小寫的影響,就要定義一個(gè)函數(shù)來決定文檔分組所依據(jù)的鍵。
定義分組函數(shù)就要用到$keyf鍵(注意不是"key"),使用"$keyf"的group命令如下所示:
> db.posts.group({"ns" : "posts", ... "$keyf" : function(x) { return x.category.toLowerCase(); }, ... "initializer" : ... })
有了"$keyf",就能依據(jù)各種復(fù)雜的條件進(jìn)行分組了。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/44114.html
摘要:上一篇文章指南下一篇文章為在集合上執(zhí)行基本的聚合任務(wù)提供了一些命令。也可以給傳遞一個(gè)查詢文檔,會(huì)計(jì)算查詢結(jié)果的數(shù)量對(duì)分頁顯示來說總數(shù)非常必要共個(gè),目前顯示個(gè)。使用時(shí)必須指定集合和鍵。指定要進(jìn)行分組的集合。 上一篇文章:MongoDB指南---17、MapReduce下一篇文章: MongoDB為在集合上執(zhí)行基本的聚合任務(wù)提供了一些命令。這些命令在聚合框架出現(xiàn)之前就已經(jīng)存在了,現(xiàn)在(大多...
摘要:操作花費(fèi)的時(shí)間,單位是毫秒。處理完成后,會(huì)自動(dòng)將臨時(shí)集合的名字更改為你指定的集合名,這個(gè)重命名的過程是原子性的。作用域在這些函數(shù)內(nèi)部是不變的。上一篇文章指南聚合下一篇文章指南聚合命令 上一篇文章:MongoDB指南---16、聚合下一篇文章:MongoDB指南---18、聚合命令 MapReduce是聚合工具中的明星,它非常強(qiáng)大、非常靈活。有些問題過于復(fù)雜,無法使用聚合框架的查詢語言...
摘要:操作花費(fèi)的時(shí)間,單位是毫秒。處理完成后,會(huì)自動(dòng)將臨時(shí)集合的名字更改為你指定的集合名,這個(gè)重命名的過程是原子性的。作用域在這些函數(shù)內(nèi)部是不變的。上一篇文章指南聚合下一篇文章指南聚合命令 上一篇文章:MongoDB指南---16、聚合下一篇文章:MongoDB指南---18、聚合命令 MapReduce是聚合工具中的明星,它非常強(qiáng)大、非常靈活。有些問題過于復(fù)雜,無法使用聚合框架的查詢語言...
摘要:將返回結(jié)果限制為前個(gè)。所以,聚合的結(jié)果必須要限制在以內(nèi)支持的最大響應(yīng)消息大小。包含字段和排除字段的規(guī)則與常規(guī)查詢中的語法一致。改變字符大小寫的操作,只保證對(duì)羅馬字符有效。只對(duì)羅馬字符組成的字符串有效。 上一篇文章:MongoDB指南---15、特殊的索引和集合:地理空間索引、使用GridFS存儲(chǔ)文件下一篇文章:MongoDB指南---17、MapReduce 如果你有數(shù)據(jù)存儲(chǔ)在Mon...
摘要:將返回結(jié)果限制為前個(gè)。所以,聚合的結(jié)果必須要限制在以內(nèi)支持的最大響應(yīng)消息大小。包含字段和排除字段的規(guī)則與常規(guī)查詢中的語法一致。改變字符大小寫的操作,只保證對(duì)羅馬字符有效。只對(duì)羅馬字符組成的字符串有效。 上一篇文章:MongoDB指南---15、特殊的索引和集合:地理空間索引、使用GridFS存儲(chǔ)文件下一篇文章:MongoDB指南---17、MapReduce 如果你有數(shù)據(jù)存儲(chǔ)在Mon...
閱讀 2069·2023-04-25 22:58
閱讀 1415·2021-09-22 15:20
閱讀 2699·2019-08-30 15:56
閱讀 1995·2019-08-30 15:54
閱讀 2111·2019-08-29 12:31
閱讀 2734·2019-08-26 13:37
閱讀 597·2019-08-26 13:25
閱讀 2102·2019-08-26 11:58