麻豆小视频在线观看_中文黄色一级片_久久久成人精品_成片免费观看视频大全_午夜精品久久久久久久99热浪潮_成人一区二区三区四区

首頁 > 數據庫 > MongoDB > 正文

MongoDB的聚合框架Aggregation Framework入門學習教程

2020-10-29 18:49:12
字體:
來源:轉載
供稿:網友

1. 聚合框架
使用聚合框架對集合中的文檔進行變換和組合,可以用多個構件創建一個管道(pipeline),用于對一連串的文檔進行處理。這些構件包括篩選(filtering),投射(projecting),分組(grouping),排序(sorting),限制(limiting),跳過(skipping)。
例如一個保存著動物類型的集合,希望找出最多的那種動物,假設每種動物被保存為一個mongodb文檔,可以按照以下步驟創建管道。
1)將每個文檔的動物名稱映射出來。
2)安裝名稱排序,統計每個名稱出現的次數。
3)將文檔按照名稱出現的次數降序排列。
4)將返回結果限制為前五個。
具體操作符:
1){"$porject", {"name" : 1}}
類似于查詢階段的字段選擇器,指定"fieldname" : 1選定需要的字段,"fieldname" : 0排除不需要的字段,"_id"字段自動顯示。結果保存在內存中,不會寫入磁盤。

db.test_collection.aggregate({"$project" : {"name" : 1}});    =>{ "_id" : ObjectId("535a2d3c169097010b92fdf6"), "name" : "snake" }

2){"$group", {"_id" : "$name", "count" : {"$sum" : 1}}}
首先指定了分組的字段"name",該操作執行完后,每個name只對應一個結果,所有可以將name指定為唯一標識符"_id"。
第二個字段表明分組內的每個文檔"count"字段加1。新加入的文檔中不會有count字段。

db.test_collection.aggregate({"$project" : {"name" : 1}}, {"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}});    =>{ "_id" : "bird", "count" : 8344 }{ "_id" : "snake", "count" : 8443 }{ "_id" : "cat", "count" : 8183 }{ "_id" : "rabbit", "count" : 8206 }{ "_id" : "tiger", "count" : 8329 }{ "_id" : "cow", "count" : 8309 }{ "_id" : "horse", "count" : 8379 }{ "_id" : "dog", "count" : 8406 }{ "_id" : "dragon", "count" : 8372 }{ "_id" : "elephant", "count" : 8264 }{ "_id" : "pig", "count" : 8403 }{ "_id" : "lion", "count" : 8362 }

3){"$sort" : {"count" : -1}}
對結果集中的文檔根據count字段做降序排列。
4){"$limit" : 5}
將返回結果限制為5個文檔。
將上述結果綜合起來:

db.test_collection.aggregate({  "$project" : {"name" : 1}},   {"$group" : {"_id" : "$name", "count" : {"$sum" : 1}}},   {"$sort" : {"count" : -1}},  {"$limit" : 5});

aggregate會返回一個文檔數組,內容為出現次數最多的5個動物:

{ "_id" : "snake", "count" : 8443 }{ "_id" : "dog", "count" : 8406 }{ "_id" : "pig", "count" : 8403 }{ "_id" : "horse", "count" : 8379 }{ "_id" : "dragon", "count" : 8372 }

調試過程中。可以逐一對管道符進行排查。
聚合框架不能對集合進行寫入操作,所有結果返回給客戶端,聚合結果必須限制在16M以內。

2. 管道操作符
每個操作符都會接受一連串的文檔,對這些文檔進行類型轉換,最后得到的文檔作為結果傳遞給下一操作符。
不同的管道操作符可以將任意順序組合在一起使用,而且可以被重復任意多次。

2.1 $match
$match用于對文檔集合進行篩選,之后得到的文檔子集做聚合。
"$match"支持所有的常規查詢操作符("$gt","$lt","$ne")等,不能使用地理空間操作符。
實際操作中盡量將"$match"放在管道的前面部分,一方面可以提快速將不需要的文檔過濾掉,另外在映射和分組前篩選,查詢可以使用索引。

2.2 $project
使用"$project"可以提取字段,可以重命名字段,

