MongoDB基礎(chǔ)之分組聚合函數(shù)講解

MongoDB 中主要有三個(gè)函數(shù):

  • group:分組統(tǒng)計(jì)
  • aggregate:簡(jiǎn)單聚合
  • mapReduce:強(qiáng)大統(tǒng)計(jì)

1 group

1.1 定義

MongoDB 中使用group來(lái)進(jìn)行分組聚合畔勤,語(yǔ)法如下:

db.collection.group(document);

document中信息:

{
    key:{key1:true,key2:true},
    cond:{},
    reduce:function(curr,result){},
    initial:{},
    finalize:function(curr,result){}
}

document中字段說(shuō)明:

  • key:分組字段腿倚,作為分組的key,等價(jià)于sql中g(shù)roup by a,b
  • cond:查詢條件
  • reduce:聚合函數(shù)
    一個(gè)聚合函數(shù)操作文檔的分組操作期間胚想。這些函數(shù)可以返回一個(gè)sum或count。
    該函數(shù)接受兩個(gè)參數(shù):當(dāng)前文檔和這個(gè)群體聚集的結(jié)果文檔
  • initial:初始化聚合結(jié)果文檔變量霸饲,為空時(shí)自動(dòng)為每列提供初始變量
  • finalize:統(tǒng)計(jì)一組后的回調(diào)函數(shù)
    db.collection.group()返回最終結(jié)果之前,此功能可以修改的結(jié)果文檔或替換的結(jié)果文檔作為一個(gè)整體

注意

  1. group需要我們手寫(xiě)聚合函數(shù)的業(yè)務(wù)邏輯
  2. group不支持shard cluster之众,無(wú)法分布式運(yùn)算
  3. 有些版本不支持group函數(shù)则奥,可以通過(guò)db.collection.help()函數(shù)查看

1.2 操作group

1.2.1 求數(shù)目

db.collection.group({
key:{cat_id:1},
cond:{},
reduce:function(curr,result) {
    result.cnt += 1;
},
initial:{cnt:0}
})

類似于sql:
select  count(*) from goods group by cat_id;

1.2.2 求總和

db.collection.group({
key:{cat_id:1},
cond:{},
reduce:function(curr,result) {
    result.num += curr.goods_number;
},
initial:{num:0}
})

類似于sql:
select  sum(goods_number) from goods group by cat_id;

1.2.3 求最大

db.collection.group({
key:{cat_id:1},
cond:{},
reduce:function(curr , result) {
    if(curr.shop_price > result.max) {
        result.max = curr.shop_price;
    }
},
initial:{max:0}
})

類似于sql:
select  max(shop_price) from goods group by cat_id;

1.2.4 求平均

求平均時(shí)主要用到了finalize函數(shù)

db.collection.group({
key:{cat_id:1},
cond:{},
reduce:function(curr , result) {
    result.cnt += 1;  //統(tǒng)計(jì)數(shù)量
    result.sum += curr.shop_price; //統(tǒng)計(jì)總量
},
initial:{sum:0,cnt:0},
finalize:function(result) {
    result.avg = result.sum/result.cnt;//求平均
}
})

2 aggregate

2.1 定義

MongoDB 中聚合(aggregate)主要用于處理數(shù)據(jù)(諸如統(tǒng)計(jì)平均值,求和等)拥坛,并返回計(jì)算后的數(shù)據(jù)結(jié)果

基本語(yǔ)法:

db.collection.aggregate(docment);

具體方法實(shí)例有:$match蓬蝶,$project,$group猜惋,$unwind丸氛,$sort,$limit著摔,$skip等相關(guān)操作

2.2 操作aggregate

準(zhǔn)備腳本

> db.book.insertMany([
{"_id": 1, "author": "monday", "book": "《Java》", "like": 10}, 
{"_id": 2, "author": "monday", "book": "《Java Core》", "like": 20}, 
{"_id": 3, "author": "mengday", "book": "《Spring Boot》", "like": 15}
])

> db.book.find()
{ "_id" : 1, "author" : "monday", "book" : "《Java》", "like" : 10 }
{ "_id" : 2, "author" : "monday", "book" : "《Java Core》", "like" : 20 }
{ "_id" : 3, "author" : "mengday", "book" : "《Spring Boot》", "like" : 15 }

