概述
一个mongod服务可以有建立多个数据库,每个数据库可以有多张表,这里的表名叫collection,每个collection可以存放多个文档document,每个文档都以BSON(binary json)的形式存放于硬盘中,因此可以存储比较复杂的数据类型。它是以单文档为单位存储的,你可以任意给一个或一批文档新增或删除字段,而不会对其它文档造成影响,这就是所谓的schema-free,这也是文档型数据库最主要的优点。跟一般的key-value数据库不一样的是,它的value中存储了结构信息,所以你又可以像关系型数据库那样对某些域进行读写、统计等操作。Mongo最大的特点是他支持的查询语言非常强大,其语法有点类似于面向对象的查询语言,几乎可以实现类似关系数据库单表查询的绝大部分功能,而且还支持对数据建立索引。Mongo还可以解决海量数据的查询效率,根据官方文档,当数据量达到50GB以上数据时,Mongo数据库访问速度是MySQL10 倍以上。
BSON
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':'yushunzhi@sohu.com','Al':['test-1@sohu.com','test-2@sohu.com']}) #存储数组对象
删除
- 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字段
修改器
- $set 和 $unset:修改字段值和删除字段;
- $rename,修改字段名字,db.students.update({_id:1},{$rename:{'nickname':'alias','cell':'mobile'}})
- $inc:数字类型字段增减,db.user.update({'name':'owen'},{'$inc':{'age':1}});
- $push和$pushAll:向数组类型末尾追加数据,字段不存在则创建;
- $addToSet:向数组类型追加数据时避免重复;
- $each:遍历数组;
- $pop:从数组中删除数据,{$pop:{key:1}}从末尾删除数据,-1从头部删除;
- $pull和$pullAll:从数组中删除指定数据,db.list.update({},{$pull:{'todo':'undo'}});
- 数组定位修改器:数组可以通过下标定位,也可以通过’$‘定位查询匹配到的记录,db.blog.update({'comments.author':'joe'},{'$set':{'comments.$.author':'jim'}});
返回已更新的文档
findAndModify,这个命令能根据查询条件找到相应的记录并对一条结果做更新操作:
- db.blog.findAndModify({'query':{'name':'jim'},'sort':{'age':1},'remove':true,'new':true});
查询
$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 的数据
- 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'});
- db.users.ensureIndex({'username':1},{'unique':true});
- db.users.ensureIndex({'username':1},{'unique':true,'dropDups':true});
- db.users.ensureIndex({'username':1},{'background':true});
查看索引
- db.foo.getIndexes()
- db.foo.getIndexKeys()
- db.foo.dropIndex('username_1')
使用explain和hint
- db.users.find().explain()
- db.users.find().hint({'username':1})
地理空间索引
地理空间索引为针对坐标平面查询提供了专门的索引,可以很方便的找到距离当前位置最近的场所。
既然是针对平面坐标的索引,所以建立索引的键必须某种形式的一对值,比如说最常见的经度,维度。下面看看如何创建:
- db.map.ensureIndex({'gps':'2d'},{'min':-1000,'max':1000})
gps的值可以是包含两个值的数组或者内嵌文档,比如:
- {'gps':[0,100]}
- {'gps':{'x':-30,'y':30}}
- db.map.find({'gps':{'$near':[10,10]}})
- db.runCommand({geoNear:'map',near:[10,10],num:10})
除了可以距离查找外还可以范围查找:
- db.map.find({gps:{$within:{$box:[[0,0],[35,35]]}}})
- db.map.find({gps:{$within:{$center:[[0,0],20]}}})