MongoDB数据库简单介绍
MongoDB是一个高性能 ,开源 ,无模式的文档型数据库,它在许多场景下可用于替代传统的关系型数据库或键/值存储模式。MongoDB是用C++开发, 提供了以下功能:
1.面向集合的存储:适合存储对象及JSON形式的数据。
2.动态查询:Mongo支持丰富的查询表达式。查询指令使用JSON形式的 标记,可轻易查询文档中内嵌的对象及数组。
3.完整的索引支持:包括文档内嵌对象及数组。Mongo的查询优化 器会分析查询表达式,并生成一个高效的查询计划。
4.查 询监视:Mongo包含一个监视工具 用于分析数据库操作的性能。
5.复制 及自动故障转移:Mongo数据库支持服务器 之间的数据复制,支持主-从模式及服务 器之间的相互复制。复制的主要目标是提供冗余及自 动故障转移。
6.高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)。
7.自动分片以支持云级别的伸缩性(处于 早期alpha阶段):自动分片功能支持水平的数据库集群 ,可动态添加额外的机器。
MongoDB的主要目标是在键/值存储方式(提供了高性能和高度伸缩性)以及传统的RDBMS系统 (丰富的功能)架起一座桥梁,集两者的优势于一 身。根据官方网站的描述,Mongo 适合用于以下场景:
1.网站数据:Mongo非常适合实时的插入,更新与查询,并具备网站实时数据存储所需的复制及高度伸缩性。
2.缓存 :由于性能很高,Mongo也适合作为信息基础设施的缓存层。在系统重启之后,由Mongo搭建的持久化缓存层可以避免下层的数据源 过载。
3.大尺寸,低价值的数据:使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很 多时候程序员往往会选择传统的文件 进行存储。
4.高伸缩性的场景:Mongo非常适合由数十或数百台服务器组成的数据库。Mongo的路线图中已经包含对MapReduce引擎的内置支持。
5.用于 对象及JSON数据的存储:Mongo的BSON数据格式非常适合文档化格式的存储 及查询。
自然,MongoDB的使用也会有一些限制,例如它不适合:
1.高度事务性的系统:例如银行或会计系统。传统的关系型数据库目前还是更适用于需要大量原子性复杂事务的应用 程序。
2.传统的商业智能应用:针对特定问题的BI数据库会对产生高度优化的查询方式。对于此类应用,数据仓库可能是更合适的选择.
3.需要SQL的问题
MongoDB支持OS X、Linux及Windows等操作系统 ,并提供了Python,PHP,Ruby,Java,C,C#,Javascript,Perl及C++语言的驱动程序,社区中也提供了对Erlang 及.NET等平台的驱动程序
第一讲
1.创建一个数据库
use foobar
2.查看所有数据库
show dbs
3.给指定数据库添加集合并且添加记录
db.persons.insert({name:"uspcat"})
db.persons.insert({name:"extjs4.0"})
4.查看数据库中的所有集合
show collections
5.查询指定集合的数据
db.system.indexes.find()
db.persons.find()
db.persons.findOne()
6.更新文档数据
db.persons.update({name:"extjs4.0"},{$set:{name:"extjs4.1"}})
db.persons.update({name:"extjs4.1"},{$set:{age:1,name:"extjs4.2"}})
update persons set name = 'extjs4.1' where name = 'extjs4.0'
var p = db.persons.findOne()
db.persons.update(p,{name:"uspcat2"})
var p = db.persons.findOne()
p
7.删除集合中的数据
db.persons.remove({name:"extjs4.2"})
8.删除数据库中的集合
db.persons.drop()
show collections
9.删除数据库
db.dropDatabase()
show dbs
10.shell的help
db.help()
DB methods:
db.addUser(username, password) 添加数据库授权用户
db.auth(username, password) 访问 认证
db.cloneDatabase(fromhost) 克隆数据库
db.commandHelp(name) returns the help for the command
db.copyDatabase(fromdb, todb, fromhost) 复制数据库
db.createCollection(name, { size : ..., capped : ..., max : ... } ) 创建表
db.currentOp() displays the current operation in the db
db.dropDatabase() 删除当前数据库
db.eval(func, args) run code server-side
db.getCollection(cname) same as db['cname'] or db.cname
db.getCollectionNames() 获取当前数据库的表名
db.getLastError() - just returns the err msg string
db.getLastErrorObj() - return full status object
db.getMongo() get the server connection object
db.getMongo().setSlaveOk() allow this connection to read from the nonmaster member of a replica pair
db.getName()
db.getPrevError()
db.getProfilingLevel()
db.getReplicationInfo()
db.getSisterDB(name) get the db at the same server as this onew
db.killOp() kills the current operation in the db
db.printCollectionStats() 打印各表的状态信息
db.printReplicationInfo() 打印主数据库的复制状态信息
db.printSlaveReplicationInfo() 打印从数据库的复制状态信息
db.printShardingStatus() 打印分片状态信息
db.removeUser(username) 删除数据库用户
db.repairDatabase() 修复数据库
db.resetError()
db.runCommand(cmdObj) run a database command. if cmdObj is a string, turns it into { cmdObj : 1 }
db.setProfilingLevel(level) 0=off 1=slow 2=all
db.shutdownServer ()
db.version() current version of the server
如果想知道当前数据库下的表或者表 collection支持哪些方法,可以使用一下命令如:
db.user.help(); user为表名
DBCollection help
db.foo.count() 统计表的行数
db.foo.dataSize() 统计表数据的大小
db.foo.distinct( key ) - eg. db.foo.distinct( 'x' ) 按照给定的条件除重
db.foo.drop() drop the collection 删除表
db.foo.dropIndex(name) 删除指定索引
db.foo.dropIndexes() 删除所有索引
db.foo.ensureIndex(keypattern,options) - options should be an object with these possible fields: name, unique, dropDups 增加索引
db.foo.find( [query] , [fields]) - first parameter is an optional query filter. second parameter is optional set of fields to return. 根据条件查找数据 e.g. db.foo.find( { x : 77 } , { name : 1 , x : 1 } )
db.foo.find(...).count()
db.foo.find(...).limit(n) 根据条件查找数据并返回指定记录数
db.foo.find(...).skip(n)
db.foo.find(...).sort(...) 查找排序
db.foo.findOne([query]) 根据条件查询只查询一条数据
db.foo.getDB() get DB object associated with collection 返回表所属的库
db.foo.getIndexes() 显示表的所有索引
db.foo.group( { key : ..., initial: ..., reduce : ...[, cond: ...] } ) 根据条件分组
db.foo.mapReduce( mapFunction , reduceFunction , <optional params> )
db.foo.remove(query) 根据条件删除数据
db.foo.renameCollection( newName ) renames the collection 重命名表
db.foo.save(obj) 保存数据
db.foo.stats() 查看表的状态
db.foo.storageSize() - includes free space allocated to this collection 查询分配到表空间大小
db.foo.totalIndexSize() - size in bytes of all the indexes 查询所有索引的大小
db.foo.totalSize() - storage allocated for all data and indexes 查询表的总大小
db.foo.update(query, object[, upsert_bool]) 根据条件更新数据
db.foo.validate() - SLOW 验证表的详细信息
db.foo.getShardVersion() - only for use with sharding
11.mongoDB的API
http://api.mongodb.org/
mongoDB的shell内置javascript引擎可以直接执行js代码
use db-text
db.getCollection("db-text").text.insert({name:123123})
db.getCollection("db-text").text.find()
function insert(object){
db.getCollection("db-text").text.insert(object)
}
insert({name:"yun"})
shell可以用eval
db.eval("return 'mongodb'")
第二讲
1.插入文档
db.persons.insert({_id:"001",name:"yun"})
2.批量插入文档
for(var i=0;i<10;i++){
db.persons.insert({name:i})
}
3.save操作
save操作和insert操作区别在于当遇到_id相同的情况下,save完成保存操作,insert则会报错
db.persons.save({_id:"001",name:11})
1.删除列表中的所有数据
db.persons.remove()集合的本身和索引不会被删除
2.根据指定条件删除
db.persons.remove({_id:"001"})
3.小技巧
如果你想清除一个数据量十分庞大的集合,直接删除该集合并且重新建立索引的办法(drop)比直接用remove的效率会高很多
1.强硬的文档替换式更新操作
db.[documentName].update({查询器},{修改器})
强硬的更新会用新的文档代替老的文档
2.主键冲突的时候会报错并且停止更新操作
db.persons.update({_id:2},{_id:3,name:3})
因为是强硬替换当替换的文档和已有文档ID冲突的时候,则系统会报错
3.insertOrUpdate操作
目的:查询器查出来数据就执行更新操作,查不出来就替换操作
做法:db.[documentName].update({查询器},{修改器},true)
db.persons.update({_id:4},{_id:4,name:4},true)
4.批量更新操作
默认情况当查询器查询出多条数据的时候默认就修改第一条数据
如何实现批量修改
db.[documentName].update({查询器},{修改器},false, true)
db.persons.update({name:"3"},{$set:{name:"33"}},false,true)
5.使用修改器来完成局部更新操作
$set 它用来指定一个键值对,如果存在键就进行修改不存在则进行添加.
db.persons.update({name:4},{$set:{age:27}})
$inc 只是使用与数字类型,他可以为指定的键对应的数字类型的数值进行加减操作.
db.persons.update({age:27},{$inc:{age:1}})
$unset 他的用法很简单,就是删除指定的键
db.persons.update({age:28},{$unset:{age:1}})
$push 1.如果指定的键是数组增追加新的数值 2.如果指定的键不是数组则中断当前操作Cannot apply $push/$pushAll modifier to non-array 3.如果不存在指定的键则创建数组类型的键值对
db.persons.insert({_id:5,name:5,books:[]})
db.persons.update({_id:5},{$push:{books:"js"}})
db.persons.update({_id:5},{$push:{books:"extjs4"}})
db.persons.update({_id:5},{$push:{class:"01class"}})
db.persons.update({_id:5},{$push:{name:"01class"}})
$pushAll 用法和$push相似他可以批量添加数组数据
db.persons.update({_id:5},{$pushAll:{class:["02","03","04"]}})
$addToSet 目标数组存在此项则不操作,不存在此项则加进去
db.persons.update({_id:5},{$addToSet:{books:"extjs"}})
$pop 从指定数组删除一个值1删除最后一个数值,-1删除第一个数值
db.persons.update({_id:5},{$pop:{books:-1}})
db.persons.update({_id:5},{$pop:{books:1}})
$pull 删除一个被指定的数值
db.persons.update({_id:5},{$pull:{books:"js"}})
$pullAll 一次性删除多个指定的数值
db.persons.update({_id:5},{$pullAll:{books:["extjs4","mongodb"]}})
$ 1.数组定位器,如果数组有多个数值我们只想对其中一部分进行操作我们就要用到定位器($) 例子:
例如有文档{name:”YFC”,age:27,books:[{type:’JS’,name:”EXTJS4”},{type:”JS”,name:”JQUERY”},{type:”DB”,name:”MONGODB”}]}
我们要把type等于JS的文档增加一个相同的作者author是USPCAT
办法:
db.persons.insert({_id:6,name:"YFC",age:27,books:[{type:"js",name:"extjs4"},{type:"js",name:"jquery"},{type:"db",name:"mongodb"}]})
db.persons.update({"books.type":"js"},{$set:{"books.$.author":"USPCAT"}}
6. $addToSet与$each结合完成批量数组更新
$each会循环后面的数组把每一个数值进行$addToSet操作
db.persons.update({_id:5},{$addToSet:{books:{$each:["js","db"]}}})
7.存在分配与查询效率
当document被创建的时候DB为其分配内存和预留内存,当修改操作不超过预留内层的时候则速度非常快,反而超过了就要分配新的内存则会消耗时间
6. runCommand函数和findAndModify函数
runCommand可以执行mongoDB中的特殊函数
findAndModify就是特殊函数之一他的用于是返回update或remove后的文档
ps = db.runCommand({
"findAndModify":"persons",
"query":{"name":"333"},
"update":{"$set":{"age":100}},
"new":true
})
findAndModify的调用方式和普通的更新略有不同,还有点慢,这是因为它要等待数据库的响应。这对于操作查询以及执行其他需要取值和赋值风格的原子性操作来说是十分方便的。findAndModify命令中每个键对应的值如下所示。
findAndModify 字符串,集合名。
query 查询文档,用来检索文档的条件。
sort 排序结果的条件。
update 修改器文档,对所找到的文档执行的更新。
remove 布尔类型,表示是否删除文档。
new 布尔类型,表示返回的是更新前的文档还是更新后的文档。默认是更新前的文档。
"update"和"remove"必须有一个,也只能有一个。要是匹配不到文档,这个命令会返回一个错误。
这个命令有些限制。它一次只能处理一个文档,也不能执行upsert操作,只能更新已有文档。
相比普通更新来说,findAndModify速度要慢一些。大概耗时相当于一次查找,一次更新和一次getLastError顺序执行所需的时间。
db.runCommand("findAndModify":集合名,"query":{查询条件},"upadte":{修改器})
第三讲
Find 详讲
1.指定返回的键
db.[documentName].find ({条件},{键指定})
数据准备persons.json
1.1 查询出所有数据的指定键(name ,age ,country)
db.persons.find({},{_id:0,name:1,country:1})
2.查询条件
2.查询条件
2.1查询出年龄在25到27岁之间的学生
db.persons.find({age:{$gte:25,$lte:27}},{_id:0,name:1,age:1})
2.2查询出所有不是韩国籍的学生的数学成绩
db.persons.find({country:{$ne:"Korea"}},{_id:0,m:1,country:1})
3.包含或不包含
$in或$nin
2.3查询国籍是中国或美国的学生信息
db.persons.find({country:{$in:["USA","China"]}},{_id:0,m:1,country:1})
2.4查询国籍不是中国或美国的学生信息
db.persons.find({country:{$nin:["USA","China"]}},{_id:0,m:1,country:1})
4.OR查询
$or
2.4查询语文成绩大于85或者英语大于90的学生信息
db.persons.find({$or:[{c:{$gte:85}},{e:{$gte:90}}]},{_id:0,c:1,e:1,name:1})
5.Null 把中国国籍的学生上增加新的键sex
db.persons.update({country:"China"},{$set:{sex:"m"}},false,true)
2.5查询出sex 等于 null的学生
db.persons.find({sex:{$in:[null]}},{_id:0,name:1,sex:1})
6.正则查询
2.6查询出名字中存在”li”的学生的信息
db.persons.find({name:/li/},{_id:0,name:1,sex:1})
7.$not的使用
$not可以用到任何地方进行取反操作
2.7查询出名字中不存在”li”的学生的信息
db.persons.find({name:{$not:/li/}},{_id:0,name:1,sex:1})
$not和$nin的区别是$not可以用在任何地方儿$nin是用到集合上的
8.数组查询$all和index应用
2.8查询喜欢看MONGOD和JS的学生
db.persons.find({books:{$all:["JS","MONGODB"]}},{_id:0,name:1,sex:1})
2.9查询第二本书是JAVA的学习信息
db.persons.find({"books.1":"JAVA"},{_id:0,name:1,sex:1})
9.查询指定长度数组$size它不能与比较查询符一起使用(这是弊端)
2.8查询出喜欢的书籍数量是4本的学生
db.persons.find({books:{$size:4}},{_id:0,books:1})
2.9查询出喜欢的书籍数量大于3本的学生
1.增加字段size
db.persons.update({},{$set:{size:4}},false, true)
2.改变书籍的更新方式,每次增加书籍的时候size增加1
db.persons.update({查询器},{$push:{books:”ORACLE”},$inc:{size:1}})
db.persons.update({name:"jim"},{$push:{books:"ORACLE"},$inc:{size:1}})
3.利用$gt查询
db.persons.find({size:{$gt:3}})
db.persons.find({size:5},{_id:0,name:1,sex:1})
2.10利用shell查询出Jim喜欢看的书的数量
var persons = db.persons.find({name:"jim"})
while(persons.hasNext()){
obj = persons.next();
print(obj.books.length)
}
课间小结
1.mongodb 是NOSQL数据库但是他在文档查询上还是很强大的
2.查询符基本是用到花括号里面的更新符基本是在外面
3.shell是个彻彻底底的JS引擎,但是一些特殊的操作要靠他的各个驱动包来完成(JAVA,NODE.JS)
10.$slice操作符返回文档中指定数组的内部值
2.11查询出Jim书架中第2~4本书
db.persons.find({name:"jim"},{books:{$slice:[1,3]}})
2.12查询出最后一本书
db.persons.find({name:"jim"},{books:{$slice:-1},_id:0})
11.文档查询
为jim添加学习简历文档 jim.json
2.13查询出在K上过学的学生
1. 这个我们用绝对匹配可以完成,但是有些问题(找找问题?顺序?总要带着score?)
db.persons.find({school:{school:"K",score:"A"}},{_id:0,school:1})
2.为了解决顺序的问题我可以用对象”.”的方式定位
db.persons.find({"school.score":"A","school.school":"K"},{_id:0,school:1})
3.这样也问题看例子:
db.persons.find({"school.score":"A","school.school":"J"},{_id:0,school:1})
同样能查出刚才那条数据,原因是score和school会去其他对象对比
4.正确做法单条条件组查询$elemMatch
db.persons.find({school:{$elemMatch:{school:"K",score:"A"}}},{_id:0,school:1})
12.$where
12.查询年龄大于22岁,喜欢看C++书,在K学校上过学的学生信息
复杂的查询我们就可以用$where因为他是万能,但是我们要尽量避免少使用它因为他会有性能的代价
db.persons.find({"$where":function(){
//得到查询结果的每一条文档
var books = this.books;
//得到文档中的school对象
var school = this.school;
//如果年纪>=22
if(this.age > 22){
var php = null;
//遍历书籍
for ( var i = 0; i < books.length; i++) {
if(books[i] == "C++"){
php = books[i];
//如果学校是真
if(school){
for (var j = 0; j < school.length; j++) {
//判断是不是在K上学
if(school[j].school == "K"){
//返回是真
return true;
}
}
break;
}
}
}
}
}})
分页与排序
1.Limit返回指定的数据条数
1.1查询出persons文档中前5条数据
db.persons.find({},{_id:0,name:1}).limit(5)
2.Skip返回指定数据的跨度
2.1查询出persons文档中5~10条的数据
db.persons.find({},{_id:0,name:1}).limit(5).skip(5)
3.Sort返回按照年龄排序的数据[1,-1]
db.persons.find({},{_id:0,name:1,age:1}).sort({age:1})
注意:mongodb的key可以存不同类型的数据排序就也有优先级
最小值
null
数字
字符串
对象/文档
数组
二进制
对象ID
布尔
日期
时间戳
正则
最大值
4.Limit和Skip完成分页
4.1三条数据位一页进行分页
第一页?db.persons.find({},{_id:0,name:1}).limit(3).skip(0)
第二页?db.persons.find({},{_id:0,name:1}).limit(3).skip(3)
4.2skip有性能问题,没有特殊情况下我们也可以换个思路
对文档进行重新结构设计
每次查询操作的时候前后台传值全要把上次的最后一个文档的日期保存下来
db.persons.find({date:{$gt:日期数值}}).limit(3)
个人建议---应该把软件的中点放到快捷和精确查询上而不是分页的性能上
因为用户最多不会翻查过2页的
游标和其他知识
1.游标 利用游标遍历查询数据
var persons = db.persons.find();
while(persons.hasNext()){
obj = persons.next();
print(obj.name)
}
2.游标几个销毁条件
1.客户端发来信息叫他销毁
2.游标迭代完毕
3.默认游标超过10分钟没用也会被清除
3.查询快照
快照后就会针对不变的集合进行游标运动了,看看使用方法.
db.persons.find({name:"jim"},{_id:0,name:1,age:1})
db.persons.find({$query:{name:"jim"}},{_id:0,name:1,age:1})
高级查询选项
$query
$orderby
$maxsan:integer 最多扫描的文档数
$min:doc? 查询开始
$max:doc? 查询结束
$hint:doc?? 使用哪个索引
$explain:boolean? 统计
$snapshot:boolean 一致快照
4.为什么有的时候要用查询快照?看图.