db.foo.aggregate({"$project" : {"city" : 1, "_id" : 0}})    =>{ "city" : "NEW WORK" }

可以將投射過的字段重命名:

db.foo.aggregate({"$project" : {"newcity" : "$city", "_id" : 0}})    =>{ "newcity" : "NEW WORK" }

使用"$fieldname"語法為了在聚合框架中引用fieldname字段,例如上面"$city"會被替換為"NEW WORK"。
對字段重命名后,Mongdb不會記錄其記錄字段的歷史名稱,所以應該在修改字段名稱前使用索引。
2.2.1 管道表達式
可以使用表達式將多個字面量和變量組合為一個值。
可以使用組合或者任意深度的嵌套,創建復雜的表達式。
2.2.2 數學表達式
數學表示式用來操作數據運算。

db.foo.aggregate(  {"$project" :    {"total" :       {"$add" : ["$age", "$year"]},      "_id" : 0    }  }){"total" : 15}

可以將多個表達式組合為更為復雜的表達式:

db.foo.aggregate(  {"$project" :    {"sub" :      {"$subtract" : [{"$add" : ["$age", "$year"]}, 7]},       "_id" : 0    }  }){ "sub" : 8 }

操作符語法:
1)"$add" : [expr1, [, expr2, ..., exprN]]
將表達式相加
2)"$subtract" : [expr1, expr2]
表達式1減去表達式2
3)"$multiply" : [expr1, [, expr2, ..., exprN]]
將表達式相乘
4)"$divide" : [expr1, expr2]
表達式1除以表達式2得到商
5)"$mod" : [expr1, expr2]
表達式1除以表達式2得到余數

2.2.3 日期表達式
用于提取日期信息的表達式:"$year","$month","$week","$dayOfMonth","$dayOfweek","$hour","$minute","$second"。只能對日期類型的字段進行日期操作,不能對數值類型進行日期操作。

db.bar.insert({"name" : "pipi", "date" : new Date()})db.bar.aggregate(  {"$project" :     {"birth-month" :       {"$month" : "$date"},      "_id" : 0    }  }){ "birth-month" : 4 }

也可以使用字面量日期。

db.bar.aggregate(  {"$project" :     {"up-to-now" :       {"$subtract" : [{"$minute" : new Date()}, {"$minute" : "$date"}]},      "_id" : 0    }  }){ "up-to-now" : 18 }

2.2.3 字符串表達式
操作符語法:
1)"$substr" : [expr, startOffset, numoReturn]
接受字符串,起始位置以后偏移N個字節,截取字符串。
2)"$concat" : [expr1[, expr2, ..., exprN]]
將給定的表達式連接在一起作為返回結果。
3)"$toLower" : expr
返回參數的小寫形式
4)"$toUpper" : expr
返回參數的大寫形式
例如:

db.foo.insert({"firstname" : "caoqing", "lastname" : "lucifer"})db.foo.aggregate({  "$project" : {    "email" : {      "$concat" : [        {"$substr" : ["$firstname", 0, 1]},        ".",        "$lastname",        "@gmail.com"          ]        },        "_id" : 0      }  }){ "email" : "[email protected]" }

2.2.3 邏輯表達式
操作符語法:
1)"$cmp" : [expr1, expr2]
比較兩個參數,相等返回0,大于返回整數,小于返回負數。
2)"$strcasecmp" : [string1, string2]
比較字符串,區分大小寫
3)"$eq"/"$ne"/"$gt"/"$gte"/"lt"/"lte" : [expr1, expr2]
比較字符串,返回結果(true or false)
4)"$and" : [expr1[, expr2, ..., exprN]]
所有值為true返回true,否則返回false。
5)"$or" : [expr1[, expr2, ..., exprN]]
任意表達式為true返回true,否則返回false
6)"$not" : expr
對表示式取反
還有兩個控制語句。

"$crond" : [booleanExpr, trueExpr, falseExpr]

如果為true,返回trueExpr,否則,返回falseExpr。

"$ifFull" : [expr, replacementExpr]

