mongodb是开源世界一颗冉冉升起的明星,是一个文档型的数据库,允许数据嵌套存储。它的设计目标是:高可扩展性、高性能、便捷的数据访问。

 

这里不打算对安装做陈述,具体可参看官网。

 

mongo book(命令行):连接book数据库,但这时book数据库还不存在,直到我们向次数据库中写入第一条数据。

 

use book(mongo shell):切换到book数据库

 

插入数据:

 
  
  1. > db.towns.insert({ 
  2. name: "New York", 
  3. population: 22200000, 
  4. last_census: ISODate("2009-07-31"), famous_for: [ "statue of liberty", "food" ], 
  5. mayor : { 
  6. name : "Michael Bloomberg", 
  7. party : "I" } 
  8. }) 

 

显示当前数据库中的所有collection:

 
  
  1. > show collections 
  2. system.indexes 
  3. towns 

 

查询collection中所有记录:

 
  
  1. > db.towns.find() 
  2. "_id" : ObjectId("4d0ad975bb30773266f39fe3"), 
  3. "name" : "New York"
  4. "population": 22200000, 
  5. "last_census""Fri Jul 31 2009 00:00:00 GMT-0700 (PDT)"
  6. "famous_for" : [ "statue of liberty""food" ], 
  7. "mayor" : { "name" : "Michael Bloomberg""party" : "I" } 

 _id这个域是系统为我们自动生成的,有点类似于postresql中的serial increment

_id:由12个字节组成,前4个为timestamp,3个机器id,2个进程id,3个自增长的计数

 

查看数据类型:

 
  
  1. typeof db 
  2. object 
  3. typeof db.towns 
  4. object 
  5. typeof db.towns.insert 
  6. function 

 