2.2.1 $match篩選

用于過(guò)濾數(shù)據(jù)缓窜,只輸出符合條件的文檔。$match使用MongoDB的標(biāo)準(zhǔn)查詢操作谍咆。
篩選條件禾锤,相當(dāng)于SQL中的where部分,過(guò)濾掉不滿足條件的文檔,可以使用常規(guī)的查詢操作符卧波,如 $gt时肿、$lt$in等港粱。

> db.book.aggregate({"$match": {"like": {"$gt": 10}}})
{ "_id" : 2, "author" : "monday", "book" : "《Java Core》", "like" : 20 }
{ "_id" : 3, "author" : "mengday", "book" : "《Spring Boot》", "like" : 15 }

當(dāng)matchgroup之前類似于SQLwhere操作,在group之后類似于SQLhaving操作

db.book.aggregate([{"$match": {"like": {"$gte": 10}}},{$group:{_id:'$author',total:{$sum:1}}},{$match:{total:{$gte:2}}}])

{ "_id": "monday", "total": 2}

2.2.2 $project映射

$project:修改輸入文檔的結(jié)構(gòu)〔槠海可以用來(lái)重命名寸宏、增加或刪除域,也可以用于創(chuàng)建計(jì)算結(jié)果以及嵌套文檔偿曙。
映射相當(dāng)于SQL中的 select 映射 from之間的部分氮凝,用于指定要查詢的字段,或者對(duì)字段進(jìn)行一些處理望忆。
投射常用的3個(gè)功能:

  • 對(duì)字段重命名罩阵。
  • 在投射中使用一些表達(dá)式對(duì)字段值進(jìn)行處理:數(shù)學(xué)表達(dá)式、日期表達(dá)式启摄、字符串表達(dá)式稿壁、邏輯表達(dá)式(比較表達(dá)式、布爾表達(dá)式歉备、控制語(yǔ)句)傅是。
  • 用于包含、排除字段: 設(shè)置要查詢或者要過(guò)濾掉的字段蕾羊,
    • 0: 要過(guò)濾掉的字段喧笔,不顯示
    • 1:需要查詢的字段。

2.2.2.1 自定義字段

查詢自定義的字段

db.book.aggregate({"$project": {"_id": 0, "book": 1, "like": 1}})
{ "book" : "《Java》", "like" : 10 }
{ "book" : "《Java Core》", "like" : 20 }
{ "book" : "《Spring Boot》", "like" : 15 }

2.2.2.2 起別名

對(duì)字段起別名龟再,相當(dāng)于SQL中的as的作用

db.book.aggregate({"$project": {"_id": 0, "book name": "$book", "like": 1}})
{ "like" : 10, "book name" : "《Java》" }
{ "like" : 20, "book name" : "《Java Core》" }
{ "like" : 15, "book name" : "《Spring Boot》" }

2.2.2.3 引用原字段:$字段名

如果對(duì)_id進(jìn)行起別名會(huì)將別名作為一個(gè)新的字段加入到文檔中书闸,而_id保持不變

db.book.aggregate({"$project": {"id": "$_id", "book name": "$book", "like": 1}})
{ "_id" : 1, "like" : 10, "id" : 1, "book name" : "《Java》" }
{ "_id" : 2, "like" : 20, "id" : 2, "book name" : "《Java Core》" }
{ "_id" : 3, "like" : 15, "id" : 3, "book name" : "《Spring Boot》" }

2.2.2.4 使用算術(shù)表達(dá)式

使用算術(shù)表達(dá)式 $add$subtract利凑、$multiply梗劫、$divide$mod 處理數(shù)字類型的字段

db.book.aggregate({"$project": {"like": {"$add": ["$like", "$like", 1]}}})
{ "_id" : 1, "like" : 21 }
{ "_id" : 2, "like" : 41 }
{ "_id" : 3, "like" : 31 }

2.2.2.5 字符串截取

$substrCP: [exp, startOffset, numToReturn] : 字符串截取操作

db.book.aggregate({"$project": {"newValue": {"$substrCP": ["$book", 1, 4]}}})
{ "_id" : 1, "newValue" : "Java" }
{ "_id" : 2, "newValue" : "Java" }
{ "_id" : 3, "newValue" : "Spri" }

