MongoDb基本操作

1.keyvalue数据类型

上面说过,MongoDB的基本数据是以key-value为单位的,key只能是字符串,但value的数据类型则多种多样,这些类型基本从BSON格式的基础上进行一些添加和修改而来,以下是一些常见类型。

Js代码  收藏代码
  1. /* 空值 null */  
  2.   
  3. "name" : null }  
  4.   
  5. /* 布尔值 boolean */  
  6.   
  7. "flag" : true }  
  8.   
  9. /* 数值 
  10.     包括32bit、64bit整数integer类型和64bit浮点floating point类型 */  
  11.   
  12. {  
  13.   "copies" : 300,  
  14.   "price" : 60.8  
  15. }  
  16.   
  17. /* 字符串 string */  
  18.   
  19. "dbname" : "MongoDB" }  
  20.   
  21. /* 日期 date */  
  22.   
  23. "post_time" : new Date() }  
  24.   
  25. /* 正则表达式 regular expression */  
  26.   
  27. "name_match" : /huangz/i }  
  28.   
  29. /* 数组类型 array */  
  30.   
  31. "tags" : ["mongodb""nosql"] }  
  32.   
  33. /* 嵌套文档 embedded document  
  34.     一个文档里面Key的值可以是另外一个文档 */  
  35.   
  36. {  
  37.   "name" : "huangz",  
  38.   "phone" : { "home" : 123321,  
  39.                      "mobile" :  15820123123}  
  40. }  
  41.   
  42. /* id 
  43.     每个MongoDB文档都必须有一个叫作"_id"的Key, 同一集合里面的文档_id必须各不相同。 
  44.     id可以在创建文档的时候指定,可以是任何类型,无指定时,默认使用ObjectId对象,自动生成不同的id。 
  45.  
  46.     ObjectId对象生成id(12位)的规则如下: 
  47.         0-3 : Timestamp,时间撮 
  48.         4-6  :  Machine,机器代码 
  49.         7-8  :  PID,MongoDB的程序id 
  50.         9-11:  Increment,一个自增量 
  51. */  
  52.   
  53. "_id" : ObjectId("4da025ac5149e6d915098c59"), "name" : "huangz""phone" : { "home" : 33123123, "mobile" : 15820123123 } }  

不必担心自动生成id会重复,因为它足够大。

如果想模仿关系数据库,生成一个连续自增的id,可以使用类似如下的代码来实现:

Js代码  收藏代码
  1. /* 生成连续自增id */  
  2.   
  3. > db.person.id = 0  
  4. 0  
  5.   
  6. > db.person.insert({  
  7. ... "_id" : db.person.id += 1,  
  8. ... "name" : "huangz"   
  9. ... })  
  10.   
  11. > db.person.insert({  
  12. ... "_id" : db.person.id += 1,  
  13. ... "name" : "jack",  
  14. ... })  
  15.   
  16. > db.person.find()  
  17. "_id" : 1, "name" : "huangz" }  
  18. "_id" : 2, "name" : "jack" }  
  19.   
  20. > db.person.find({"_id" : 1})  
  21. "_id" : 1, "name" : "huangz" }  
 
 
 
2.CRUD操作

了解MongoDB的基本数据类型和组织方式后,我们可以测试以下常见的CRUD操作。

