1. 程式人生 > >mongodb 高級查詢詳解

mongodb 高級查詢詳解

dde 字節 size 普通查詢 proc 高級 lock sum pri

MongoDB:管道操作

使用聚合框架可以對集合中的文檔進行變換和組合。基本上,可以用多個構件創建一個管道(pipeline),用於對一連串的文檔進行處理。這些構件包括篩選(filter)、投射(projecting)、分組(grouping)、排序(sorting)、限制(limiting)和跳過(skipping)。

例如,有一個保存書籍信息的集合,你想知道投票數量最多的書籍。可以按照如下步驟創建管道:

  • 將每個書籍信息中的書名和投票投射出來{"$project":{"title":1,"vote_num":1}}
  • 統計每個書名所有的投票數(書名可能重復){"$group":{"_id":"$title","vote_num":{"$sum":"$vote_num"}}}
  • 按照投票數降序排列{"$sort":{"vote_num":-1}}
  • 將返回結果限制為前5個{"$limit":5}
db.book_info.aggregate({"$project":{"title":1,"vote_num":1}},{"$group":{"_id":"$title","vote_num":{"$sum":"$vote_num"}}},{"$sort":{"vote_num":-1}},{"$limit":5})

結果

{ "_id" : "小王子", "vote_num" : 265438 }
{ "_id" : "活著", "vote_num" : 263733 }
{ "_id" : "追風箏的人", "vote_num" : 253532 }
{ "_id" : "白夜行", "vote_num" : 232564 }
{ "_id" : "夢裏花落知多少", "vote_num" : 204792 }

$match

$match用於對文檔集合記性篩選,之後就可以在篩選得到的文檔子集上做聚合。

db.book_info.aggregate({"$match":{"title":"小王子"}},{"$project":{"title":1,"vote_num":1}})

結果

{ "_id" : ObjectId("58d3972be13823416ede764c"), "title" : "小王子", "vote_num" : 210926 }
{ "_id" : ObjectId("58d397c6e13823416ede823c"), "title" : "小王子", "vote_num" : 10273 }
{ "_id" : ObjectId("58d398a6e13823416ede930a"), "title" : "小王子", "vote_num" : 3748 }
...

match使"

gt”、”lt""

in”等)

db.book_info.aggregate({"$match":{"title":"小王子","vote_num":{"$lt":100}}},{"$project":{"title":1,"vote_num":1}})

結果

{ "_id" : ObjectId("58d39d27e13823416edeec07"), "title" : "小王子", "vote_num" : 65 }
{ "_id" : ObjectId("58d39e33e13823416edf01e4"), "title" : "小王子", "vote_num" : 91 }
{ "_id" : ObjectId("58d39f2fe13823416edf1670"), "title" : "小王子", "vote_num" : 93 }
...

$project

使用$project操作,可以從子文檔中提取字段,可以重命名字段,還可以再這些字段上進行一些有意思的操作。

選擇想要的字段

db.book_info.aggregate({"$project":{"title":1,"vote_num":1}})

結果

{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "vote_num" : 38478 }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "vote_num" : 598 }
{ "_id" : ObjectId("58d39725e13823416ede75f2"), "title" : "醞釀之道", "vote_num" : 32 }
...

重命名字段。這裏的$vote_num語法是為了在聚合框架中引用vote_num字段

db.book_info.aggregate({"$project":{"title":1,"vote_num_rename":"$vote_num"}})

結果

{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "vote_num_rename" : 38478 }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "vote_num_rename" : 598 }
{ "_id" : ObjectId("58d39725e13823416ede75f2"), "title" : "醞釀之道", "vote_num_rename" : 32 }

$project還支持如下表達式

數學表達式

  • “$add”:[expr1[,expr2,…,exprN]] 接受一個或多個表達式作為參數,將這些表達式相加
  • “$subtract”:[expr1,expr2] 接受兩個表達式作為參數,用第一個表達式減去第二個表達式作為結果
  • “$multiply”:[expr1[,expr2,…,exprN]] 接受一個或多個表達式,並且將他們相乘
  • “$divide”:[expr1,expr2] 接受兩個表達式,用第一個表達式除以第二個表達式的商作為結果
  • “$mod”:[expr1,expr2] 接受兩個表達式,將第一個表達式除以第二個表達式得到的余數作為結果