2.2.2.6 字符串拼接

$concat:[exp1, exp2, ..., expN]: 字符串操作:將數(shù)組中的多個(gè)元素拼接在一起

db.book.aggregate({"$project": {"newValue": {"$concat": ["$book", "(", "$author", ")"]}}})
{ "_id" : 1, "newValue" : "《Java》(monday)" }
{ "_id" : 2, "newValue" : "《Java Core》(monday)" }
{ "_id" : 3, "newValue" : "《Spring Boot》(mengday)" }

2.2.2.7 大小寫(xiě)轉(zhuǎn)換

$toUpper: 字符串操作截碴,轉(zhuǎn)大寫(xiě)
$toLower: exp, 字符串轉(zhuǎn)小寫(xiě)

db.book.aggregate({"$project": {"newValue": {"$toUpper": "$book"}}})
{ "_id" : 1, "newValue" : "《JAVA》" }
{ "_id" : 2, "newValue" : "《JAVA CORE》" }
{ "_id" : 3, "newValue" : "《SPRING BOOT》" }

2.2.2.8 日期表達(dá)式

日期表達(dá)式:用于獲取日期中的任意一部分梳侨,年月日時(shí)分秒 星期等
$year$month日丹、$dayOfMonth走哺、$dayOfWeek$dayOfYear哲虾、$hour丙躏、$minute$second

  • $dayOfYear: 返回該日期是這一年的第幾天(全年 366 天)束凑。
  • $dayOfMonth: 返回該日期是這一個(gè)月的第幾天(1到31)晒旅。
  • $dayOfWeek: 返回的是這個(gè)周的星期幾(1:星期日,7:星期六)汪诉。
  • $year: 返回該日期的年份部分废恋。
  • $month: 返回該日期的月份部分( 1 到 12)谈秫。
  • $week: 返回該日期是所在年的第幾個(gè)星期( 0 到 53)。
  • $hour: 返回該日期的小時(shí)部分鱼鼓。
  • $minute: 返回該日期的分鐘部分拟烫。
  • $second: 返回該日期的秒部分(以0到59之間的數(shù)字形式返回日期的第二部分,但可以是60來(lái)計(jì)算閏秒)迄本。
  • $millisecond:返回該日期的毫秒部分( 0 到 999)硕淑。
  • $dateToString: { $dateToString: { format: , date: } }。

準(zhǔn)備腳本

db.book.find()
{ "_id" : 1, "author" : "monday", "book" : "《Java》", "like" : 10, "publishDate" : ISODate("2021-04-12T13:38:14.829Z") }
{ "_id" : 2, "author" : "monday", "book" : "《Java Core》", "like" : 20, "publishDate" : ISODate("2021-04-12T13:38:14.829Z") }
{ "_id" : 3, "author" : "mengday", "book" : "《Spring Boot》", "like" : 15, "publishDate" : ISODate("2021-04-12T13:38:14.829Z") }

求日期

db.book.aggregate({"$project": {"year": {"$year": "$publishDate"}}})
{ "_id" : 1, "year" : 2021 }
{ "_id" : 2, "year" : 2021 }
{ "_id" : 3, "year" : 2021 }

2.2.2.9 比較非字符串大小

$cmp: [exp1, exp2]: 用于比較兩個(gè)非字符串類型的值嘉赎,exp1 == exp2 返回 0置媳, 小于返回一個(gè)負(fù)數(shù),大于返回一個(gè)正數(shù)

db.book.aggregate({"$project": {"result": {"$cmp": ["$like", 15]}}})
{ "_id" : 1, "result" : -1 }
{ "_id" : 2, "result" : 1 }
{ "_id" : 3, "result" : 0 }

2.2.2.10 比較字符串大小

$strcasecmp: 用于比較字符串, 不區(qū)分大小寫(xiě)公条,相等返回0

db.book.aggregate({"$project": {"result": {"$strcasecmp": ["$author", "Monday"]}}})
{ "_id" : 1, "result" : 0 }
{ "_id" : 2, "result" : 0 }
{ "_id" : 3, "result" : -1 }

2.2.2.11 比較表達(dá)式