Js代码  收藏代码
  1. /* 选择数据库和集合,这里使用test数据库 */  
  2.   
  3. > use test  
  4. switched to db test  
  5.   
  6.   
  7. /* CREATE 创建一个post文档,并将post添加到test.blog集合里面 */  
  8.   
  9. > post = {  
  10. ... "title" : "First day with MongoDB",  
  11. ... "author" : "huangz",  
  12. ... "content" : "Today, i try MongoDB, it's great!",  
  13. ... "date" : new Date()  
  14. ... }  
  15. {  
  16.     "title" : "First day with MongoDB",  
  17.     "author" : "huangz",  
  18.     "content" : "Today, i try MongoDB, it's great!",  
  19.     "date" : "Sat Apr 09 2011 16:21:51 GMT+0800 (CST)"  
  20. }  
  21.   
  22. > db.blog.insert(post)    /*这里的db是一个指向test数据库的变量 */  
  23.   
  24.   
  25. /* READ 查询创立的post文档,MongoDB提供了包括find和findOne等多种查询方式 
  26.  
  27.     find函数的参数如果为空字典,则是查询整个集合,显示最先20条记录(record), 
  28.     find函数也可以使用参数,作为查询条件, 
  29.     findOne函数和find类似,但是只返回符合条件的第一条记录 */  
  30.   
  31. > db.blog.find()  
  32. "_id" : ObjectId("4da017c55149e6d915098c57"), "title" : "First day with MongoDB""author" : "huangz""content" : "Today, i try MongoDB, it's great!""date" : "Sat Apr 09 2011 16:24:26 GMT+0800 (CST)" }  
  33.   
  34. > db.blog.find({"author" : "huangz"})  
  35. "_id" : ObjectId("4da0177a0fcb4b53ae3ba9fc"), "title" : "First day with MongoDB""author" : "huangz""content" : "Today, i try MongoDB, it's great!""date" : "Sat Apr 09 2011 16:21:51 GMT+0800 (CST)" }  
  36.   
  37. > db.blog.findOne()  
  38. {  
  39.     "_id" : ObjectId("4da0177a0fcb4b53ae3ba9fc"),  
  40.     "title" : "First day with MongoDB",  
  41.     "author" : "huangz",  
  42.     "content" : "Today, i try MongoDB, it's great!",  
  43.     "date" : "Sat Apr 09 2011 16:21:51 GMT+0800 (CST)"  
  44. }  
  45.   
  46. /* UPDATE 更新post记录,为它增加一个Key名字为comments的列表。*/  
  47.   
  48. > post                                                  
  49. {  
  50.     "title" : "First day with MongoDB",  
  51.     "author" : "huangz",  
  52.     "content" : "Today, i try MongoDB, it's great!",  
  53.     "date" : "Sat Apr 09 2011 16:24:26 GMT+0800 (CST)"  
  54. }  
  55.   
  56. > post.comments = []  
  57. [ ]  
  58.   
  59. > db.blog.update({"title" : "First day with MongoDB"}, post)  
  60.   
  61. > post  
  62. {  
  63.     "title" : "First day with MongoDB",  
  64.     "author" : "huangz",  
  65.     "content" : "Today, i try MongoDB, it's great!",  
  66.     "date" : "Sat Apr 09 2011 16:24:26 GMT+0800 (CST)",  
  67.     "comments" : [ ]  
  68. }  
  69.   
  70.   
  71. /* DELETE 删除post,下面两种方法等效 */  
  72.   
  73. db.blog.remove(post)  
  74. db.blog.remove({"title" : "First day with MongoDB"})  

深入查询:

 

1.条件查询

除了使用

Js代码  收藏代码
  1. db.collection.find()  

查询集合里所有文档外,我们可以指定特定的查询条件,比如以下语句只查询age为20的人。

Js代码  收藏代码
  1. > db.user.find({"age": 20})  
  2. "_id" : ObjectId("4d7aff454dca002afb000000"), "age" : 20, "name" : "huangz" }  

还可以使用多个查询条件,比如以下语句不但查询age为20的人,还查询name为huangz的人。

Js代码  收藏代码
  1. > db.user.find({"age": 20, "name" : "huangz"})  
  2. "_id" : ObjectId("4d7aff454dca002afb000000"), "age" : 20, "name" : "huangz" }  

还可以指定返回文档的数目

Js代码  收藏代码
  1. > db.user.find().limit(1)  
  2. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  

以及跳过指定数目的文档

