一个mongod服务可以有建立多个数据库,每个数据库可以有多张表,这里的表名叫collection,每个collection可以存放多个文档document,每个文档都以BSON(binary json)的形式存放于硬盘中,因此可以存储比较复杂的数据类型。它是以单文档为单位存储的,你可以任意给一个或一批文档新增或删除字段,而不会对其它文档造成影响,这就是所谓的schema-free,这也是文档型数据库最主要的优点。跟一般的key-value数据库不一样的是,它的value中存储了结构信息,所以你又可以像关系型数据库那样对某些域进行读写、统计等操作。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。Mongo还可以解决海量数据的查询效率,根据官方文档,当数据量达到50GB以上数据时,Mongo数据库访问速度是MySQL10 倍以上。
BSON是Binary JSON 的简称,是一个JSON文档对象的二进制编码格式。BSON同JSON一样支持往其它文档对象和数组中再插入文档对象和数组,同时扩展了JSON的数据类型。如:BSON有Date类型和BinDate类型。
BSON有以下三个特点: 轻量级、跨平台、效率高
use admin #进入数据库admin
db.addUser('name','pwd') #增加或修改用户密码
db.system.users.find() #查看用户列表
db.auth('name','pwd') #用户认证
db.removeUser('name') #删除用户
show users #查看所有用户
show dbs #查看所有数据库
show collections #查看所有的collection
db.printCollectionStats() #查看各collection的状态
db.printReplicationInfo() #查看主从复制状态
db.repairDatabase() #修复数据库
db.setProfilingLevel(1) #设置记录profiling,0=off 1=slow 2=all
show profile #查看profiling
db.copyDatabase('mail_addr','mail_addr_tmp') #拷贝数据库
db.mail_addr.drop() #删除collection
db.dropDatabase() #删除当前的数据库
插入主要有save和insert,它们的区别是,当传入给save的记录含有_id字段时,save会做更新操作,否则和insert一样做插入操作
db.foo.insert({'name':'tony'}) #插入一条记录,也可以批量插入,即传递一个数组即可
db.foo.save({'name':'ysz','address':{'city':'beijing','post':100096},'phone':[138,139]}) #存储嵌套的对象
db.user_addr.save({'Uid':'[email protected]','Al':['[email protected]','[email protected]']}) #存储数组对象
db.foo.remove({'yy':5}) #删除yy=5的记录
db.foo.remove() #删除一个集合所有的记录,集合本身和索引不删除
db.foo.update({'yy':5},{'$set':{'xx':2}},upsert=true,multi=true) #根据query条件修改,如果不存在则插入,允许修改多条记录
db.foo.update({'name':'joe'},{'$unset':{'email':1}}) #找到name为joe的记录并删除email字段
findAndModify,这个命令能根据查询条件找到相应的记录并对一条结果做更新操作:
db.blog.findAndModify({'query':{'name':'jim'},'sort':{'age':1},'remove':true,'new':true});
其中query为查询条件,sort指定排序,remove表示删除(remove和update不能同时指定),new表示返回更新之前还是之后的结果,这条命令只能返回一条结果。
$gt : > ,$lt : < ,$gte: >= ,$lte: <= ,$ne : !=、<> ,$or,$in : in ,$nin: not in ,$all: all ,$not: 反匹配。
db.users.find({name: {$ne: "bruce"}, age: {$gte: 18}});
db.users.find({creation_date:{$gt:new Date(2010,0,1), $lte:new Date(2010,11,31)});
db.users.find({age: {$in: [20,22,24,26]}});
db.usets.find({'$or':[{'name':'jim','address':'china'}]});
db.users.find('this.age % 10 == 0');
db.users.find({age : {$mod : [10, 0]}});
db.users.find({favorite_number : {$all : [6, 8]}}); #多元素匹配,可以查询出{name: 'David', age: 26, favorite_number: [ 6, 8, 9 ] } ,查询不出{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] }
db.users.find({name: {$not: /^B.*/}});
db.users.find({age : {$not: {$mod : [10, 0]}}}); #查询 age取模10不等于0 的数据
find查询时会返回匹配记录的所有字段(_id字段总是会被返回),我们可以指定只返回需要的字段,比如:
db.users.find({}, {age:1}); #0为false,表示不返回, 非0为true,表示需要返回
db.users.find({}, {age:3});
db.users.find({}, {age:true});
db.users.find({ name : "bruce" }, {age:1});
db.users.find({ name : "bruce" }, {age:1, address:1});
db.users.find({}, {age:0, address:false});
db.users.find({ name : "bruce" }, {age:0, address:false});
字段值和类型相关查询:
db.users.find({favorite_number: {$size: 3}}); #匹配{name: 'David', age: 26, favorite_number: [ 6, 7, 9 ] }
db.users.find({name: {$exists: true}}); #查询所有存在name字段的记录
db.users.find({phone: {$exists: false}});#查询所有不存在phone字段的记录
db.users.find({name: {$type: 2}}); #查询所有name字段是字符类型的,http://docs.mongodb.org/manual/reference/operator/type/#_S_type 中有数据类型和代表数字的说明
db.users.find({age: {$type: 16}});#查询所有age字段是整型的
db.users.find({name: /^b.*/i});#查询以字母b或者B带头的所有记录
db.users.find({age: {$gt: 18}}); #查询 age > 18 的记录,以下查询都一样
db.users.find({$where: "this.age > 18"});
db.users.find("this.age > 18");
f = function() {return this.age > 18} db.users.find(f);
对结果排序
db.users.find().sort({age: 1}); #以年龄升序asc
db.users.find().sort({age: -1});#以年龄降序desc
限制返回数据条数:
db.users.find().limit(5); #返回5条记录
db.users.find().limit(3).forEach(function(user) {print('my age is ' + user.age)}); #返回3条记录并打印信息
db.users.find().skip(3).limit(5);#从第3条记录开始,返回5条记录(limit 3, 5)
db.users.find().count();
db.users.find({age:18}).count();
db.users.find().skip(10).limit(5).count(); #以下返回的不是5,而是user表中所有的记录数量
db.users.find().skip(10).limit(5).count(true);#如果要返回限制之后的记录数量,要使用count(true)或者count(非0)
返回一段数据:$slice
db.users.find({'name':'jim'},{'school':{'$slice':3}}) #返回jim读过学校的前三所
db.users.find({'name':'jim'},{'school':{'$slice':[2,3]}})
游标,主要用来做迭代
var cursor = db.people.find();
cursor.forEach(function(x){
print(x.name)
});
创建索引
db.users.ensureIndex({'firstname':1,'lastname':1},{'name':'idx_name'});
在字段firstname和lastname上创建了一个名为idx_name的联合索引,创建索引时值1表示索引结构中该字段升序排列,-1为倒序。
db.users.ensureIndex({'username':1},{'unique':true});
在字段username上创建了一个唯一索引,所以在插入重复的username值时会出错,需要注意的时,如果插入一条记录没有username字段时,该记录的username会被赋值为null并被加入到索引中,所以同样不能插入两条没有username字段的记录。
db.users.ensureIndex({'username':1},{'unique':true,'dropDups':true});
在创建唯一索引时添加dropDups字段表示如果有重复的索引字段,只保留第一条记录,其它记录会被删除。db.users.ensureIndex({'username':1},{'background':true});
在生产环境对已有数据添加索引时,注意加上background选项,它会让生成索引的过程非阻塞其它请求。db.foo.getIndexes()
db.foo.getIndexKeys()
删除索引
db.foo.dropIndex('username_1')
db.users.find().explain()
使用explain可以返回查询使用的索引、查询时间和扫描行数等相关统计信息,输出的信息里重点关注cursor,n,nscanned和millis四个数据,分别说面了查询是否使用了索引,查询返回的记录数,查询扫描的记录数和开销时间,在使用索引的情况下,n和nscanned数
值越接近说明索引效率越好。
db.users.find().hint({'username':1})
地理空间索引为针对坐标平面查询提供了专门的索引,可以很方便的找到距离当前位置最近的场所。
既然是针对平面坐标的索引,所以建立索引的键必须某种形式的一对值,比如说最常见的经度,维度。下面看看如何创建:
db.map.ensureIndex({'gps':'2d'},{'min':-1000,'max':1000})
注意,创建地理空间索引时,索引值不再是1或者-1,二是2d。min和max指定了平面的范围,默认是-180~180。
gps的值可以是包含两个值的数组或者内嵌文档,比如:
{'gps':[0,100]}
{'gps':{'x':-30,'y':30}}
根据位置查询:
db.map.find({'gps':{'$near':[10,10]}})
默认返回100条记录,结果按由近到远排序;
db.runCommand({geoNear:'map',near:[10,10],num:10})
geoNear命令还会返回每个文档到查询点的距离。
db.map.find({gps:{$within:{$box:[[0,0],[35,35]]}}})
db.map.find({gps:{$within:{$center:[[0,0],20]}}})
$box指定在一个矩形内查找,两个坐标指定了矩形的左下角和右上角。$center指定了在一个圆内查找,给定圆心和半径。