执行命令时不加()可以查看函数的源码:

 
  
  1. db.towns.insert 
  2. function (obj, _allow_dot) { 
  3. if (!obj) { 
  4. throw "no object passed to insert!"
  5. if (!_allow_dot) { 
  6.         this._validateForStorage(obj); 
  7.     } 
  8. if (typeof obj._id == "undefined") { var tmp = obj; 
  9. obj = {_id:new ObjectId}; for (var key in tmp) { 
  10.             obj[key] = tmp[key]; 
  11.         } 
  12.     } 
  13.     this._mongo.insert(this._fullName, obj); 
  14.     this._lastID = obj._id; 

 

我们可以自定义类javascript式的函数,

 
  
  1. mongo/insert_city.js 
  2. function insertCity( 
  3. name, population, last_census, famous_for, mayor_info 
  4. ){ db.towns.insert({ 
  5.     name:name, 
  6.     population:population, 
  7.     last_census: ISODate(last_census), 
  8.     famous_for:famous_for, 
  9.     mayor : mayor_info 
  10. }); 

然后使用其操作:

 
  
  1. insertCity("Punxsutawney", 6200, '2008-31-01'
  2. ["phil the groundhog"], { name : "Jim Wehrle" } 
  3. insertCity("Portland", 582000, '2007-20-09'
  4. ["beer""food"], { name : "Sam Adams", party : "D" } 

 

根据某个字段查询:

 
  
  1. db.towns.find({ "_id" : ObjectId("4d0ada1fbb30773266f39fe4") }) 
  2. "_id" : ObjectId("4d0ada1fbb30773266f39fe4"), 
  3. "name" : "Punxsutawney"
  4. "population" : 6200, 
  5. "last_census" : "Thu Jan 31 2008 00:00:00 GMT-0800 (PST)"
  6. "famous_for" : [ "phil the groundhog" ], 
  7. "mayor" : { "name" : "Jim Wehrle" } 

 

只查询出特定字段:

 
  
  1. db.towns.find({ _id : ObjectId("4d0ada1fbb30773266f39fe4") }, { name : 1 }) 
  2. "_id" : ObjectId("4d0ada1fbb30773266f39fe4"), 
  3. "name" : "Punxsutawney" 

 

查询出除了name以外的其它字段:

 
  
  1. db.towns.find({ _id : ObjectId("4d0ada1fbb30773266f39fe4") }, { name : 0 }) 
  2. "_id" : ObjectId("4d0ada1fbb30773266f39fe4"), 
  3. "population" : 6200, 
  4. "last_census" : "Thu Jan 31 2008 00:00:00 GMT-0800 (PST)"
  5. "famous_for" : [ "phil the groundhog" ] 

 

使用正则表达式匹配查询、范围运算符查询:

 
  
  1. db.towns.find( 
  2.   { name : /^P/, population : { $lt : 10000 } }, 
  3.   { name : 1, population : 1 } 
  4. "name" : "Punxsutawney""population" : 6200 } 

 

查询条件操作符的格式如下:

 
  
  1. field : { $op : value } 

 

使用javascript使得我们可以像操作对象一样来构造查询:

 
  
  1. var population_range = {} 
  2. population_range['$lt'] = 1000000 
  3. population_range['$gt'] = 10000 
  4. db.towns.find( 
  5. { name : /^P/, population : population_range }, 
  6. { name: 1 } ) 
  7. "_id" : ObjectId("4d0ada87bb30773266f39fe5"), "name" : "Portland" } 

 

再看一个使用运算符的例子:

 
  
  1. db.towns.find( 
  2. { last_census : { $lte : ISODate('2008-31-01') } }, 
  3. _id : 0, name: 1 } 
  4. "name" : "Punxsutawney" } 
  5. "name" : "Portland" } 

 

嵌套的子文档的查询(使用.):

 
  
  1. db.towns.find( 
  2. { 'mayor.party' : 'I' }, 
  3. { _id : 0, name : 1, mayor : 1 } 
  4. )  
  5. "name" : "New York", 
  6. "mayor" : { 
  7. "name" : "Michael Bloomberg", 
  8. "party" : "I" 

 

再插入一些数据:

 

 
  
  1. db.countries.insert({ 
  2. _id : "us"
  3. name : "United States", exports : { 
  4. foods : [ 
  5. { name : "bacon", tasty : true }, { name : "burgers" } 
  6. ] } 
  7. }) 
  8.  
  9. db.countries.insert({ 
  10. _id : "ca"
  11. name : "Canada", exports : { 
  12. "name" : "New York""mayor" : { 
  13. "name" : "Michael Bloomberg"
  14. "party" : "I" } 
  15. foods : [ 
  16. { name : "bacon", tasty : false }, 
  17. { name : "syrup", tasty : true } ] 
  18. }) 
  19.  
  20. db.countries.insert({
  21.  _id : "mx"
  22. name : "Mexico", exports : { 
  23. foods : [{ 
  24. name : "salsa", tasty : true, condiment : true 
  25. }] } 
  26. }) 

 

统计有多少条数据:

 
  
  1. > print( db.countries.count() ) 

 

现在我们想查找出这样一个国家:出口bacon、又是出口tasty的bacon,先看下面的例子:

 
  
  1. db.countries.find( 
  2. 'exports.foods.name' : 'bacon''exports.foods.tasty' : true }, 
  3. { _id : 0, name : 1 } 
  4.  
  5. "name" : "United States" } 
  6. "name" : "Canada" } 

但是这不是我们想要的,我们想要的是同一个document,这时可以使用$elemMatch运算符:

 
  
  1. db.countries.find( 
  2.   { 
  3.     'exports.foods' : { 
  4.       $elemMatch : { 
  5. name : 'bacon'
  6. tasty : true } 
  7. }, 
  8.   { _id : 0, name : 1 } 
  9. "name" : "United States" } 

 

逻辑运算:

 
  
  1. db.countries.find( 
  2. $or : [ 
  3. { _id : "mx" }, 
  4. { name : "United States" }
  5. }, 
  6. { _id:1 } 
  7. "_id" : "us" } { "_id" : "mx" } 

注:逻辑运算符的value是个数组

 

其它运算符:

 
  
  1. Command Description 
  2. $regex Match by any PCRE-compliant regular expression string (or just use the // delimiters as shown earlier) 
  3. $ne Not equal to 
  4. $lt Less than 
  5. $lte Less than or equal to 
  6. $gt Greater than 
  7. $gte Greater than or equal to 
  8. $exists Check for the existence of a field 
  9. $all Match all elements in an array 
  10. $in Match any elements in an array 
  11. $nin Does not match any elements in an array 
  12. $elemMatch Match all fields in an array of nested documents 
  13. $or or 
  14. $nor Not or 
  15. $size Match array of given size 
  16. $mod Modulus 
  17. $type Match if field is a given datatype 
  18. $not Negate the given operator check 
     

 

更新一条document:

 
  
  1. db.towns.update( 
  2. { _id : ObjectId("4d0ada87bb30773266f39fe5") },  
  3. { $set : { "state" : "OR" } } 
  4. ); 

这会向此_id的文档中添加列state并将其值置为"OR",

update(criteria, operation):前一个参数为查询条件,后一个为向查询出的记录所做之操作

 

请注意不要向下面这样用:

 
  
  1. db.towns.update( 
  2. { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { state : "OR" } 
  3. ); 

这会将匹配记录的整条文档替换成{state : "OR"},而不是添加一个field,这通常不是我们想要看到的

 

再看一个更新的例子:

 
  
  1. db.towns.update( 
  2. { _id : ObjectId("4d0ada87bb30773266f39fe5") }, { $inc : { population : 1000} } 

 

更新时可用的操作符:

 
  
  1. Command Description 
  2. $set Sets the given field with the given value 
  3. $unset Removes the field 
  4. $inc Adds the given field by the given number 
  5. $pop Removes the last (or first) element from an array 
  6. $push Adds the value to an array 
  7. $pushAll Adds all values to an array 
  8. $addToSet Similar to push, but won’t duplicate values 
  9. $pull Removes matching value from an array 
  10. $pullAll Removes all matching values from an array 

 

引用:

由于mongo的设计目的,join注定在mongo中是性能非常低下的,但有时你可能确实需要它。

这时你可以使用这样的结构:{$ref : "collection_name", $id : "reference_id"}

 
  
  1. db.towns.update( 
  2. { _id : ObjectId("4d0ada87bb30773266f39fe5") }, 
  3. { $set : { country: { $ref: "countries", $id: "us" } } } 

现在我们可从towns中查询出对应的国家:

 
  
  1. var portland = db.towns.findOne({ _id : ObjectId("4d0ada87bb30773266f39fe5") }) 
  2.  
  3. db.countries.findOne({ _id: portland.country.$id }) 

 

删除:与find类似,只要将find()函数替换成remove()函数即可,有一点需要注意:删除时会删除掉所有匹配的文档包括子文档。

 
  
  1. var bad_bacon = { 'exports.foods' : { 
  2. $elemMatch : { name : 'bacon', tasty : false 
  3. } } 
  4. db.countries.find( bad_bacon ) 
  5. "_id" : ObjectId("4d0b7b84bb30773266f39fef"), "name" : "Canada"
  6. "exports" : { 
  7. "foods" : [ { 
  8. "name" : "bacon"
  9.         "tasty" : false 
  10.       }, 
  11. "name" : "syrup""tasty" : true 
  12. } ] 
  13. } } 
  14.  
  15. db.countries.remove( bad_bacon ) 
  16. db.countries.count() 

 

 再看几个关于查询的例子(尽量少用这种自定义的查询,性能较差):

 
  
  1. db.towns.find( function() { 
  2. return this.population > 6000 && this.population < 600000
  3. }) 
  4.  
  5. db.towns.find("this.population > 6000 && this.population < 600000") 
  6.  
  7. db.towns.find( { 
  8. $where : "this.population > 6000 && this.population < 600000", famous_for : /groundhog/ 
  9. }) 

 

再插入些数据:

 
  
  1. populatePhones = function(area,start,stop) { for(var i=start; i < stop; i++) { 
  2. var country = 1 + ((Math.random() * 8) << 0); var num = (country * 1e10) + (area * 1e7) + i; db.phones.insert({ 
  3.       _id: num, 
  4.       components: { 
  5.         country: country, 
  6.         area: area, 
  7.         prefix: (i * 1e-4) << 0, 
  8.         number: i 
  9. }, 
  10. display: "+" + country + " " + area + "-" + i }); 
  11. } } 

 
  
  1. populatePhones( 800, 5550000, 5650000 ) 
  2. db.phones.find().limit(2) 
  3. "_id" : 18005550000, "components" : { "country" : 1, "area" : 800, "prefix" : 555, "number" : 5550000 }, "display" : "+1 800-5550000" } 
  4. "_id" : 88005550001, "components" : { "country" : 8, "area" : 800, "prefix" : 555, "number" : 5550001 }, "display" : "+8 800-5550001" } 

 

查看数据库中的索引:

 
  
  1. db.system.indexes.find() 
  2. "name" : "_id_""ns" : "book.phones""key" : { "_id" : 1 } } 

 

使用explain()函数查看执行计划:

 
  
  1. db.phones.find({display: "+1 800-5650001"}).explain() { 
  2. "cursor" : "BasicCursor""nscanned" : 109999, "nscannedObjects" : 109999, "n" : 1, 
  3.   "millis" : 52, 
  4.   "indexBounds" : { 
  5.   } 

 

创建索引:

 
  
  1. db.phones.ensureIndex( 
  2.   { display : 1 }, 
  3.   { unique : true, dropDups : true } 

 

再次执行查询计划,看看两次的nscanned的区别:

 

 
  
  1. db.phones.find({ display: "+1 800-5650001" }).explain() 
  2. "cursor" : "BtreeCursor display_1""nscanned" : 1, 
  3. "nscannedObjects" : 1, 
  4. "n" : 1, 
  5. "millis" : 0, 
  6. "indexBounds" : { 
  7.     "display" : [ 
  8.       [ 
  9. "+1 800-5650001"
  10.         "+1 800-5650001" 
  11. ] ] 
  12. } } 

 

查看执行较慢的query:

 
  
  1. db.system.profile.find() 
  2. "ts" : ISODate("2011-12-05T19:26:40.310Z"), "op" : "query"
  3. "ns" : "book.phones"
  4. "query" : { "display" : "+1 800-5650001" }, "responseLength" : 146, 
  5. "millis" : 0, 
  6. "client" : "127.0.0.1""user" : "" 

profile中默认是记录超过100ms的查询,可以通过db.setProfilingLevel(2)来设置记录的级别

 

建立索引通常是比较耗时的,建议在quiet time进行,最好在后台执行,如下:

 
  
  1. db.phones.ensureIndex({ "components.area": 1 }, { background : 1 }) 

 

查看某一个数据库的所有索引:

 
  
  1. db.system.indexes.find({ "ns" : "book.phones" }) { 
  2. "name" : "_id_", 
  3. "ns" : "book.phones", "key" : { "_id" : 1 } 
  4. "_id" : ObjectId("4d2c96d1df18c2494fa3061c"), "ns" : "book.phones", 
  5. "key" : { "display" : 1 }, 
  6. "name" : "display_1", 
  7. "unique" : true, 
  8. "dropDups" : true 
  9.  
  10. "_id" : ObjectId("4d2c982bdf18c2494fa3061d"), "ns" : "book.phones", 
  11. "key" : { "components.area" : 1 }, 
  12. "name" : "components.area_1" 

 

集合查询:像关系型数据库一样,mongodb也为我们提供了丰富的结合查询函数

统计文档个数:

 
  
  1. db.phones.count({'components.number': { $gt : 5599999 } }) 
  2. 50000 

distinct(): 去重

 
  
  1. db.phones.distinct('components.number', {'components.number': { $lt : 5550005 } }) 
  2. [ 5550000, 5550001, 5550002, 5550003, 5550004 ] 

 

group():分组

 
  
  1. db.phones.group({ 
  2. initial: { count:0 }, 
  3. reduce: function(phone, output) { output.count++; }, cond: { 'components.number': { $gt : 5599999 } }, key: { 'components.area' : true } 
  4. }) 
  5. [ { "800" : 50000, "855" : 50000 } ] 

 

服务端命令:通常我们执行的这些命令都是在客户端执行的,mongodb为我们提供了一些在服务端执行命令的方式,以提高性能

eval:

 
  
  1. update_area = function() { db.phones.find().forEach( 
  2. function(phone) { phone.components.area++; phone.display = "+"
  3. phone.components.country+" "+ phone.components.area+"-"+ phone.components.number; 
  4.       db.phone.update({ _id : phone._id }, phone, false); 
  5.     } 
  6. ) } 
  7.  
  8. > db.eval(update_area) 

有一点需要注意的是:eval会阻塞mongod,所以不要太过频繁的执行,而且所执行的操作一定要快速能够执行完毕。

 

runCommand:

 
  
  1. > use admin 
  2. > db.runCommand("top") 
  3.  
  4. > db.runCommand({ "count" : "phones" }) { "n" : 100000, "ok" : 1 }