Js代码  收藏代码
  1. /* 所有文档 */  
  2. > db.user.find()  
  3. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  4. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  5. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  6.   
  7. /* 跳过第一个文档 */  
  8. > db.user.find().skip(1)  
  9. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  10. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  

 排序指定文档的key,国际惯例,1为升序,-1为降序。

Js代码  收藏代码
  1. /* 以name项的降序排列文档 */  
  2.   
  3. > db.user.find().sort({"name": -1})  
  4. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  5. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  6. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  

 

2.指定返回项

如果你只是对文档的某个(或数个)key感兴趣,那么你可以在查询时,指定要返回的key(注意:"_id"项默认总是被返回的,除非你明确指定不返回"_id"项)。

Js代码  收藏代码
  1. /* 所有结果 */  
  2.   
  3. > db.user.find()  
  4. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  5. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  6. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  7.   
  8. /* 只显示age为20岁的人所喜欢的水果,其他不显示。(比如一个水果贩子它只关心水果的受欢迎程度,不管你姓甚名谁。) */  
  9.   
  10. > db.user.find({"age": 20}, {"favorite fruit": 1})  
  11. "_id" : ObjectId("4da070040d03918e09fe7dac"), "favorite fruit" : "pear" }  
  12. "_id" : ObjectId("4da070180d03918e09fe7dad"), "favorite fruit" : "apple" }  
  13.   
  14. /* 除了favorite fruit项之外,都显示 */  
  15.   
  16. > db.user.find({"age": 20}, {"favorite fruit": 0})  
  17. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20 }  
  18. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20 }  
  19.   
  20. /* 但是似乎没有办法同时指定显示和不显示。。。  
  21.  
  22. > db.user.find({"age": 20}, {"favorite fruit": 0, "name": 1}) 
  23. error: { 
  24.     "$err" : "You cannot currently mix including and excluding fields. Contact us if this is an issue." 
  25. } 
  26.  
  27. */  

 

3.条件查询(使用条件操作符)

可以使用MongoDB提供的查询条件操作符来进行查询,比如"$lt", "$lte", "$gt", "$gte"分别代表 "<", "<=", ">", ">="。

Js代码  收藏代码
  1. /* 只查询age <= 20的人 */  
  2.   
  3. > db.user.find({"age": {"$lte" : 20}})  
  4. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  5. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  

 "$ne"操作符代表"!=",不等关系。

Js代码  收藏代码
  1. /* 只查询age != 20的人 */  
  2. > db.user.find({"age": {"$ne" : 20}})  
  3. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  

"$in"操作符表示,只要符合条件数组中的任何一个,就能被选中。

Js代码  收藏代码
  1. /* 选中喜欢吃苹果apple或者香蕉banana的人 */  
  2.   
  3. > db.user.find({"favorite fruit": {"$in" : ["apple""banana"]}})  
  4. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  5. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  

 "$nin"操作符表示,只要不符合条件数组的任何一个,就能被选中。

Js代码  收藏代码
  1. /* 选中不喜欢吃苹果或香蕉的任何一种的人 */  
  2.   
  3. > db.user.find({"favorite fruit": {"$nin" : ["apple""banana"]}})  
  4. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  

"$all"操作符表示,必须符合条件数组中的每一个,才能被选中。

Js代码  收藏代码
  1. /* 所有文档 */  
  2.   
  3. > db.post.find()  
  4. "_id" : ObjectId("4da079ee78d367d0af2ded04"), "title" : "today, a new database release""content" : "long long ago...""tags" : [ "nosql""mongodb""database" ] }  
  5. "_id" : ObjectId("4da07aee78d367d0af2ded06"), "title" : "MySQL sucks""content" : "i use MySQL 10 year ago, is a long time...""tags" : [ "database""mysql" ] }  
  6.   
  7. /* 只选中tags项符合["nosql", "mongodb", "database"]的文档(注意条件的顺序这里不重要) */  
  8.   
  9. > db.post.find({"tags": {$all: ["nosql""mongodb""database"]}})  
  10. "_id" : ObjectId("4da079ee78d367d0af2ded04"), "title" : "today, a new database release""content" : "long long ago...""tags" : [ "nosql""mongodb""database" ] }  