如果expr為null,返回replacementExpr,否則返回expr。
算術操作符必須接受數值,日期操作符必須接受日期,字符串操作符必須接受字符串。
例如,根據學生出勤率(10%),平時作業(30%)和考試成績(60%)得出最終成績,如果是老師寵愛的學生,直接得100分:
插入數據:

db.bar.insert(  {    "name" : "xiaobao",    "teachersPet" : 1,    "attendance" : 90,    "quizz" : 80,    "test" : 85  })db.bar.insert(  {    "name" : "caoqing",    "teachersPet" : 0,    "attendance" : 20,    "quizz" : 50,    "test" : 90  })db.bar.insert(  {    "name" : "pipi",    "teachersPet" : 0,    "attendance" : 100,    "quizz" : 50,    "test" : 10  })

聚合:

db.bar.aggregate(  {    "$project" : {      "grade" : {        "$cond" : [          "$teachersPet",          100,        {          "$add" : [            {"$multiply" : [0.1, "$attendance"]},            {"$multiply" : [0.3, "$quizz"]},            {"$multiply" : [0.6, "$test"]},          ]        }        ]      },       "_id" : 0    }  })

返回結果:

{ "grade" : 100 }{ "grade" : 71 }{ "grade" : 31 }

3. MapReduce
Mapreduce非常強大與靈活,Mongodb使用javascript作為查詢語言,可以表示任意復雜的邏輯。
Mapreduce非常慢,不應該用在實際的數據分析中。
Mapreduce可以在多臺服務器之間并行執行,可以將一個問題拆分為多個小問題,之后將各個小問題發送到不同的機器上,每臺機器只負責完成一部分工作,所有的機器完成時,將這些零碎的解決方案合并為一個完整的解決方案。
最開始的是映射(map),將操作映射到集合中的各個文檔,然后是中間環節,成為洗牌(shuffle),按照鍵分組,將產生的鍵值組成列表放在對應的鍵中。化簡(reduce)則是把列表中的值化簡為一個單值。

3.1 找出集合中的所有鍵
MongoDB假設你的模式是動態的,所以并不會跟蹤記錄每個文檔的鍵。通常找到集合中所有文檔的所有鍵的最好方式就是MapReduce。
在映射環節,map函數使用特別的emit函數返回要處理的值。emit會給MapReduce一個鍵和一個值。
這里用emit將文檔某個鍵的計數返回。this就是當前映射文檔的引用:

map = function() {  emit(this.country, {count : 1});}

reduce接受兩個參數,一個是key,就是emit返回的第一個值,還有一個數組,由一個或多個鍵對應的{count : 1}文檔組成。

reduce = function(key, value) {  var result = {count : 0};  for (var i = 0; i < value.length; i++) {    result.count += value[i].count;  }  return result;}

示例表數據:

{ "_id" : 38, "country" : "japan", "money" : 724 }{ "_id" : 39, "country" : "germany", "money" : 520 }{ "_id" : 40, "country" : "india", "money" : 934 }{ "_id" : 41, "country" : "china", "money" : 721 }{ "_id" : 42, "country" : "germany", "money" : 156 }{ "_id" : 43, "country" : "canada", "money" : 950 }{ "_id" : 44, "country" : "india", "money" : 406 }{ "_id" : 45, "country" : "japan", "money" : 776 }{ "_id" : 46, "country" : "canada", "money" : 468 }{ "_id" : 47, "country" : "germany", "money" : 262 }{ "_id" : 48, "country" : "germany", "money" : 126 }{ "_id" : 49, "country" : "japan", "money" : 86 }{ "_id" : 50, "country" : "canada", "money" : 870 }{ "_id" : 51, "country" : "india", "money" : 98 }{ "_id" : 52, "country" : "india", "money" : 673 }{ "_id" : 53, "country" : "japan", "money" : 487 }{ "_id" : 54, "country" : "india", "money" : 681 }{ "_id" : 55, "country" : "canada", "money" : 491 }{ "_id" : 56, "country" : "japan", "money" : 98 }{ "_id" : 57, "country" : "china", "money" : 172 }

運行結果:

db.foo.mapReduce(map, reduce, {out : "collection"}){    "result" : "collcetion",    "timeMillis" : 83,    "counts" : {        "input" : 99,        "emit" : 99,        "reduce" : 5,        "output" : 5    },    "ok" : 1,    "$gleStats" : {        "lastOpTime" : Timestamp(1399168165, 15),        "electionId" : ObjectId("535a2ce15918f42de9ab1427")    },}

(1)result:存放的集合名
(2)timeMillis:操作花費的時間,單位是毫秒
(3)input:傳入文檔數目
(4)emit:此函數被調用的次數
(5)reduce:此函數被調用的次數
(6)output:最后返回文檔的個數
查看下collection結果內容:

 db.collection.find();{ "_id" : "canada", "value" : { "count" : 19 } }{ "_id" : "china", "value" : { "count" : 15 } }{ "_id" : "germany", "value" : { "count" : 25 } }{ "_id" : "india", "value" : { "count" : 20 } }{ "_id" : "japan", "value" : { "count" : 20 } }

3.2 MapRecude其他的鍵
(1)"finalize" : function
可以將reduce的結果發送給這個鍵,這是整個處理過程的最后一步。
(2)"keeptemp自動為true。" : boolean
如果為true,則在連接關閉后結果保存,否則不保存。
(3)"out" : string
輸出集合的名稱,如果設置,keeptemp自動為true。
(4)"query" : document
在發往map前,先用指定條件過濾文檔。
(5)"sort" : document
在發往map前,先進行排序。
(6)"limit" : integer
發往map函數的文檔數量上限。
(7)"scope" : document
可以在javascripts代碼中使用的變量。
(8)"verbose" : boolean
是否記錄詳細的服務器日志。
3.2.1 finalize函數
可以使用finalize函數作為參數,會在最后一個reduce輸出結果后執行,然后將結果保存在臨時集合里。
3.2.2 保存結果集合
默認情況下,執行mapreduce時創建一個臨時集合,集合名稱為mr.stuff.ts.id,即mapreduce.集合名.時間戳.數據庫作業ID。MongoDB會在調用的連接關閉時自動銷毀這個集合。
3.2.3 對子文檔執行mapreduce
每個傳遞給map的文檔都需要先反序列化,從BSON對象轉換為js對象,這個過程非常耗時,可以先對文檔過濾來提高map速度,可以通過"query","limit"和"sort"等對文檔進行過濾。
"query"的值是一個查詢文檔。
"limit","sort"配合可以發揮很大的作用。
"query","limit"和"sort"可以隨意組合使用。
3.2.4 作用域
作用域鍵"scope",可以用變量名:值這樣普通的文檔來設置該選項,
3.2.5 獲取更多的輸出
設置verbose為true,可以將mapreduce過程更多的信息輸出到服務器日志上。

4 聚合命名
count和distinct操作可以簡化為普通命令,不需要使用聚合框架。
4.1 count
count返回集合中的文檔數量:

db.foo.count() =>99

可以傳入一個查詢文檔:

db.foo.count({country : "china"}) =>15

增加查詢條件會使count變慢。
4.2 distinct
distinct用來找出給定鍵的所有不同值。使用時必須指定集合和鍵。

db.runCommand({ "distinct" : "foo", "key" : "country"}) =>{    "values" : [        "japan",        "germany",        "india",        "china",        "canada"    ],    "stats" : {        "n" : 99,        "nscanned" : 99,        "nscannedObjects" : 99,        "timems" : 22,        "cursor" : "BasicCursor"    },    "ok" : 1,    "$gleStats" : {        "lastOpTime" : Timestamp(1399171995, 15),        "electionId" : ObjectId("535a2ce15918f42de9ab1427")    }}

4.3 group
使用group可以進行更為復雜的聚合。先選定分組所依據的鍵,然后根據選定鍵的不同值分為若干組,然后對每一個分組進行聚合,得到結果文檔。
插入示例數據:

var name = ["Caoqing", "Spider-man", "Garfield"]for (var i = 0; i < 10000; i++) {  iname = name[Math.floor(Math.random() * name.length)];  date = new Date().getTime();  number = Math.floor(100 * Math.random());  db.coll.insert({_id : i, name : iname, time : date, age : number});}

