MongoDB学习笔记(一)
来源:互联网 发布:无法安装java 编辑:程序博客网 时间:2024/06/06 01:10
虽然在TTMS这个项目中,我已经使用了MongoDB数据库,但是我觉得我还不是很了解它,所以在项目结束的第一天,我打算重新认识MongoDB
一.认识MongoDB
NoSQL
NoSQL(Not Only SQL,非关系型数据库),主要特点是非关系型的,分布式的,开源的,水平可扩展的。
关系型数据库,我目前只使用过MySQL,SQL Server,但是关系型和非关系型两种在使用的时候的差别就比较明显了
NoSQL特点:
- 可以处理超大量的数据
- 它运行在便宜的PC服务器集群上
- 它击碎了性能瓶颈
- 它没有过多的操作
- 它的支持者源于社区
MongoDB特点及功能
MongoDB最大的特点是支持的查询语言非常强大,其语法有点类似关系数据库单表查询的绝大部分功能,而且支持对数据建立索引,它是一个面向集合的,模式自由的文档型数据库
面向集合
数据被分组存储在数据集中,被称为一个集合,每个集合在数据库中都有一个唯一的标识名,并且可以包含无限数目的文档,集合的概念类似关系型数据库(RDBMS)里的表(table),不同的是它不需要定义任何模式(schema)
模式自由
意味着对于存储在MongoDB数据库中的文件,你不需要知道它的任何结构定义,这点我在使用过程中也深有体会,保存在MongoDB数据库中某个集合下的文档它的键可以是不一样的
{“welcome” : “Beijing”}
{“age” : 25}文档型
我们存储的数据是键-值对的集合,键是字符串,值可以是数据类型集合里的任何类型,包括数组和文档,我们把这个数据格式称为“BSON”,即“Binary serialized document notation”
MongoDB的特点:
- 面向集合存储,易于存储对象类型的数据
- 模式自由
- 支持动态查询
- 支持完全索引,包含内部对象
- 支持查询
- 支持复制和故障恢复
- 使用高效的二进制数据存储,包括大型对象(如视频等)
- 自动处理碎片,以支持云计算层次的可扩展性
- 支持Python,PHP,Ruby,Java,C,C#,JavaScript,Perl及C++语言的驱动程序,社区也提供了对Erlang及.NET等平台的驱动程序
- 文件存储格式为BSON(一种JSON的扩展)
- 可通过网络访问
MongoDB的功能
- 面向集合的存储:适合存储对象及JSON形式的数据
- 动态查询:MongoDB支持丰富的查询表达式,查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组
- 完整的索引支持,支持文档内嵌套对象和数组,MongoDB的查询优化器会分析查询表达式,生成一个高效的查询计划
- 查询监视:MongoDB包含一系列的监视工具用于分析数据库操作的性能
- 复制和自动故障转移:MongoDB数据库支持服务器之间的数据复制,支持主-从模式及服务器之间的相互复制,复制的主要目标是提供冗余及自动故障转移
- 高效的传统存储方式:支持二进制数据及大型对象(如照片或图片)
- 自动分片以支持云级别的伸缩性:自动分片功能支持水平的数据库集群,可动态添加额外的机器
MongoDB适用场合
- 网站数据:MongoDB非常适合实时的插入,更新和查询,并具备网站实时数据存储所需的复制及高度伸缩性
- 缓存:由于性能很高,MongoDB也适合作为信息基础设施的缓存层,在系统重启之后,由MongoDB搭建的持久性缓存层可以避免下层的数据源过载
- 大尺寸,低价值的数据,使用传统的关系型数据库存储一些数据时可能会比较昂贵,在此之前,很多时候程序员往往会选择传统的文件进行存储
- 高伸缩性的场景:MongoDB非常适合数十或数百台服务器组成的数据库,MongoDB的高线路图中已经包含对MapReduce引擎的内置支持
- 用于对象及JSON数据的存储:MongoDB的BSON数据格式非常适合文档化格式的存储及查询
MongoDB是一个可移植的数据库,在主流的操作系统上都可以使用,及跨平台特性,MongoDB Shell是MongoDB自带的交互式JavaScript Shell,是对MongoDB执行操作和管理的交互式环境
二.MongoDB入门
体系结构
一个运行着的MongoDB可以看作一个MongoDB Server,该Server由实例和数据库组成。一般情况下,一个MongoDB。一般情况下,一个MongoDB Server机器上包含一个实例和多个与之对应的数据库,但是在特殊情况下,如特殊的情况下,如硬件投入成本有限或特殊的应用需求,也允许一个Server机器上有多个实例和数据库
MongoDB中一系列物理文件(数据文件、日志文件等)的集合或与之对应的逻辑结构(集合、文档等)称为数据库。简单来说,数据库是由一系列与磁盘有关的物理文件组成的。
数据逻辑结构
MongoDB数据逻辑结构是面向用户的,用户使用MongoDB开发应用程序使用的是逻辑结构,MongoDB逻辑结构是一种层次结构,由文档(document)、集合(collection)、数据库(database)三部分组成,它们之间三部分的关系如下:
- MongoDB的文档相当于关系数据库中的一条记录
- 多个文档组成一个集合,相当于关系数据库中的表
- 多个集合逻辑上组织在一起就是数据库
- 一个MongoDB实例支持多个数据库
数据库的每张表都对应一个命名空间,每个索引也有对应的命名空间,MongoDB 内部有预分配空间的机制,每个预分配的文件都用 0 进行填充,由于有了这个机制, MongoDB 始终保持额外的空间和空余的数据文件,从而有效避免了由于数据暴增而带来的磁盘压力过大的问题
由于表中数据量的增加,数据文件每新分配一次,它的大小都会是上一个数据文件大小的 2倍,每个数据文件最大 2G。这样的机制有利于防止较小的数据库浪费过多的磁盘空间,同时又能保证较大的数据库有相应的预留空间使用。
数据类型
MongoDB中的文档可以理解为JSON类型的对象,MongoDB也支持很多额外的数据结构
number类型:包括四字节整数、八字节整数、八字节浮点数
date类型:表示日期类型,使用的时候,要new Date()
,而不是Date()
,后者返回的是一个字符串,而不是一个Date对象,通过MongoDB Shell查看可得到下面的结果
Shell 里用本地区域设置来显示时间,但在数据库里只是记录从纪元开始到现在所过去的毫秒数,所以并没有把区域设置保存进去
array:数组的值可以进行有序处理,类似列表、堆栈、队列;也可以无序处理,类似于集合。这个数组和JavaScript的数组还是有些类似的,比如数组里面的各个元素的类型可以是不一致的,MongoDB还允许数组内部元素的原子更新
embedded document:嵌套文档就是将一个key的值表示为另外一个文档,这样可以让document看起来比一个平面的结构更加自然
操作数据库
如下面所示,通过Shell进行了插入记录和查看记录的操作
1.插入操作
从此也可以看出:
MongoDB是不需要预先创建一个集合,在第一次插入数据时就会自动创建
在文档中可以存储任何结构的数据,但在实际应用中存储的还是相同类型文档的集合,此特征很灵活,不需要类似alter table语句来修改数据结构
每次插入数据时,集合中都会有一个ID(_id)
可以用for循环来插入数据:
MongoDB支持的数据类型中,_id是其自有产物,存储在MongoDB集合中的每个文档(document)都有一个默认的主键_id,这个主键名称是固定的,它可以是MongoDB支持的任何数据类型,默认是ObjectId。在关系数据库Schema设计中,主键大多数是数值型,在MongoDB在设计之初就定位于分布式存储系统,所以它不支持自增主键。
2.查询操作
find的使用:
游标风格的输出,hasNext()函数用于判断是否还有数据,如果有则调用Next()函数将数据取出来,当使用的是JavaScript Shell时就可以使用forEach循环输出数据,但forEach()必须定义一个函数供每个游标元素调用,如下面:
也可以把游标当成是数组来使用:
使用游标的时候要注意占用内存的问题,特别是很大的游标对象,有可能会内存溢出,所以应该用迭代的方式来输出,下面就将游标转换成真实的数组对象类型:
3.条件查询
> db.things.find({num: 4}).forEach(printjson)
{ “_id” : ObjectId(“593ce88c1991bcbd477f7a02”), “num” : 4 }
也可以指明只输出某列的值:
> db.things.find({x : 1}, {y:true}).forEach(printjson)
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “y” : 2 }
findOne
为了避免游标可能带来的开销,提供了一个findOne()函数,这个函数和find()函数一样,不过它返回的是游标中的第一条数据,或者返回null,即空数据
> printjson(db.things.findOne({y : 3}))
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3 }
limit限制结果集数量
在需要限制结果集的长度,可以调用limit方法
> db.things.find().limit(3)
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a14”), “x” : 3, “y” : 4 }
这个可以解决性能问题,通过限制条数来减少网络传输,比如分页
4.修改记录
> db.things.update({x : 3}, {$set:{x : 100}})
WriteResult({ “nMatched” : 1, “nUpserted” : 0, “nModified” : 1 })
> db.things.findOne({x:100})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a14”), “x” : 100, “y” : 4 }
5.删除记录
删除表的记录可以通过remove方法
> db.things.remove({x: 100})
WriteResult({ “nRemoved” : 1 })
> db.things.findOne({x:100})
null
三.高级查询
查询操作符
1.条件查询符
常用的条件查询符:<( $lt) 、<=($lte)、>($gt )、>=($gte)
db.things.find({“x”: {$gt: 3}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
db.things.find({x:{\$lt: 5}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
3< x < 5
db.things.find({x: {$gt: 3, $lt:5}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
2. $all匹配所有
这个与SQL语法中的in类似,不同的是in只需要匹配括号内的某一个值,而$all必须满足括号中的所有值,如下面的代码所示:
> db.things.find({x: {$all:[1,1]}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2 }
3.$exists判断字段是否存在
此操作符用判断某个字段是否存在
> db.things.find({x : {$exists:true}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
如果查询所有不存在某个字段的记录,只需要将true改为false就可以了
4. null值处理
此操作符用于处理null值,null的处理稍微复杂
> db.things.find({age: null})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
null会找到对应字段age为null值的情况,也可以找到不存在age字段的记录,当只需要找到第一种情况下的文档的时候,只需要用$exists限制一下就好
> db.things.find({age: {$in:[null], “$exists”:true}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3, “age” : null }
5.$mod取模运算
> db.things.find({age : {$mod : [10, 1]}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2, “age” : 11 }
6.$ne不等于
此运算符用于布尔不等于的运算,例如,age不等于11
> db.things.find({age: {$ne: 11}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3, “age” : null }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
7.$in包含
此操作符与SQL标准语法的用途一样,即要查询的数据在一个特定的取值范围内
> db.things.find({x: {$in : [1, 2, 3]}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2, “age” : 11 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3, “age” : null }
8.$nin不包含
此操作符与SQL标准语法的用途是一样的,即要查询的数据在一系列枚举的范围外
> db.things.find({x: {$nin : [1, 2, 3]}})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
9.$size匹配数组元素个数
此操作符用于统计数组中的元素个数
> db.things.find({label : {$size : 3}})
{ “_id” : ObjectId(“593d33bb9f76736827f8b1ec”), “label” : [ 1, 2, 4 ] }
10.count查询记录条数
此操作符用于统计记录的条数
> printjson(db.things.find().count())
4
11.skip限制返回记录的条数
skip(n).limit(m),从第n条开始,返回m条数据
> db.things.find().skip(1).limit(3)
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3, “age” : null }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
{ “_id” : ObjectId(“593d33bb9f76736827f8b1ec”), “label” : [ 1, 2, 4 ] }
12.sort排序
此操作符用于将结果集排序,例如,以x降序排序
> db.things.find().sort({x:-1})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a15”), “x” : 4, “y” : 5 }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a13”), “x” : 2, “y” : 3, “age” : null }
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2, “age” : 11 }
{ “_id” : ObjectId(“593d33bb9f76736827f8b1ec”), “label” : [ 1, 2, 4 ] }
升序排列,将-1改为1就可以了
13.distinct去掉重复值
在表中可能会包含重复值,distinct操作符用于过滤掉多余的重复记录,对于重复记录只保留一条,通常用 它来返回不重复的记录的条数,但是此操作在数据量大的表里比较耗时,慎用
x不重复的记录有三条
> db.things.distinct(“x”)
[ 1, 2, 4 ]
14.group分组统计
group by子句主要用于对where得到的结果进行分组,也就是说,它在where子句之后执行,对经过where子句筛选后的结果按照某些列进行分组,之后进行相对应的处理工作
select a, b, sum(c) csum from coll where active=1 group by a,b
db.coll.group({ {key : {a:true, b:true}}, cond: {active : 1}, reduce : function(obj, prev) { prev.csum += obj.c }, initial : {csum : 0}})
参数说明:
- key : 要分组的列,如上面的a,b
- cond: 分组条件,可以理解为where后面的 active=1
- reduce : 分组计算的方法,sum(c)
- initial: 分组计算的初始值,csum的值从0开始计算
查询语法
MongoDB最大的特点是,它支持的查询语言非常强大,其语法类似于面向对象的查询语言,不但可以实现关系数据库查询的大部分功能,而且还支持对数据建立索引
1.数组内容的查询
> db.things.find({x : 1})
{ “_id” : ObjectId(“593cf4401991bcbd477f7a12”), “x” : 1, “y” : 2, “age” : 11 }
2.内嵌文档的查询
数据库中存在这样一个文档
{ "_id" : ObjectId("593d44099f76736827f8b39e"), "authorA" : { "name" : "Amy", "age" : 29 }, "authorB" : { "name" : "Lindy", "age" : 30 }}
当我们想要查询A作家的名字的时候
> db.things.find({“authorA.name”: “Amy”})
{ “_id” : ObjectId(“593d44099f76736827f8b39e”), “authorA” : { “name” : “Amy”, “age” : 29 }, “authorB” : { “name” : “Lindy”, “age” : 30 } }
查询内嵌属性需要加上引号“”
3.正则表达式匹配
模糊查询,匹配查询name=“v*”开头的文档
> db.things.find({name : /^v.*/})
{ “_id” : ObjectId(“593d463f9f76736827f8b3dc”), “name” : “vamous” }
不是以name=“v*”开头的文档,查询语句向下面一样:
db.things.find({name : {$not: /^v.*/}})
4.$where查询
$where查询是指采用类似SQL的where语句来进行查询,但命令中并不包含where关键字
db.cl.find("this.a > 3")
,这种情况下,会将BSON转换为一个JavaScript对象,所以$where查询会比一般的查询慢很多
联合查询
通过连接运算可以实现多个表联合查询,可以用两种方法去应付这类需求:简单手工关联和DBRef方式
加入存在两个集合:一个是用户表、一个是帖子表,通过用户名获得该用户发表过的帖子
首先取得了用户对象”u”,然后通过对象u来取得帖子列表
var u = db.getCollection('user').find({name: "vamous"});for(var p = db.getCollection('postings').find({"name": u._query.name}); p.hasNext();){ printjson(p.next().title)}
DBRef方式关联
就是说在两个collection之间定义一个关联,比如把collectionB的“_id“列的值存在collectionA的一个列中,然后通过collectionA这个列中所存的值在collectionB中找到对应的记录
1.在user这个集合中找到name=vamous的文档
u = db.getCollection('user').find({"name": "vamous"})[0];printjson(u._id); //得到objectId//ObjectId("593d4e1d1991bcbd477f7a17")
2 在postings这个集合中做关联
db.getCollection('postings').insert({title : "vue", user : new DBRef('user', u._id)})
- 通过帖子查找用户
db.getCollection('postings').find({title : "vue"})[0].user.fetch()
DBRef就是从文档的一个属性指向另一个文档的指针
游标和存储过程
游标是系统为用户开设的一个数据缓冲区,用来存放SQL语句的执行结果,游标提供了一种对从表中检索出来的数据进行操作的灵活手段,hasNext判断是否还有数据,找到的数据通过next方法提取出来,=
for(var p = db.getCollection('postings').find({"name": u._query.name}); p.hasNext();){ printjson(p.next().title)}
db.getCollection('postings').find({"name": u._query.name}).forEach(function(u) { .... })
存储过程:存储过程可有一个应用调用来执行,并且允许用户声明变量,MongoDB的存储过程存储在db.system.js表中
db.system.js.save({_id: "addNumbers", value: function(x, y) { //保存 return x + y;}}) db.system.js.find() //查找db.system.js.eval('addNumber(3, 4.2)') //调用
通过执行db.eval命令调用该方法的接口,也可以把存储过程的逻辑直接放在db.eval()的参数里直接调用
存储过程的优点:
- 存储过程利用流程控制语句编写,有很强的灵活性,可以完成复杂的判断和运算
- 通过存储过程可以使相关的动作同时发生,从而维护数据库的完整性
- 降低网络的通信量
高级更新
更新命令
两个更新命令,一个是update,另外一个是save。
update命令:update(criteria, objNew, upsert, multi)
分别为查询条件(where语句之后的东西),更新的对象和一些更新的操作符(如$、$inc等,可以理解为set之后的内容),如果不存在update的记录是否插入(true为插入,false为不插入),默认为false(只找到第一条,true则代表着找到的都更新)
db.things.update({x: 1}, {$set: {x: 88}},true, false)
save命令:save(obj)
obj为单个文档,在collection里如果存在相同的”_id”的记录,MongoDB就会把obj对象代替为collections已经存在的内容
更新操作符
$inc(增加):
db.things.update({x: 88}, {$inc:{x : 2}})
$set(设置为)
db.things.update({x: 1}, {$set: {x: 88}},true, false)
$unset(删除字段)
db.things.update({name:”vamous”}, {$unset: {name: “vamous”}})
这种情况下是删除了这个键值对,文档还是存在的
$push与 $pushAll
{$push: {field: value}}
,将value追加到field中,fied一定是数组类型,如果field不存在,不增加一个数组类型
{$pushAll: {field : value_Array}}
,可以追加多个值到一个数组字段中
$pop与 $pull
{$pop: {field : -1}}
删除第一个值,为1代表删除最后一个值
{$pull: {field : _value}}
从数组中删除一个等于value的值
{$pullAll: {field : value_array}}
可以删除数组中的多个值
$rename
{$rename: {old_field_name: new_field_name}}
参考《MongoDB管理与开发精要》
- MongoDB学习笔记(一)初识MongoDB
- MongoDB 学习笔记(一)
- MongoDB 学习笔记(一)
- MongoDB学习笔记(一)
- mongodb学习笔记(一)
- MongoDB学习笔记(一)
- MongoDB学习笔记(一)
- MongoDB学习笔记(一)
- MongoDB学习笔记(一)
- mongodb 学习笔记一
- mongodb学习笔记一
- MongoDB学习笔记《一》
- mongodb学习笔记一
- MongoDB学习笔记(一)
- MongoDB学习笔记<一>
- Mongodb学习笔记一
- MongoDB 学习笔记一
- Mongodb学习笔记(一)
- 微信缓存初探
- bitbake
- iOS在某个界面不显示状态栏
- golang tcp listen(函数调用链)
- 6.SpringMVC总结以及在面试中的一些问题
- MongoDB学习笔记(一)
- 基于STM32的USB枚举
- io补充
- 均值滤波、中值滤波、混合中值滤波C++源码实例
- Tensorflow 04: tensorboard的使用
- 算法(三):动态规划法
- select下拉框(支持筛选、多选)
- android View的事件分发
- Android 国际化button以及tablayout字母自动变大写问题