"$not"操作符表示,对操作取反。

Js代码  收藏代码
  1. /* 只选中age不小于或等于20的人 */  
  2.   
  3. > db.user.find({"age": {"$not": {"$lte": 20}}})  
  4. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  

"$or"操作符表示,至少符合条件数组的其中一个,就能被选中。

Js代码  收藏代码
  1. /* 下面包含or的语句本该返回2个文档,但在我的测试中,1.8.1版本一个结果也不返回。。。真诡异 */  
  2.   
  3. > db.user.find()  
  4. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  5. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  6. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  7.   
  8. /* WTF */  
  9. > db.user.find({"$or": [{"age": 20}, {"age": 21}]})  
  10. >  
  11.   
  12. /* 本该是 
  13.  
  14. { "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz", "age" : 20, "favorite fruit" : "pear" } 
  15. { "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack", "age" : 20, "favorite fruit" : "apple" } 
  16. */  

"$and"操作符表示。。。嗯。。。MongoDB没有$and,你在写查询的时候提供多个条件就是了。

Js代码  收藏代码
  1. > db.user.find({"name":"huangz""age": 20})  
  2. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  

  

"$size"操作符搜索符合指定大小的数组的文档。
 
Js代码  收藏代码
  1. /* 所有文档,分别有size为2和3的文档各一个 */  
  2.   
  3. > db.post.find()  
  4. "_id" : ObjectId("4da079ee78d367d0af2ded04"), "title" : "today, a new database release""content" : "long long ago...""tags" : [ "nosql""mongodb""database" ] }  
  5. "_id" : ObjectId("4da07aee78d367d0af2ded06"), "title" : "MySQL sucks""content" : "i use MySQL 10 year ago, is a long time...""tags" : [ "database""mysql" ] }  
  6.   
  7. /* 查找所有tags的size为3的文档 */  
  8.   
  9. > db.post.find({"tags": {"$size": 3}})  
  10. "_id" : ObjectId("4da079ee78d367d0af2ded04"), "title" : "today, a new database release""content" : "long long ago...""tags" : [ "nosql""mongodb""database" ] }  
 
"$slice"操作符返回符合查询条件的文档的,指定大小的数组分割。
 
Js代码  收藏代码
  1. /* 返回所有post集合文档, 其中只返回大小为2的tags数组 */  
  2.   
  3. > db.post.find({}, {"tags": {"$slice": 2}})  
  4. "_id" : ObjectId("4da079ee78d367d0af2ded04"), "tags" : [ "nosql""mongodb""database" ] }  
  5. "_id" : ObjectId("4da07aee78d367d0af2ded06"), "tags" : [ "database""mysql" ] }  
  6.   
  7. /* $slice操作怎么又错了,悲剧阿。。。 */  
 
"$elemMatch"操作符用来处理嵌套文档的查询。
 
Js代码  收藏代码
  1. // Document 1  
  2. "foo" : [  
  3.       {  
  4.         "shape" : "square",  
  5.         "color" : "purple",  
  6.         "thick" : false  
  7.       },  
  8.       {  
  9.         "shape" : "circle",  
  10.         "color" : "red",  
  11.         "thick" : true  
  12.       }  
  13. ] }  
  14.   
  15.   
  16. // Document 2  
  17. "foo" : [  
  18.       {  
  19.         "shape" : "square",  
  20.         "color" : "red",  
  21.         "thick" : true  
  22.       },  
  23.       {  
  24.         "shape" : "circle",  
  25.         "color" : "purple",  
  26.         "thick" : false  
  27.       }  
  28. ] }  
  29.   
  30.   
  31. /* 失败查询 */  
  32. db.foo.find({"foo.shape""square""foo.color""purple"})  
  33. db.foo.find({foo: {"shape""square""color""purple"} } )  
  34.   
  35. /* 正确方法 */  
  36. db.foo.find({foo: {"$elemMatch": {shape: "square", color: "purple"}}})  
 
 4.指针
 