$eq: 用于判斷兩個(gè)表達(dá)式是否相等拇囊,相等返回true,不相等返回false, 區(qū)分大小寫(xiě)
$ne: 不相等
$gt: 大于
$gte: 大于等于
$lt: 小于
$lte: 小于等于
注意:{"$eq": ["$author", "monday"]}$eq鍵的值是個(gè)數(shù)組而不是一個(gè)對(duì)象

db.book.aggregate({"$project": {"result": {"$eq": ["$author", "monday"]}}})
{ "_id" : 1, "result" : true }
{ "_id" : 2, "result" : true }
{ "_id" : 3, "result" : false }

2.2.2.12 And連接

$and:[exp1, exp2, ..., expN]用于連接多個(gè)條件赃份,當(dāng)所有條件為真的時(shí)候?yàn)?code>true

db.book.aggregate({"$project": {"result": {"$and": [{"$eq": ["$author", "monday"]}, {"$gt": ["$_id", 1]}]}}})
{ "_id" : 1, "result" : false }
{ "_id" : 2, "result" : true }
{ "_id" : 3, "result" : false }

2.2.2.13 Or連接

$or: [exp1, exp2, ..., expN]有一個(gè)為真則為真

db.book.aggregate({"$project": {"result": {"$or": [{"$eq": ["$author", "monday"]}, {"$eq": ["$_id", 1]}]}}})
{ "_id" : 1, "result" : true }
{ "_id" : 2, "result" : true }
{ "_id" : 3, "result" : false }

2.2.2.14 not連接

$not: exp 用于取反操作

db.book.aggregate({"$project": {"result": {"$not": {"$eq": ["$author", "monday"]}}}})
{ "_id" : 1, "result" : false }
{ "_id" : 2, "result" : false }
{ "_id" : 3, "result" : true }

2.2.2.15 三位運(yùn)算符

$cond: [booleanExp, trueExp, falseExp]: 三位運(yùn)算符

db.book.aggregate({"$project": {"result": {"$cond": [ {"$eq": ["$author", "monday"]}, "M", "F" ]}}})
{ "_id" : 1, "result" : "M" }
{ "_id" : 2, "result" : "M" }
{ "_id" : 3, "result" : "F" }

2.2.2.16 ifNull連接

$ifNull: [expr, replacementExpr]: 如果字段不存在或者字段值為null會(huì)返回replacementExpr寂拆,否則返回原來(lái)的值,即字段為空時(shí)給一個(gè)默認(rèn)值
IFNULL(bool, default_value)

db.book.aggregate({"$project": {"result": {"$ifNull": ["$price", "0.00"]}}})
{ "_id" : 1, "result" : 66 }
{ "_id" : 2, "result" : "0.00" }
{ "_id" : 3, "result" : "0.00" }

2.2.3 $group 分組

2.2.3.1 單字段分組

相當(dāng)于SQL中的group by 部分
$group 表示分組
_id: 用于指定要分組的字段
count: 是聚合后的結(jié)果的字段別名抓韩,類似于SQL中的as后面的別名纠永,可以任意定義,字段前面使用$表示應(yīng)用某個(gè)字段而不是一個(gè)普通的字符串
$sum: 對(duì)分組中的每個(gè)文檔做什么類型的聚合谒拴,是求和還是求平均數(shù)等尝江,1:表示對(duì)分組的每一條文檔都加1進(jìn)行統(tǒng)計(jì),這就相當(dāng)于SQL中的count(*)
select _id, count(*) from book group by author

db.book.aggregate({"$group": {"_id": "$author", "count": {"$sum": 1}}})
{ "_id" : "monday", "count" : 2 }
{ "_id" : "mengday", "count" : 1 }

2.2.3.2 對(duì)多個(gè)字段進(jìn)行分組

類似于:select _id, count(*) from book group by author, like

db.book.aggregate({"$group": {"_id": {"author": "$author", "like": "$like"}, "count": {"$sum": 1}}})
{ "_id" : { "author" : "monday", "like" : 10 }, "count" : 1 }
{ "_id" : { "author" : "monday", "like" : 20 }, "count" : 1 }
{ "_id" : { "author" : "mengday", "like" : 15 }, "count" : 1 }

2.2.3.3 聚合運(yùn)算

