MongoDB权威指南学习笔记(1)--基础知识与对文档的增删改查
Mongo
基础知识与对文档的增删改查
基础知识
文档
文档就是键值对的一个有序集,例如
{"greeting":"hello"}
文档中的值可以时多种不同的数据类型;文档中的键时字符串,但有少数例外情况
- 键不能含有 (空字符)
- .和$具有特殊含义,只能在特定环境下使用
集合
集合就是一组文档,一个集合就相当于关系数据库的一张表
动态模式
集合时动态模式的,就是说集合里面的文档可以时各式各样的。
命名
命名需要满足以下条件:
- 不能是空字符串
- 不能包含 字符
- 不能以system.开头
- 不能包含$
子集合
使用.来分割不同命名空间的子集合,例如一个博客系统可能包含两个集合,分别时blog.posts和blog.authors。
数据库
数据库就是多个集合,一个mongo实例可以承载多个数据库,每个数据库可以有多个集合,每个数据库都有独立的权限。
数据库命名需要满足以下条件
- 不能是空字符串
- 不能含有特殊字符,基本只能使用字母和数字
- 区分大小写,(应全部小写)
- 最多为64字节
有一些数据库名时保留的,可以直接访问这些特殊含义的数据库
- admin: root数据库
- local: 不可复制,所有本地集合都可以存储在其中
- config: 用于存储分片信息
shell
功能完备的JavaScript解释器,可以运行任意JavaScript程序
mongodb客户端
- db: 查看当前指向那个数据库 $ db
- use: 选择数据库 $ use foobar
基本操作
创建
insert函数将一个文档添加到集合中。
// 声明post变量
post={
"title":"my blog test",
"content":"blog post",
"date":new Date()
}
// 插入blog集合
db.blog.insert(post)
查询调用find方法
db.blog.find()
读取
- find():查询所有文档(shell会自动显示最多20个匹配的文档)
- findOne():查询一个文档
更新
使用update()进行更新操作,接受两个参数,第一个限定条件,第二个时新的文档。
post.comments=[]
db.blog.update({title:"my blog test"},post)
删除
使用remove()方法将文档从数据库永久删除
- 如果不携带参数,会将集合内所有文档都删除
- 携带一个限定条件作为参数,会删除指定文档
数据类型
基本数据类型
- null
- 布尔型
- 数值
- 字符串
- 日期
- 正则表达式
- 数组
- 内嵌文档
- 对象id
- 二进制数据
- 代码
使用MongoDB shell
在启动shell指定机器名和端口,就可以连接不同的机器
$ mongo some-host:30000/myDB
使用shell执行脚本
- 在shell中传递脚本 $ mongo script.js
- 使用laod(),从交互式shell运行脚本 load("scipt.js")
在脚本中可以访问db变量,以及其他全局变量,然而shell辅助函数不可以在文件中使用
- 在shell中使用run()执行命令行程序 run("ls","-l")
- 如果某些脚本被频繁加在,可以将他们添加到mongorc.js文件中,这个文件会在启动shell时自动运行
创建 更新 删除文档
插入并保存
使用insert()方法向目标集合插入一个文档
db.foo.insert({"bar":"baz"})
批量插入
使用batchInsert()方法向目标集合批量插入文档
db.foo.insert([{"_id":0},{"_id":1},{"_id":2}])
- 不能在单词请求中将多个文档批量插入多个集合中
- 如果在执行批量插入的过程中有一个文档插入失败,那么在该文档之前的所有文档都会成功插入,这个文档之后的文档都会插入失败
- 插入文档的_id不能重复
- 在批量插入遇到错误时,可以使用continueOnError选项忽略错误并继续执行后续插入,但在shell中并不支持,在驱动中可以执行
插入校验
mongo只对数据进行最基本的检查,检查文档的基本结构,如果没有_id字段,就自动增加一个,并且所有文档都必须小于16MB
删除文档
使用remove()删除
删除速度
删除文档通常很快,如果要清空集合,建议使用drop直接删除集合(然后在空集合上重建索引)
更新文档
使用update()进行更新
更新操作不可分割,先到先执行
文档替换
用一个新文档完全替换匹配的文档,这适用于大规模迁移的情况
使用修改器
通常文档只会有一部分字段要更新,所以可以使用原子性的更新修改器,指定对文档中的某些字段进行更新。
更新修改器是种特殊的键,用来制定复杂的更新操作
设置操作
$set修改器:用来制定一个字段的值,如果这个字段不存在,则创建它。这对更新模式或者增加用户定义的键非常方便。
- 向已有的集合中添加字段
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$set":{
"test":"test"
}
}
)
- 修改集合中已有的字段(可以修改键的类型或内嵌文档)
增加、修改、删除键时,应该使用$修改器
增加减少操作
$inc修改器:用来增加已有键的值,如果该键不存在那就创建一个。对于更新分析数据、因果关系等有数值变化的地方非常方便
更新id为xxx的value=value+1
db.foo.update({
"_id":ObjectId("5ace332ff02a40eb6148fc36")
},
{
"$inc":{
"value":1
}
}
)
- 除了_id不能修改其他都可以修改
- 和$set用法类似,专门用来增加或减少数字的
- 只能用于整型、长整型或双精度浮点型的值
数组修改器
有一大类修改器可以用于操作数组
添加元素
push修改器:如果数组已经存在,push会向已有的数组末尾加入一个元素,要是没有就创建一个新的数组
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"name":"joe",
"email":"xxx@qq.com"
}
}
}
)
/* 1 */
{
"_id" : ObjectId("5ace2559f02a40eb6148fc34"),
"title" : "my blog test2",
"content" : "blog post2",
"date" : ISODate("2018-04-11T15:10:17.952Z"),
"test" : "test",
"posts" : [
{
"name" : "joe",
"email" : "xxx@qq.com"
}
]
}
以上是一种比较简单的push使用形式,也可以应用在一些比较复杂的数组操作,使用each子操作符,可以通过一次
添加多个元素到数组中
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111]
}
}
}
)
如果希望数组的长度时固定的,可以使用slice和push组合在一起使用,可以保证数组不会超过设定好的最大长度,实际上就得到了一个最多包含n个元素的数组
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111],
"$slice":-10
}
}
}
)
- slice的值必须时负整数,如果数组的元素数量小鱼10(push之后),那么所有元素都会被保留,如果数组的元素大于10,那么只有最后10个元素会被保留。
可以在清理元素之前使用$sort,只要向数组中添加子对象就需要清理
db.blog.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$push":{
"posts":{
"$each":[111,1111,111111],
"$slice":-10,
"$sort":{
"rating":-1
}
}
}
}
)
这样会根据rating字段的值对数组中所有元素进行排序,然后保留前10个。
不能只将slice或者sort和push配合使用,且必须使用each
将数组作为数据集使用
如果想将数组作为数据集使用,保证数组内的元素不会重复。可以使用$ne实现。
例如:要是作者不在引文列表中,就添加进去
db.papers.update(
{
"authors cited":{
"$ne": "Richie"
}
},
{
"$push":{
"authors cited":"Richie"
}
}
)
实现上述需求,还可以使用addToSet实现,有些时候,更适合用addToSet例如:有一个表示用户的文档,已经有了电子邮件地址的数据集,添加新地址时,用
db.users.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$addToSet":{
"emails":"doe@gmail.com"
}
}
)
将addToSet和each组合可以实现添加多个不同的值,可以一次添加多个邮件地址,
db.users.update(
{
"_id":ObjectId("5ace2559f02a40eb6148fc34")
},
{
"$addToSet":{
"emails":{
"$each”: ["xxx@xxx.com","1111@xxx.com"]
}
}
}
)
删除元素
$pop修改器:从数组的任何一端删除元素
从数组末尾删除一个元素
{
"$pop":{
"key":1
}
}
从数组头部删除一个元素
{
"$pop":{
"key":-1
}
}
$pull:居于特定条件删除元素,而不仅仅以及元素位置
db.lists.update({},
{
"$pull":{
"todo":"xxx"
}
}
)
删除todo等于xxx的文档
基于位置的数组修改器
若时数组中有多个值,我们只想对其中的一部分进行操作,有另种方式
- 通过位置 增加第一个评论的投票数量
db.blog.update(
{
"post":post_id
},
{
"$inc":{
"commonts.0.votes:1
}
}
)
- ,用来定位查询文档已经匹配的数组元素、进行隔壁服更新 要是用户john把名字改成了Jim,就可以用定位符替换他在评论中的名字
db.blog.update(
{
"comments.author":"john"
},
{
"$set":{
"commonts.$.votes":"jim"
}
}
)
upsert
是一种特殊的更新,要是没有找到符合更新条件的文档,就会以这个条件和更新文档为基础创建一个新的文档,如果找到了匹配的文档,则正常更新。
upsert非常方便,不必预制集合,同一套代码既可以用于创建文档也可以用于更新文档
记录网站页面访问次数的例子:
db.analytics.update(
{
"url":"/blog"
},
{
"$inc":{
"pageviews":1
}
},
true
)
- 第三个参数true表示这是个upsert
- upsert操作时原子性的,创建文档会将条件文档作为基础,然后对他应用修改器文档
save hello 帮助程序
save时一个shell函数,如果文档不存在,它会自动创建文档,如果文档存在,它就更新这个文档,它只有一个参数,文档。要是这个文档含有_id键,save会调用upsert,否在会调用insert
更新多个文档
默认情况下,更新只能对符合匹配条件的第一个文档执行操作,要是有多个文档符合条件,只有第一个文档会呗更新。
如果要更新所有匹配的文档,可以将update的第四个参数设置为true
db.users.update({
"brithday":"10/13/1978"
},
{
"$set":{
"gift":"happy birhday"
}
},
false,
true
)
返回呗更新的文档
findAndModift能够在一个操作中返回匹配结果并进行更新
查询
find
指定需要返回的键
有时并不需要将文档中所有键/值对都返回,可以通过find(或findOne)的第二个参数来指定想要的键。 这样可以减少传输的数据量,又能节省客户端解码文档的时间和内存消耗。
db.users.find({},{
"username":1,
"email":1
})
如果不指定”_od”是否返回,”_id”是默认呗返回的
既然可以选择需要的键,当然也可以排除查询结果中的某些键值对
db.users.find({},{
"xxx":0
})
查询条件
查询条件
比较操作符:
- $lt :<
- $lte:<=
- $gt:>
- $gte:>=
例如查询“age”字段大于等于18、小于等于30的所有文档
db.users.find({
"age":{
"$gte":18,
"$lte":30
}
})
OR查询
有两种方式进行OR查询:
$in可以用于查询一个键的多个值
db.users.find({
"user_id":{
"$in":[123456,"joe"]
}
})
与in相反的是nin,将返回与数组中所有条件都不匹配的文档
$or可以在多个键中查询任意的给定值
db.raffle.find({
"$or";[
{
"ticket_no":725
},
{
"winner":true
}
]
})
$not
是元条件句,可以用在任何其他条件之上,表示否定的含义
条件语义
条件语句时内层文档的键,而修改器是外层文档的键
一个键可以在任意多个条件,但是一个键不能对应多个更新修改器
特定类型的查询
null
null不仅会匹配某个键的值为null的文档,而且还会匹配不包含这个键的文档。这个匹配还会返回缺少这个键的所有文档
如果仅想匹配键值为null的文档,既要检查该键的值是否时null,还要通过$exists条件判断键值是否存在。
正则表达式
正则表达式能够有效地匹配字符串。
例如: 想要查找所有名为Joe或者joe的用户,就可以使用正则表达式执行不区分大小写的匹配
db.users.find({
"name":/joe/i
})
- 系统可以接受正则表达式标志(i),但不一定要有。
- mongoDB使用Perl兼容的正则表达式来匹配正则表达式
查询数组
查询数组元素和查询标量值是一样的
例如有一个水果列表
db.food.insert({
"fruit":["aople","banana","peach"]
})
通过下面的查询可以成功匹配到文档
db.food.find({
"fruit":"banana"
})
$all
如果需要通过多个元素来匹配数组,就需要使用$all。
- 要找到既有apple又有banana的文档 db.food.find({ "fruit":{ "$all":["apple","banana"] } })
- 如果不使用$all,那就是对整个数组进行精确匹配,但是精确匹配对于缺少元素或者元素沉余的情况不适用
下面将不能匹配到文档
db.food.find({
"fruit":["apple","banana"]
})
- 如果想查询数组特定位置的元素,需要使用key.inex语法指定下标
将数组第三个元素和peach进行匹配
db.food.find({
"fruit.2":"peach"
})
$size
用它查询特定长度的数组。
db.food.find({
"fruit":{
"$size":3
}
})
$size并不能与其他查询条件组合使用,但是这种查询可以通过在文档中添加一个“size”的值
$slice操作符
可以返回某个键匹配的数组元的一个子集
假设现在有一个博客文章的文档,我们希望返回前10条评论
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":10
}
})
返回后10条
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":-10
}
})
指定偏移量以及希望返回的元素数量,来返回元素集合中间位置的某些结果
db.blog.posts.findOne(criteria,{
"comments":{
"$slice":[23,10]
}
})
除非特别声明,否则使用$slice时返回文档中的所欲键,别的键说明符都是默认返回未提及的键
返回一个匹配的数组元素
希望返回与查询条件相匹配的任意一个数组元素,可以使用$操作符得到一个匹配的元素。
用如下的方式得到Bob的评论
db.blog.posts.find({
"comments.name":"bob"
},{
"comments.$":1
})
数组和范围查询的相互作用
文档中的标量(非数组元素)必须与查询条件中的每一条语句相匹配
使用elemMatch要求使用查询条件中的两个语句与一个数组元素进行比较,elemMatch不会匹配非数组元素
db.test.find({
"x":{
"$elemMatch":{
"$gt"10,
"$lt":20
}
}
})
查询内嵌文档
有两种方法可以查询内嵌文档
- 查询整个文档
- 针对其键/值对进行查询
查询整个内嵌文档与普通查询完全相同,例如有如下文档
{
"name":{
"first":"joe",
"last":"schmoe"
},
"age":45
}
要查询姓名为joe schmoe的人可以这样
db.peop;e.find({
"name":{
"first":"joe",
"last":"schmoe"
}
})
如果想要查询一个完整的子文档,那么子文档必须精确匹配,如果joe决定添加一个代表钟建明的键,那么查询就不在可行。
如果允许的话,通常只针对内嵌文档的特定键值进行查询,我们一般使用点表示法查询内嵌文档的键
db.people.find({
"name.first":"joe",
"name.last":"schmoe"
})
查询文档可以包含点来表达进入内嵌文档内部的意思
$where查询
为安全起见,应该严格限制或消除$where语句的使用
最常见的应用就是比较文档中的两个键的值是否相等
游标
数据库使用游标返回find的执行结果,客户端对游标的实现通常能够对最终结果进行有效的控制。可以限制结果的数量,略过部分结果,根据任意键按任意顺序的组合对结果进行各种排序,或者执行一些强大的操作。
limit、skip、sort
要限制结果数量,可在find后使用limit函数
db.c.find().limit(3)
要是匹配的结果不到3个,则返回匹配数量的结果。
skip和limit类似,不过时跳过前n个匹配的文档,返回余下的文档
db.c.find().skip(3)
sort接受一个独享作为参数,这个对象时一组键值对,键对应文档的键名,值代表排序的方向。排序方向可以是1(升序)或者-1(降序)
db.c.find({
"username":1,
"age":-1
})
比较顺序
如果混合类型的键排序,其排序顺序是预先定义好的,优先级从小到大,其顺序如下:
- 最小值
- null
- 数字
- 字符串
- 对象(文档)
- 数组
- 二进制数据
- 对象id
- 布尔型
- 日期型
- 时间戳
- 正则表达式
- 最大值
避免使用skip略过大量结果
- 不同skip对结果分页 用limit返回结果的第一页,然后每个后续页面作为相对于开始的偏移量返回
- 随机选取文档 在插入文档时给每个文档都添加一个额外的随机键
搞基查询选项
两种类型查询:
- 简单查询
- 封装查询
用于向查询中添加各种选项:
- $maxscan : integer 指定本次扫描中扫描文档数量的上限
- $min: document 查询的开始条件,在这样的查询中,文档必须与索引的键完全匹配
- $max: document 查询的结束条件,在这样的查询中,文档必须与索引的键完全匹配
注:
- 上述测试在MongoDB 3.4.3-8-g05b19c6中成功
- 上述文字皆为个人看法,如有错误或建议请及时联系我
- 深化“互联网+先进制造业”发展工业互联网的系列解读二:打造平台体系
- WebFont 三宗罪之二:吹毛求疵的WebFont 渲染差异
- IDC发布IT转型报告,现代化、自动化、转型三要素必不可少
- 存储过程和触发器的应用
- 两部委印发车联网产业标准体系建设指南 提到了自动驾驶
- 状态开关按钮ToggleButton
- 微软开放 .NET 框架源代码
- Angularjs基础(十二)
- 妙趣横生的HTML5 Page Visibility API
- 禁止/移除 WordPress 4.2 中前台自动加载的 emjo 脚本
- 项目管理方面的几个.NET开源项目
- 如何向十岁以下的朋友解释编程?这个说法碉堡了!
- 快速比较和合并文件
- 前端页面中 iOS 版微信长按识别二维码的bug 与解决方案
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- R语言机器学习实战之多项式回归
- 5000字!带你零距离接触websocket!
- 使用 GitLab CI 和 Docker 自动部署 Spring Boot 应用
- 玩转StyleGAN2模型:教你生成动漫人物
- R语言时间序列数据指数平滑法分析交互式动态可视化
- 再见Excel!最强国产开源在线表格Luckysheet走红GitHub
- R语言广义线性模型索赔频率预测:过度分散、风险暴露数和树状图可视化
- R语言多分类logistic逻辑回归模型在混合分布模拟单个风险损失值评估的应用
- sas神经网络:构建人工神经网络模型来识别垃圾邮件
- 图像倾斜校正算法的MATLAB实现:图像倾斜角检测及校正
- 手写dubbo框架9-SPI实现
- R语言非参数模型厘定保险费率:局部回归、广义相加模型GAM、样条回归
- VsCode插件之Live Serve探秘.(上)
- R语言小数定律的保险业应用:泊松分布模拟索赔次数
- R语言中自编基尼系数的CART回归决策树的实现