指针用来记住一个文档的位置,它是惰性的,不会马上求值,适合用来做一些迭代形的操作,比如翻页。
 
Js代码  收藏代码
  1. /* 翻页程序 */  
  2.   
  3. var record_per_page = 50  
  4.   
  5. var pages = db.post.find().sort({"date": -1}).limit(record_per_page)  
  6.   
  7. latest = loop_and_print_pages(pages)  
  8.   
  9. var pages = db.post.find({"date": {"$gt": latest.date}}).sort({"date": 1}).limit(record_per_page)  
  10.   
  11. latest = loop_and_print_pages(pages)  
 
 为什么要用指针代替skip?因为skip的值变得越来越大的时候,也会越来越慢,而用一个指针代替庞大的skip,可以节省时间。
 
5.随机
 
经常会遇到问题,需要随机获取任意一个文档,你可以这样做。
 
Js代码  收藏代码
  1. >db.user.find().skip(Math.floor(Math.random()*db.user.find().count())).limit(1)                                                                               
  2. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  3.   
  4. >db.user.find().skip(Math.floor(Math.random()*db.user.find().count())).limit(1)  
  5. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  6.   
  7. >db.user.find().skip(Math.floor(Math.random()*db.user.find().count())).limit(1)  
  8. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  9.   
  10. >db.user.find().skip(Math.floor(Math.random()*db.user.find().count())).limit(1)  
  11. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
 
 因为每次随机都要读取一次count(),你可以用随机id或者其他办法优化速度。MongoDB的官方cookbook有一个相关实例: http://cookbook.mongodb.org/patterns/random-attribute/
6.快照查询
 
当你对一系列文档同时进行查询-修改-保存的操作哦时,文档有可能在过程中被意外修改或者因为有新的文档被插入而导致数据错误,这时,你需要使用快照查询,对当前数据做一个快照,确保在查询的整个过程中数据(暂时)不变。
 
Js代码  收藏代码
  1. > db.user.find({"$query": {}, "$snapshot"true})  
  2. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  3. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  4. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
 
 
3.索引
 
MongoDB的索引和关系数据库的索引差不多,都是加速查询用的,个人通常的做法是,对常用的查询key添加索引。
 
Js代码  收藏代码
  1. /* 比如你经常要按升序查询名字,可以给名字(name)项加上索引 */  
  2.   
  3. > db.user.ensureIndex({"name":1})  
  4.   
  5. > db.user.find().sort({"name":1})  
  6. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  7. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  8. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
 
可以用explain()函数对文档的索引情况进行详细调查。
 
Js代码  收藏代码
  1. /* 使用了索引的查询 */  
  2.   
  3. > db.user.find({"name""huangz""age": 20}).explain()  
  4. {  
  5.     "cursor" : "BtreeCursor name_1_age_1",    // 索引的类型,索引成功一般为BasicCursor,name_1_age_1显示索引的名字  
  6.     "indexBounds" : [    // 索引查询的条件限制  
  7.         [  
  8.             {  
  9.                 "name" : "huangz",  
  10.                 "age" : 20  
  11.             },  
  12.             {  
  13.                 "name" : "huangz",  
  14.                 "age" : 20  
  15.             }  
  16.         ]  
  17.     ],  
  18.     "nscanned" : 1,    // 扫描项目数量,项目可以是Object也可以是索引关键字  
  19.     "nscannedObjects" : 1,    // 扫描过的对象数量  
  20.     "n" : 1,   // 实际返回文档数量  
  21.     "millis" : 0    // 查询使用时间  
  22. }  
  23.   
  24.   
  25. /* 没有使用索引的查询 */  
  26.   
  27. > db.post.find({"title""feak title"}).explain()  
  28. {  
  29.     "cursor" : "BasicCursor",  
  30.     "indexBounds" : [ ],  
  31.     "nscanned" : 2,  
  32.     "nscannedObjects" : 2,  
  33.     "n" : 0,  
  34.     "millis" : 0  
  35. }  
 