$avg: 求分組中某個(gè)字段的平均值
$max: 求分組中某個(gè)字段的最大值
$min: 求分組中某個(gè)字段最小的值
$first: 求分組中的第一個(gè)值
$last: 求分組中最后一個(gè)值

db.book.aggregate( {"$group": {"_id": "$author", "avg": {"$avg": "$like"}}} )
{ "_id" : "monday", "avg" : 15 }
{ "_id" : "mengday", "avg" : 15 }

2.2.3.4 addToSet

$addToSet: 字段引用, 將分組后的每個(gè)文檔指定的值放在set集合中英上,集合不重復(fù)炭序,無(wú)序

db.book.aggregate( {"$group": {"_id": "$author", "likes": {"$addToSet": "$like"}}} )
{ "_id" : "monday", "likes" : [ 20, 10 ] }
{ "_id" : "mengday", "likes" : [ 15 ] }

2.2.3.5 push操作

$push: exp, 將分組后的每個(gè)文檔指定的值放在數(shù)組中,允許重復(fù)苍日,有序

db.book.aggregate( {"$group": {"_id": "$author", "likes": {"$push": "$like"}}} )
{ "_id" : "mengday", "likes" : [ 15 ] }
{ "_id" : "monday", "likes" : [ 10, 20 ] }

2.2.4 $unwind

將數(shù)組的每一個(gè)元素都單獨(dú)作為一條文檔進(jìn)行拆分惭聂。

 db.comments.insert({"_id": 1, "title": "java", "comment": ["good", "very good"]})
db.comments.find()
{ "_id" : 1, "title" : "java", "comment" : [ "good", "very good" ] }

db.comments.aggregate( {"$unwind": "$comment"} )
{ "_id" : 1, "title" : "java", "comment" : "good" }
{ "_id" : 1, "title" : "java", "comment" : "very good" }

2.2.5 $sort

對(duì)文檔進(jìn)行排序,相當(dāng)于SQL中的order by相恃。

db.book.aggregate( {"$sort": {"_id": -1}} )
{ "_id" : 3, "author" : "mengday", "book" : "《Spring Boot》", "like" : 15, "publishDate" : ISODate("2021-04-12T13:38:14.829Z") }
{ "_id" : 2, "author" : "monday", "book" : "《Java Core》", "like" : 20, "publishDate" : ISODate("2021-04-12T13:38:14.829Z"), "price" : null }
{ "_id" : 1, "author" : "monday", "book" : "《Java》", "like" : 10, "publishDate" : ISODate("2021-04-12T13:38:14.829Z"), "price" : 66 }

2.2.6 $limit

限制返回的條數(shù)辜纲,相當(dāng)于SQL中的limit count 語(yǔ)句。

db.book.aggregate( {"$limit": 2} )
{ "_id" : 1, "author" : "monday", "book" : "《Java》", "like" : 10, "publishDate" : ISODate("2021-04-12T13:38:14.829Z"), "price" : 66 }
{ "_id" : 2, "author" : "monday", "book" : "《Java Core》", "like" : 20, "publishDate" : ISODate("2021-04-12T13:38:14.829Z"), "price" : null }

2.2.7 $skip

跳過(guò)前N條文檔拦耐,和limit結(jié)合可用于分頁(yè)耕腾。

db.book.aggregate( {"$skip": 2}, {"$limit": 2} )
{ "_id" : 3, "author" : "mengday", "book" : "《Spring Boot》", "like" : 15, "publishDate" : ISODate("2021-04-12T13:38:14.829Z") }

2.2.8 綜合使用

聚合框架,就是將上一個(gè)操作符處理的結(jié)果交個(gè)下一個(gè)操作符繼續(xù)處理(這就是Linux中的管道操作)杀糯,可以使用任意多個(gè)操作符扫俺,同一個(gè)操作符也可以使用多次

  • 首先通過(guò)$match過(guò)濾掉不匹配的文檔,
  • 接著講滿足條件的文檔交給$group進(jìn)行分組固翰,
  • 分組后將將分組后的結(jié)果交給$sort進(jìn)行排序狼纬,
  • 然后將排序后的結(jié)果交給$skip處理羹呵,跳過(guò)前幾條,
  • 把剩下的文檔交給$limit處理畸颅,獲取最終的聚合結(jié)果担巩。
