跟着官方文档学习
Getting Started
db 显示当前数据库
插入文档
1 | db.inventory.insertMany([ |
查询所有文档
1 | db.inventory.find({}); |
美化查询结果
1 | db.inventory.find({}).pretty(); |
相等匹配
1 | db.inventory.find({ status: "D" }); |
对于数组,严格相等,数组项的顺序也要一致
1 | // 不匹配 { tags: ["blank", "red"] } |
定义返回结构 Projection
1 | db.inventory.find( { }, { item: 1, status: 1 } ); |
默认全是 0 或者全是 1,不能既有 0 又有 1
database and collections
MongoDB 在集合中存储 BSON 文档,即数据记录;数据库中的集合。
数据库
可以 使用 use 去切换到一个不存在的数据库
如果数据库不存在,MongoDB 将在您第一次为该数据库存储数据时创建数据库。
MongoDB 将文档存储在集合中。集合类似于关系数据库中的表。
如果一个集合不存在,MongoDB 会在您第一次存储该集合的数据时创建该集合。
ObjectId
ObjectId 是一个 12 字节的 BSON 类型字符串。按照字节顺序,依次代表:
- 4 字节 UNIX 时间戳
- 3 字节 运行 MongoDB 的机器
- 2 字节 代表生成次 id 的进程
- 3 字节 由一个随机数开始的的计数器生成的值
使用这种机制可以保证唯一性
1 | db.myNewCollection2.insertOne({ x: 1 }); |
如果 insertOne()和 createIndex()操作还不存在,那么它们将创建各自的集合。确保集合名称遵循 MongoDB 命名限制。
db.createCollection() 可以显式以指定的选项创建一个数据库
view
view 是一个可查询的对象,由其他集合或者视图聚合产生。
MongoDB 并不在硬盘中保存 view,而是在需要的时候进行计算。view 不支持写操作
1 | db.createCollection( |
view 支持以下方法
1 | db.collection.find(); |
更新文档
1 | db.collsetion.update( |
update 更新后的对象或指定一些更新的操作符
$set
直接指定更新后的值$inc
在原基础上累加
1 | db.students.update({ name: "zf" }, { $set: { age: 44 } }); |
$unset
删除字段,如果字段不存在不做处理$push
向数组添加数据$ne
1 | db.students.update({ hobby: { $ne: "smoking" } }, {}); |
$each
1 | db.students.update( |
$addToSet
插入到集合,可以保证唯一性,如果项是重复的,将不会被插入
1 | db.students.update({ name: "zfpx" }, { $addToSet: { hobby: "drink" } }); |
upsert 可选,不过不存在记录是否插入,默认不插入
muitl 可选,默认只更新找到的第一条记录,如果为 true,更新所有符合的记录
操作符
$eq
:匹配字段值等于指定值的文档
$gt
:匹配字段值大于指定值的文档
$gte
:匹配字段值大于等于指定值的文档
$lt
:匹配字段值小于指定值的文档
$lte
:匹配字段值小于等于指定值的文档
$ne
:匹配字段值不等于指定值的文档,包括没有这个字段的文档
$in
:匹配字段值等于指定数组中的任何值
$nin
:字段值不在指定数组或者不存在
$or
:文档至少满足其中的一个表达式
$not
:字段值不匹配表达式或者字段值不存在
$nor
:字段值不匹配所有的表达式的文档,包括那些不包含这些字段的文档
$exists
:boolean 等于 true 时,字段存在,包括字段值为 null 的文档
$type
:匹配字段值为指定数据类型的文档
$mod
:匹配字段值被除有指定的余数的文档
$regex
:正则表达式可以匹配到的文档
$text
:针对创建了全文索引的字段进行文本搜索
$where
:可以通过 js 表达式或 js 函数来查询文档$all
: 字段值是包含所有指定元素的数组的文档$elemMatch
:数组字段至少一个元素满足所有指定查询条件的文档$size
:匹配数组字段元素个数等于指定数量的文档$ (projection)
:限定查询结果中指定数组字段返回满足条件的第一个元素$elemMatch (projection)
:限定查询结果中指定数组字段返回满足条件的第一个元素$slice (projection)
:控制指定数组字段返回元素个数$inc
:给一个字段增加指定值$setOnInsert
:upsert 为 true 时,有插入文档操作时插入指定字段值$unset
:删除指定字段
$min
:指定值小于当前值则更新为指定值$max
: 指定值大于当前值则更新为指定值$currentDate
:设置字段值为当前日期
$
: 更新指定数组的第一个元素$addToSet
:数组字段增加一个值$pop
: 删除数组字段中的第一个或最后一个元素$pullAll
: 删除数组字段中所有指定值,如果指定值为数组,则删除匹配数组内的元素$pull
: 符合条件的值将被删除$pushAll
:向数组中追加多个指定值$push
:向数组中追加值$each
:用于 $addToSet
添加多个值到数组中
高级知识
通过配置项启动数据库
—dbpath 数据库文件的目录
—port 默认是 27017 28017
—fork 以后台守护方式运行
—logpath 日志目录
—config 指定一个配置文件
—auth 以安全参数启动
导入导出
mongoimport 导出数据
mongoexport 导入数据
mongodb 使用 mongorestore 来恢复备份的数据
Mongodump 可以 backup 整个数据库,而 mongoexport 要对每个 conllection 进行操作。恢复使用 mongostore
mongoexport 输出的 json 比 mongodump 的 bson 可读性高,进而可以直接对 json 文件进行操作然后还原数据(bson 转换 json 存在潜在兼容问题)
安全措施
物理隔离
网络隔离
防火墙
用户名和密码验证
用户管理
1 | show roles 查看角色 |
索引
ensure 表示升序
1 | db.students.ensureIndex({ age: 1 }); |
分析索引的执行过程
mongodb 提供了 explain 命令来分析查询过程
注意事项
1 为正序 -1 为倒序
索引可以提升查询速度,但会降低插入速度
数据量不大时不需要使用索引,性能的提升不明显,反而大大增加了内存和硬盘的消耗
查询数据超过表数据量 30%时,不要使用索引字段查询
排序工作的时候可以建立索引提高排序速度
数字索引要比字符串索引快得多
集群技术
主从复制
数据库集群中明确知道谁是主服务器,主服务器只有一台
从服务器要知道自己的数据源也就是主服务器是谁
—master 用来确定主服务器,—slave 和—source 来控制从服务器
其他选项
-only 从节点 指定复制某个数据库
-slavedelay 从节点 设置主数据库同步数据的延迟
-fastsync 从节点 以主数据库的节点快照为节点启动从数据库
-autoresync 从节点 如果不同步则重新同步数据库
-oplogSize 主节点 设置 oplog 的大小
db.sources
提供了 api 设置从服务器的 source
副本集
需要一台活跃服务器和两个备份服务器
当活跃服务器故障,集群根据权重算法推选出活跃服务器
当原来的主服务器恢复后又会变成从服务器
1 | dbpath=... |
mongoose
Schema
1 | var personSchema = new Schema({ |
SchemaType
以下是 mongoose 的所有合法 SchemaTypes:
- String
- Number
- Date
- Buffer
- Boolean
- Mixed
- ObjectId
- Array
- Decimal128
直接声明 schema type 为某一种 type,或者赋值一个含有 type 属性的对象。
全部可用
- required: 布尔值或函数 如果值为真,为此属性添加 required 验证器
- default: 任何值或函数 设置此路径默认值。如果是函数,函数返回值为默认值
- select: 布尔值 指定 query 的默认 projections 为 false 默认不返回该字段
- validate: 函数 adds a validator function for this property
- get: 函数 使用 Object.defineProperty() 定义自定义 getter 这个 getter 是在访问该属性时生效的 consolelog 打印的还是初始值
- set: 函数 使用 Object.defineProperty() 定义自定义 setter
- alias: 字符串 仅 mongoose >= 4.10.0。 为该字段路径定义虚拟值 gets/sets
索引相关
- index: 布尔值 是否对这个属性创建索引
- unique: 布尔值 是否对这个属性创建唯一索引
- sparse: 布尔值 是否对这个属性创建稀疏索引
String
- lowercase: 布尔值 是否在保存前对此值调用 .toLowerCase()
- uppercase: 布尔值 是否在保存前对此值调用 .toUpperCase()
- trim: 布尔值 是否在保存前对此值调用 .trim()
- match: 正则表达式 创建验证器检查这个值是否匹配给定正则表达式
- enum: 数组 创建验证器检查这个值是否包含于给定数组
Number
- min: 数值 创建验证器检查属性是否大于或等于该值
- max: 数值 创建验证器检查属性是否小于或等于该值
Date
- min: Date
- max: Date
注意
Dates
内建 Date 方法 不会触发 mongoose 修改跟踪逻辑, 如果你对使用 setMonth() 修改文档里的 Date, mongoose 在 doc.save() 的时候是察觉不到这个文档发生了变化的,因此保存不到数据库。 如果你一定要用内建 Date 方法, 请手动调用 doc.markModified(‘pathToYourDate’) 告诉 mongoose 你修改了数据。
1 | var Assignment = mongoose.model("Assignment", { dueDate: Date }); |
Mixed
一个啥都可以放的 SchemaType , 虽然便利,但也会让数据难以维护。 Mixed 可以通过 Schema.Types.Mixed 或 传入一个空对象定义。以下三种方法效果一致:
1 | var Any = new Schema({ any: {} }); |
ObjectIds
创造 SchemaTypes 或子文档数组。
1 | var ToySchema = new Schema({ name: String }); |
指定空数组相当于 Mixed
1 | var Empty1 = new Schema({ any: [] }); |
Connections
连接
mongoose.connect(‘mongodb://username:password@host:port/database?options…’);
操作缓存
不必等待连接建立成功就可以使用 mongoose models
1 | mongoose.connect("mongodb://localhost/myapp"); |
mongoose 会缓存操作,这样很方便,但有时也会造成疑惑,因为如果你没连上 ,Mongoose 不会 抛错。
要禁用缓存,请修改 bufferCommands 配置。 如果你打开了 bufferCommands 连接被挂起,尝试关闭 bufferCommands 检查你是否正确打开连接。 你也可以全局禁用 bufferCommands :
1 | mongoose.set("bufferCommands", false); |
Model
Model 是通过 Schema 构造而成的,除了具有 Schema 定义的数据库骨架以外,还可以操作数据库
第一个参数是跟 model 对应的集合( collection )名字的 单数 形式。 Mongoose 会自动找到名称是 model 名字 复数 形式的 collection 。 对于上例,Tank 这个 model 就对应数据库中 tanks 这个 collection。.model() 这个函数是对 schema 做了拷贝(生成了 model)。 你要确保在调用 .model() 之前把所有需要的东西都加进 schema 里了!
1 | var schema = new mongoose.Schema({ name: "string", size: "string" }); |
查询
可以用 model 的 find,findById,findOne,where 这些静态方法
删除
remove 方法
更新
update 方法
Documents
Mongoose document 代表着 MongoDB 文档的一对一映射。 每个 document 都是他的 Model 的实例。
更新
有很多方法可以更新文档
1 | Tank.findById(id, function (err, tank) { |
用 .set() 修改 document,在底层 tank.size = ‘large’; 用 tank.set({ size: ‘large’ })实现。
1 | Tank.findById(id, function (err, tank) { |
如果我们仅仅需要更新而不需要获取该数据, Model#update 就很适合我们:
1 | Tank.update({ _id: id }, { $set: { size: "large" } }, callback); |
如果我们确实需要返回文档
1 | Tank.findByIdAndUpdate( |
验证
document 在被保存前会被验证
覆盖
可以用 .set() 覆盖整个文档,如果要修改在中间件中被保存的文档,这样很方便
子文档 subdocument
Mongoose 中子文档有两种不同的概念 子文档数组和单个嵌套子文档。
子文档与普通 document 类似。嵌套 schema 可以有自己的 中间件、自定义检验逻辑、 虚拟值以及其他顶层 schemas 可用的特性。两者主要的不同点是 子文档不能单独保存,他们会在他们的顶级文档保存时保存。
子文档跟普通 document 一样有 save 和 validate 中间件。 调用父文档的 save() 会触发其所有子文档的 save() 中间件, validate() 中间件同理。
子文档的 pre(‘save’) 和 pre(‘validate’) 中间件执行于 顶层 document 的 pre(‘save’) 之前, 顶层 document 的 pre(‘validate’) 之后。 因为 save() 前的验证就是一个内置中间件。(待修改)
查找子文档
每个子文档都有一个默认的 _id
。Mongoose document 数组有一个特别的 id 方法, 这个方法只要传入 _id
就能返回文档数组中特定文档。
1 | var doc = parent.children.id(_id); |
添加子文档到数组
Mongoose 数组方法有 push、 unshift、 addToSet、 及其他:
1 | var Parent = mongoose.model("Parent"); |
代替声明语法的写法
如果你用对象的数组创建 schema ,mongoose 会自动 为你把对象转换成 schema:
1 | var parentSchema = new Schema({ |