删除索引
 
Js代码  收藏代码
  1. /* 删除name项的升序索引 */  
  2.   
  3. db.user.dropIndexes({"name": 1})  
 
需要注意的一些地方是:
 
1.MongoDB的索引是包含性的,位置无关的。
   比如你已经索引了{"name": 1, "age": 1},那么就不必单独索引{"name": 1}了;
   另外,{"name": 1, "age": 1}和{"age": 1, "name": 1}基本相同,说基本是因为当查询的时候,key的顺序是没有关系的,但是当遇上sort操作的时候,索引顺序的不同决定了能否使用索引。
 
Js代码  收藏代码
  1. /* 比如你以下两个查询都可以被索引 */  
  2.   
  3. db.user.ensureIndex({"name": 1, "age": 1})  
  4.   
  5. db.user.find({"name""huangz""age": 20})  
  6. db.user.find({"age": 20, "name""huangz"})  
  7.   
  8. /* 以下两个就不一样了,只有第一个查询被索引,第二个查询没有用到索引 */  
  9.   
  10. db.user.find().sort({"name": 1, "age": 1})  
  11.   
  12. db.user.find().sort({"age": 1, "name": 1})  
 
2.MongoDB不单是为快速查询而设计,其他update、delete、insert操作也是相当重要,但是索引会减慢除查询外其他操作的速度,这个要注意,如果你集合里面文档增加的速度相当快,比如你要实现一个tweeter,那么对tweet项索引可能反而会增加系统插入新tweet的负担。
外MongoDB的索引是全部放进内存当中的,如果你的内存不大,要慎重使用索引。
 
 
 
4.聚合(aggregation)
 
MongoDB提供了包括MapReduce在内的多种工具,帮助你进行数据聚合操作。
 
count:最简单的聚合工具,也非常常用——计算符合查询条件的文档数量。
 
Js代码  收藏代码
  1. > db.user.find().count()  
  2. 3  
  3. > db.user.find({"name":"huangz"}).count()  
  4. 1  
 
distinct: 计算给定key内的不同值的数量。
 
Js代码  收藏代码
  1. /* 用distinct计算出,集合里只有20岁和30岁的人。 */  
  2.   
  3. > db.user.find()  
  4. "_id" : ObjectId("4da070040d03918e09fe7dac"), "name" : "huangz""age" : 20, "favorite fruit" : "pear" }  
  5. "_id" : ObjectId("4da070180d03918e09fe7dad"), "name" : "jack""age" : 20, "favorite fruit" : "apple" }  
  6. "_id" : ObjectId("4da0702d0d03918e09fe7dae"), "name" : "peter""age" : 30, "favorite fruit" : "banana" }  
  7.   
  8. > db.user.distinct("age")  
  9. [ 20, 30 ]  
 
group:类似关系数据库,对数据进行group操作。(如果MongoDB设置为sharded环境,或者要处理的key数量在10 000以上,则目前必须用MapReduce代替group。)
 
Js代码  收藏代码
  1. /* group的一般参数如下: 
  2.  
  3.     ns: 要处理的集合名称 
  4.     key: 需要group by处理的项 
  5.     reduce: reduce函数,对集合内的每个文档使用一次,接受两个函数,一个是目前迭代的文档,另一个是聚合计数器。 
  6.     initial: 聚合的初始值 
  7.     keyf: 可选,用函数对key中的值进行修改然后作为key参数传入到group。 
  8.     cond: 可选,相当于文档的filter,符合条件的文档才会被group处理。 
  9.     finalize: 可选,每个group处理之后的结果都传入到这个函数,可以用于修改最终group结果。 
  10.  
  11.     参数有点复杂,但整体还是很清晰的。 
  12.  
  13.     最好的例子还是参考mongodb主页的例子: http://www.mongodb.org/display/DOCS/Aggregation 
  14. */  
  15.   
  16. db.test.group(  
  17.    { cond: {"invoked_at.d": {$gte: "2009-11", $lt: "2009-12"}}  
  18.    , key: {http_action: true}  
  19.    , initial: {count: 0, total_time:0}  
  20.    , reduce: function(doc, out){ out.count++; out.total_time+=doc.response_time }  
  21.    , finalize: function(out){ out.avg_time = out.total_time / out.count }  
  22.    } );  
 