db.book.aggregate( 
    {"$match": {"like": {"$gte" : 10} }}, 
    {"$group": {"_id": "$author", "count": {"$sum": 1}}},
    {"$sort": {"count": -1}},
    {"$skip": 1},
    {"$limit": 1} 
)

{ "_id" : "mengday", "count" : 1 }

2.3 常見(jiàn)聚合表達(dá)式

下表展示了一些聚合的計(jì)算表達(dá)式:

表達(dá)式 描述 實(shí)例
$sum 計(jì)算總和 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$sum : "$likes"}}}])
$avg 計(jì)算平均值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$avg : "$likes"}}}])
$min 獲取集合中所有文檔對(duì)應(yīng)值得最小值 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$min : "$likes"}}}])
$max 獲取集合中所有文檔對(duì)應(yīng)值得最大值方援。 db.mycol.aggregate([{$group : {_id : "$by_user", num_tutorial : {$max : "$likes"}}}])
$push 將值加入一個(gè)數(shù)組中没炒,不會(huì)判斷是否有重復(fù)的值 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$push: "$url"}}}])
$addToSet 將值加入一個(gè)數(shù)組中,會(huì)判斷是否有重復(fù)的值犯戏,若相同的值在數(shù)組中已經(jīng)存在了送火,則不加入。 db.mycol.aggregate([{$group : {_id : "$by_user", url : {$addToSet : "$url"}}}])
$first 根據(jù)資源文檔的排序獲取第一個(gè)文檔數(shù)據(jù)先匪。 db.mycol.aggregate([{$group : {_id : "$by_user", first_url : {$first : "$url"}}}])
$last 根據(jù)資源文檔的排序獲取最后一個(gè)文檔數(shù)據(jù) db.mycol.aggregate([{$group : {_id : "$by_user", last_url : {$last : "$url"}}}])

3 mapReduce

Map-Reduce是一種計(jì)算模型种吸,簡(jiǎn)單的說(shuō)就是將大批量的工作(數(shù)據(jù))分解(MAP)執(zhí)行,然后再將結(jié)果合并成最終結(jié)果(REDUCE)呀非。
MongoDB提供的Map-Reduce非常靈活坚俗,對(duì)于大規(guī)模數(shù)據(jù)分析也相當(dāng)實(shí)用。

3.1 mapReduce語(yǔ)法

以下是MapReduce的基本語(yǔ)法:

db.collection.mapReduce(
   function() {emit(key,value);},  //map 函數(shù)
   function(key,values) {return reduceFunction},   //reduce 函數(shù)
   {
      out: collection,
      query: document,
      sort: document,
      limit: number
   }
)

使用 MapReduce 要實(shí)現(xiàn)兩個(gè)函數(shù)Map 函數(shù)和 Reduce 函數(shù),Map 函數(shù)調(diào)用 emit(key, value), 遍歷 collection 中所有的記錄, 將 key 與 value 傳遞給 Reduce 函數(shù)進(jìn)行處理岸裙。
Map 函數(shù)必須調(diào)用 emit(key, value)返回鍵值對(duì)猖败。

參數(shù)說(shuō)明:

  • map :映射函數(shù) (生成鍵值對(duì)序列,作為 reduce 函數(shù)參數(shù))
    把屬于同一個(gè)組的數(shù)據(jù),映射到一個(gè)數(shù)組上
  • reduce:統(tǒng)計(jì)函數(shù)降允,把數(shù)組(同一組)的數(shù)據(jù)恩闻,進(jìn)行運(yùn)算
    reduce函數(shù)的任務(wù)就是將key-values變成key-value,也就是把values數(shù)組變成一個(gè)單一的值value
  • out:統(tǒng)計(jì)結(jié)果存放集合 (不指定則使用臨時(shí)集合,在客戶端斷開(kāi)后自動(dòng)刪除)剧董。
  • query:一個(gè)篩選條件幢尚,只有滿足條件的文檔才會(huì)調(diào)用map函數(shù)。(query翅楼。limit尉剩,sort可以隨意組合)
  • sortlimit結(jié)合的sort排序參數(shù)(也是在發(fā)往map函數(shù)前給文檔排序),可以優(yōu)化分組機(jī)制
  • limit 發(fā)往map函數(shù)的文檔數(shù)量的上限(要是沒(méi)有l(wèi)imit毅臊,單獨(dú)使用sort的用處不大)