生成的列表中包含最新的時間和最新的時間對應的年紀。
可以安裝name進行分組,然后取出每個分組中date最新的文檔,將其加入結果集。

db.runCommand({"group" : {  "ns" : "coll",  "key" : {"name" : true},  "initial" : {"time" : 0},  "$reduce" : function(doc, prev) {    if (doc.time > prev.time) {      prev.age = doc.age;      prev.time = doc.time;    }  }}})

(1)"ns" : "coll"
指定進行分組的集合。
(2)"key" : {"name" : true}
指定分組依據的鍵。
(3)"initial" : {"time" : 0}
初始化time值,作為初始Wednesday傳遞給后續過程。每組成員都會使用這個累加器。
結果:

"$reduce" : function(doc, prev) {...}{    "retval" : [        {            "name" : "Spider-man",            "time" : 1399179398567,            "age" : 55        },        {            "name" : "Garfield",            "time" : 1399179398565,            "age" : 85        },        {            "name" : "Caoqing",            "time" : 1399179398566,            "age" : 86        }    ],    "count" : 10000,    "keys" : 3,    "ok" : 1,    "$gleStats" : {        "lastOpTime" : Timestamp(1399179362, 1),        "electionId" : ObjectId("535a2ce15918f42de9ab1427")    }}

如果有文檔不存在指定分組的鍵,這些文檔會單獨分為一組,缺失的鍵會使用name:null這樣的形式。如下:

db.coll.insert({age : 5, time : new Date().getTime()})

返回結果:

    ...    {      "name" : null,      "time" : 1399180685288,      "age" : 5    }    "count" : 10001,    "keys" : 4,    ...

為了排除不包含指定用于分組的鍵的文檔,可以在"condition"中加入"name":{"$exists" : true}。

db.runCommand({"group" : {  "ns" : "coll",  "key" : {"name" : true},  "initial" : {"time" : 0},  "$reduce" : function(doc, prev) {    if (doc.time > prev.time) {      prev.age = doc.age;      prev.time = doc.time;    }  },  "condition" : {"name" : {"$exists" : true}}}})

4.3.1 使用完成器
完成器(finalizer)用于精簡從數據庫傳到用戶的數據,因為group命令的輸出結果需要能夠通過單次數據庫響應返回給用戶。
4.3.2 將函數作為鍵使用
分組條件可以非常復雜,不是單個鍵,例如分組時按照類別分組dog和DOG是兩個完全不同的組,為了消除大小寫差異,可以定義一個函數決定文檔分組所依據的鍵。
定義分組函數需要用到"$keyf"鍵,

db.foo.group({  "ns" : "foo",  "$keyf" : function(x) { return x.category.toLowerCase(); };  "initial" : ...,  ......})

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 国产精品国产成人国产三级 | 日韩 综合| 欧美精品一区二区三区在线 | 亚洲日韩中文字幕一区 | 全黄毛片 | 激情视频在线播放 | 欧美一级淫片a免费播放口 91九色蝌蚪国产 | 国产视频在线观看一区二区三区 | 在线成人免费视频 | 国内成人自拍视频 | 日韩视频一二三 | 欧洲精品视频在线观看 | 中文在线观看www | 欧美大胆xxxx肉体摄影 | 久久久久久久久国产 | 免费在线观看亚洲 | 国产精品久久久久久久久久东京 | 成人超碰 | 怦然心动50免费完整版 | 国产精品啪一品二区三区粉嫩 | 免费视频aaa| 国产精品美女一区二区 | 免费一级毛片在线播放不收费 | 欧美一级不卡视频 | 色妇视频 | 欧美成人高清视频 | 国产在线精品91 | 男男羞羞视频网站国产 | 日本精品久久久一区二区三区 | 亚洲欧美不卡视频 | 精品一区二区久久久久久久网精 | 日本一区二区不卡高清 | 国产精品久久久久久久成人午夜 | 羞羞视频在线免费 | 免看黄大片aa | 999插插插| 色婷婷久久一区二区 | 欧美天堂一区 | 视频久久免费 | 久久影院国产精品 | 老子午夜影院 |