MapReduce: MongoDB数据处理的终极武器,可以自动将任务分配到多台服务器上计算,然后将结果合并。主要用于后台大规模数据集处理,不要用于实时计算。
 
Js代码  收藏代码
  1. /* MapReduce参数  
  2.  
  3. db.runCommand( 
  4.  { mapreduce : <collection>, 
  5.    map : <mapfunction>, 
  6.    reduce : <reducefunction> 
  7.    [, query : <query filter object>] 
  8.    [, sort : <sort the query.  useful for optimization>] 
  9.    [, limit : <number of objects to return from collection>] 
  10.    [, out : <see output options below>] 
  11.    [, keeptemp: <true|false>]  // 如果为true,则计算的结果作为数据保存到out参数指定的地方 
  12.    [, finalize : <finalizefunction>]  // 处理所有计算结果的函数 
  13.    [, scope : <object where fields go into javascript global scope >]  // 指定计算时候可以访问的外部变量 
  14.    [, verbose : true]  // 提供计算时候的统计数据 
  15.  } 
  16. ); 
  17.  
  18. 关于out选项,当使用版本<=1.7.3时,可以指定变量或者集合名字作为参数。 
  19. 如果使用版本>=1.7.4,参数名可以是以下: 
  20.     1.collectionName: 结果覆盖同名集合 
  21.     2.{replace: collectionName}: 同上 
  22.     3.{merge: collectionName}: 将结果和集合里的数据合并,如果有同名的key,新的数据会覆盖旧的数据。 
  23.     4.{reduce: collectionName}:  如果计算结果和集合里的数据有相同key的情况出现,将调用指定的reduce function。 
  24.     5.{inline: 1}: 所有计算结果保存在内存当中。 
  25.  
  26. 详细参考MongoDB网站: http://www.mongodb.org/display/DOCS/MapReduce 
  27. */  
  28.   
  29. > db.things.insert( { _id : 1, tags : ['dog''cat'] } );  
  30. > db.things.insert( { _id : 2, tags : ['cat'] } );  
  31. > db.things.insert( { _id : 3, tags : ['mouse''cat''dog'] } );  
  32. > db.things.insert( { _id : 4, tags : []  } );  
  33.   
  34. // map function  
  35. > m = function(){  
  36. ...    this.tags.forEach(  
  37. ...        function(z){  
  38. ...            emit( z , { count : 1 } );  
  39. ...        }  
  40. ...    );  
  41. ...};  
  42.   
  43. // reduce function  
  44. > r = function( key , values ){  
  45. ...    var total = 0;  
  46. ...    for ( var i=0; i<values.length; i++ )  
  47. ...        total += values[i].count;  
  48. ...    return { count : total };  
  49. ...};  
  50.   
  51. > res = db.things.mapReduce(m,r);  
  52. > res  
  53. {"timeMillis.emit" : 9 , "result" : "mr.things.1254430454.3" ,  
  54.  "numObjects" : 4 , "timeMillis" : 9 , "errmsg" : "" , "ok" : 0}  
  55.   
  56. > db[res.result].find()  
  57. {"_id" : "cat" , "value" : {"count" : 3}}  
  58. {"_id" : "dog" , "value" : {"count" : 2}}  
  59. {"_id" : "mouse" , "value" : {"count" : 1}}  
  60.   
  61. > db[res.result].drop()  
原文地址:https://www.cnblogs.com/wishyouhappy/p/3652670.html