以下實(shí)例在集合 orders 中查找 status:"A" 的數(shù)據(jù)理茎,并根據(jù) cust_id 來(lái)分組,并計(jì)算 amount 的總和褂微。


image.png

3.2 與分組聚合對(duì)比

Aggregate中引用集合中字段區(qū)別:

  • 在分組聚合Aggregate函數(shù)中功蜓,引用原字段用$原字段
  • MapReduce中引用原來(lái)字段用this,比如:this.age

3.3 使用 mapReduce

準(zhǔn)備腳本

db.user.insertMany([
{"name" : "魯迅","book" : "吶喊","price" : 38.0,"publisher" : "人民文學(xué)出版社"},
{"name" : "曹雪芹","book" : "紅樓夢(mèng)","price" : 22.0,"publisher" : "人民文學(xué)出版社"},
{"name" : "錢鐘書(shū)","book" : "宋詩(shī)選注","price" : 99.0,"publisher" : "人民文學(xué)出版社"},
{"name" : "錢鐘書(shū)","book" : "談藝錄","price" : 66.0,"publisher" : "三聯(lián)書(shū)店"},
{"name" : "魯迅","book" : "彷徨","price" : 55.0,"publisher" : "花城出版社"}
]);

查詢每個(gè)作者的總計(jì)價(jià)格

db.user.mapReduce(
function(){emit(this.name,this.price)},
function(key,value){return Array.sum(value)},
{out:"totalPrice"});

查看處理結(jié)果

db.totalPrice.find();
{
    "_id": "魯迅",
    "value": 22
}
// 2
{
    "_id": "錢鐘書(shū)",
    "value": 93
}
// 3
{
    "_id": "曹雪芹",
    "value": 165
}

或者使用引用的方法(查詢每個(gè)價(jià)格在 40以上的書(shū)宠蚂,并用逗號(hào)分隔)

所有函數(shù)寫(xiě)在一起
db.user.mapReduce(
function(){emit(this.name,this.book)},
function(key,value){return value.join(',')},
{query:{price:{$gt:40}},out:"books"})
db.books.find();

單獨(dú)寫(xiě)式撼,然后引用
var map=function(){emit(this.name,this.book)}
var reduce=function(key,value){return value.join(',')}
var options={query:{price:{$gt:40}},out:"books"}
db.user.mapReduce(map,reduce,options);

3.4 runCommand

3.4.1 語(yǔ)法

db.runCommand(
               {
                 mapReduce: <collection>,
                 map: <function>,
                 reduce: <function>,
                 finalize: <function>,
                 out: <output>,
                 query: <document>,
                 sort: <document>,
                 limit: <number>,
                 scope: <document>,
                 jsMode: <boolean>,
                 verbose: <boolean>,
                 bypassDocumentValidation: <boolean>,
                 collation: <document>
               }
             )

參數(shù)含義:

  • mapReduce:表示要操作的集合
  • map:map函數(shù)
  • reduce:reduce函數(shù)
  • finalize:最終處理函數(shù)
  • out:輸出的集合
  • query:對(duì)結(jié)果進(jìn)行過(guò)濾
  • sort:對(duì)結(jié)果排序
  • limit:返回的結(jié)果數(shù)
  • scope:設(shè)置參數(shù)值,在這里設(shè)置的值在map求厕,reduce著隆,finalize函數(shù)中可見(jiàn)
  • jsMode:是否將地圖執(zhí)行的中間數(shù)據(jù)由javascript對(duì)象轉(zhuǎn)換成BSON對(duì)象扰楼,替換為false
  • verbose:是否顯示詳細(xì)的時(shí)間統(tǒng)計(jì)信息
  • bypassDocumentValidation:是否繞過(guò)文檔驗(yàn)證
  • collation:其他一些校對(duì)

3.4.2 案例

如下操作,表示執(zhí)行MapReduce操作重新統(tǒng)計(jì)的集合限制返回條數(shù)美浦,限制返回條數(shù)之后再進(jìn)行統(tǒng)計(jì)操作弦赖,如下:

var map=function(){emit(this.name,this.book)}
var reduce=function(key,value){return value.join(',')}
db.runCommand({mapreduce:'user',map,reduce,out:"books",limit:4,verbose:true})

db.books.find()
執(zhí)行結(jié)果:
{ "_id" : "魯迅", "value" : "吶喊" }
{ "_id" : "曹雪芹", "value" : "紅樓夢(mèng)" }
{ "_id" : "錢鐘書(shū)", "value" : "談藝錄,宋詩(shī)選注" }

這里進(jìn)行對(duì)比發(fā)現(xiàn),因?yàn)閘imit的原因浦辨,魯迅的第一本書(shū)不見(jiàn)了

finalize操作表示最終處理函數(shù)蹬竖,如下:f1函數(shù)的第一個(gè)參數(shù)鍵表示emit中的第一個(gè)參數(shù),第二個(gè)參數(shù)表示reduce的執(zhí)行結(jié)果流酬,我們可以在f1中對(duì)這個(gè)結(jié)果進(jìn)行再處理

var f1 = function(key,reduceValue){var obj={};obj.author=key;obj.books=reduceValue; return obj}
var map=function(){emit(this.name,this.book)}
var reduce=function(key,value){return value.join(',')}
db.runCommand({mapreduce:'user',map,reduce,out:"books",finalize:f1})


db.books.find()
執(zhí)行結(jié)果:

{ "_id" : "魯迅", "value" : { "author" : "魯迅", "books" : "彷徨,吶喊" } }
{ "_id" : "曹雪芹", "value" : { "author" : "曹雪芹", "books" : "紅樓夢(mèng)" } }
{ "_id" : "錢鐘書(shū)", "value" : { "author" : "錢鐘書(shū)", "books" : "談藝錄,宋詩(shī)選注" } }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末币厕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子芽腾,更是在濱河造成了極大的恐慌旦装,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件摊滔,死亡現(xiàn)場(chǎng)離奇詭異阴绢,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)艰躺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評(píng)論 2 381
  • 文/潘曉璐 我一進(jìn)店門呻袭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人描滔,你說(shuō)我怎么就攤上這事棒妨。” “怎么了含长?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,340評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵券腔,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我拘泞,道長(zhǎng)纷纫,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,449評(píng)論 1 279
  • 正文 為了忘掉前任陪腌,我火速辦了婚禮辱魁,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘诗鸭。我一直安慰自己染簇,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,445評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布强岸。 她就那樣靜靜地躺著锻弓,像睡著了一般。 火紅的嫁衣襯著肌膚如雪蝌箍。 梳的紋絲不亂的頭發(fā)上青灼,一...
    開(kāi)封第一講書(shū)人閱讀 49,166評(píng)論 1 284
  • 那天暴心,我揣著相機(jī)與錄音,去河邊找鬼杂拨。 笑死专普,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弹沽。 我是一名探鬼主播檀夹,決...
    沈念sama閱讀 38,442評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼贷币!你這毒婦竟也來(lái)了击胜?” 一聲冷哼從身側(cè)響起亏狰,我...
    開(kāi)封第一講書(shū)人閱讀 37,105評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤役纹,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后暇唾,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體促脉,經(jīng)...
    沈念sama閱讀 43,601評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,066評(píng)論 2 325
  • 正文 我和宋清朗相戀三年策州,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了瘸味。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,161評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡够挂,死狀恐怖旁仿,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情孽糖,我是刑警寧澤枯冈,帶...
    沈念sama閱讀 33,792評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站办悟,受9級(jí)特大地震影響尘奏,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜病蛉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,351評(píng)論 3 307
  • 文/蒙蒙 一炫加、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧铺然,春花似錦俗孝、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,352評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至诀艰,卻和暖如春柬甥,著一層夾襖步出監(jiān)牢的瞬間饮六,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,584評(píng)論 1 261
  • 我被黑心中介騙來(lái)泰國(guó)打工苛蒲, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卤橄,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,618評(píng)論 2 355
  • 正文 我出身青樓臂外,卻偏偏與公主長(zhǎng)得像窟扑,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子漏健,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,916評(píng)論 2 344

推薦閱讀更多精彩內(nèi)容