日期表達式(只能對日期類型的字段進行日期操作,不能對數值類型字段做日期操作)

  • $year
  • $month
  • $week
  • $dayOfMonth
  • $dayOfWeek
  • $dayOfYear
  • $hour

字符串表達式

  • “$substr”:[expr,startOffset,numToReturn] 截取字符串的子串,從startOffset字節開始,一共numToReturn個字節
  • “$concat”:[expr1[,expr2,…,exprN]] 將給定的表達式(或字符串)連接在一起作為返回結果
  • “$toLower”:expr expr必須是字符串,返回expr的小寫形式
  • “$toUpper”:expr 返回expr的大寫形式

邏輯表達式

  • “$cmp”:[expr1,expr2] 比較expr1和expr2,如果相等返回0,expr1

$group

$group操作可以將文檔依據特定字段的不同值進行分組。

db.book_info.aggregate({"$project":{"title":1,"vote_num":1}},{"$group":{"_id":"$title","count":{"$sum":1}}})

結果

{ "_id" : "jQuery實戰", "count" : 3 }
{ "_id" : "Casanova Was a Book Lover", "count" : 1 }
{ "_id" : "Chris Killip", "count" : 1 }
...

算數操作符

  • “$sum”:value 求和
  • “$avg”:value 求平均值

極值操作符

  • “$max”:expr 返回組內的最大值
  • “$min”:expr 返回組內的最小值
  • “$first”:expr 返回分組的第一個值
  • “$last”:expr 返回分組的最後一個值

數組操作符

  • “$addToSet”:expr 如果當前數組不包含expr,則將其添加到數組中,在返回結果集中,每個元素最多出現1次,而且元素的順序是不確定的
  • “$push”:expr 不管expr是什麽值,都將它添加到數組中,返回包含所有值的數組

$unwind

拆分可以將數組中的每一個值拆分成單獨的文檔。例如

> db.book_info.find({},{"title":1,"tags":1})
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : [ "茨威格", "一個陌生女人的來信", "外國文學", "愛情", "小說", "奧地利", "經典", "文學" ] }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : [ "犯罪小說", "推理", "懸疑", "尤?奈斯博", "小說", "外國文學", "北歐", "人性" ] }
...

db.book_info.aggregate({"$project":{"title":1,"tags":1}},{"$unwind":"$tags"})
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "茨威格" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "一個陌生女人的來信" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "外國文學" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "愛情" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "小說" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "奧地利" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "經典" }
{ "_id" : ObjectId("58d39725e13823416ede75f0"), "title" : "一個陌生女人的來信", "tags" : "文學" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "犯罪小說" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "推理" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "懸疑" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "尤·奈斯博" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "小說" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "外國文學" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "北歐" }
{ "_id" : ObjectId("58d39725e13823416ede75f1"), "title" : "獵豹", "tags" : "人性" }
...

$sort

可以根據任何字段(或者多個字段)進行排序,與在普通查詢中的語法是相同的。如果要對大量的文檔進行排序,強烈建議在管道的第一階段進行排序,這時的排序操作可以使用索引。否則,排序過程就會比較慢,而且會占用大量內存。

1是升序,-1是降序

db.book_info.aggregate({"$project":{"title":1,"vote_num":1}},{"$sort":{"vote_num":-1}})

結果

{ "_id" : ObjectId("58d39726e13823416ede75ff"), "title" : "追風箏的人", "vote_num" : 247079 }
{ "_id" : ObjectId("58d3972be13823416ede764c"), "title" : "小王子", "vote_num" : 210926 }
{ "_id" : ObjectId("58d3972ae13823416ede7641"), "title" : "圍城", "vote_num" : 179228 }
{ "_id" : ObjectId("58d3972de13823416ede766a"), "title" : "白夜行", "vote_num" : 172310 }
{ "_id" : ObjectId("58d39725e13823416ede75f3"), "title" : "解憂雜貨店", "vote_num" : 166469 }
...

$limit

$limit接受一個數字n,返回結果集中的前n個文檔

db.book_info.aggregate({"$limit":5})

$skip

$skip接受一個數字n,表示丟棄結果集中的前n個文檔。如果需要跳過大量的數據,那麽這個操作符的效率會比較低。

db.book_info.aggregate({"$skip":5})

mongodb 高級查詢詳解