(目录)
数据校验
MongoDB可以在文档插入或更新时,根据设定的验证规则对数据进行检查,如果不符合设定的规则,则会出现报错。
数据校验的注意事项
- 不能在
admin
,local
和config
数据库中进行数据校验 - 不能在系统集合(
system.*
)中进行数据校验 - 使用者必须拥有
collMod
操作权限
数据校验的设定方式
在MongoDB中,可以在创建集合时设置数据校验规则,也可以在集合创建后设置数据校验规则,规则的设定分为两种模式:JSON Schema验证模式和查询表达式验证模式
校验规则设定模式
- JSON Schema验证模式
可以用来规范集合必须具有哪些字段,以及字段类型。如果是数字类型,还可以规定最大值和最小值。从MongoDB3.6版本开始,官方支持这种模式,也是目前建议使用的模式。
在使用这种验证模式时,将
validator
参数与$jsonSchema
操作符搭配使用 - 查询表达式验证模式
可以搭配查询操作符指定验证的规则,如使用
$type
来规范字段类型,使用$regex
来规范字符串有哪些字节,使用$in
来规范必须是哪些值中的一个等。
不论以上哪种验证模式,都可以加上validationAction
参数来处理违反验证规则的文档,该参数可以设置为以下两种值
error
:默认值。表示如果插入或更新违反验证规则的文档,则MongoDB会自动报错warn
:如果插入或更新违反验证规则的文档,则MongoDB不会报错,但会把错误信息记录到日志中
校验规则设置方法
创建集合时设置
在db.createCollection()
时,可以通过加上参数validator
来建立验证规则。
集合创建后设置
在集合创建之后,还可以通过db.runCommand()
来建立验证规则,在使用这种方法建立验证规则是,不会对现有的文档进行验证,当这些文档被更新是,才会进行验证。
使用此方法时,可以加上validationLevel
参数,从而限制MongoDB在更新时验证规则对现有文档的严格程序。可以设置以下三种值
off
: 关闭校验规则strict
: 默认值,表示MongoDB会对文档中的所有新增或更新操作进行验证moderate
: 表示MongoDB仅对已符合验证规则的现有文档进行新增,更改验证。对不符合验证规则的现有文档,MongoDB不会再检查其操作是否符合验证规则
示例
# db.createCollection() 搭配JSON Schema验证模式
# foodColor集合必须满足以下条件
# 需要具有name,box_size,dyes三个字段
# name字段的值需要是字符串
# box_size的值只能是3,4,6其中之一
# dyes的值是一个数组,数组中需要包含至少一个元素,数据的元素不能重复。
# 对于dyes数组中的元素,要求如下
## 需要有size和color两个字段
## 不能增加其他字段
## size字段的值只能是small,medium,large其中之一
## color字段的值需要是字符串
db.createCollection ( "foodColor",
{
validator:
{
$jsonSchema:
{
bsonType: "object",
required: ["name", "box_size", "dyes"],
properties:
{
_id: {},
name: {
bsonType: ["string"],
description: "'name' is a required string"
},
box_size: {
enum: [3, 4, 6],
description: "'box_size' must be one of the values listed and is required"
},
dyes: {
bsonType: ["array"],
minItems: 1, // each box of food color must have at least one color
uniqueItems: true,
items: {
bsonType: ["object"],
required: ["size", "color"],
additionalProperties: false,
description: "'items' must contain the stated fields.",
properties: {
size: {
enum: ["small", "medium", "large"],
description: "'size' is required and can only be one of the given enum values"
},
color: {
bsonType: "string",
description: "'color' is a required field of type string"
}
}
}
}
}
}
}
})
# db.runCommand()搭配查询表达式验证模式
# contacts集合,必须满足以下条件之一:phone的类型是字符串,email中的字符需要以@mongod.com结尾,status值必须是Unknown或者Incomplete之一
db.runCommand(
{
collMod:"contacts",
validator:
{ $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /@mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
}
} )
原子性操作
在MongoDB中,原子性操作是指在保存文档时,要么全部保存,要么全部回滚。当一个文档在进行写操作时,其他对于此文档的操作是不可以进行的。MongoDB提供的原子性操作包含文档的保存、修改和删除。 MongoDB4.0之前的版本支持单个文档的原子性操作;4.0之后的版本支持在副本集的架构里实现多个文档的事务。
MongoDB中的锁
MongoDB通过锁机制来避免并发操作
- 当一个使用者对文档进行读操作时,会取得一个“读”锁。此时,其他使用者可以读此文档,但不可以对此文档进行写操作
- 当一个使用者对文档进行写操作时,会取得一个“写”锁。此时,其他使用者不可以对此文档进行读写操作
常用修改操作符
MongoDB会对文档进行隔离性的写操作,同时还可以搭配一些操作符来对文档进行修改。以下是常用的7种修改操作符
修改字段
- $set 修改指定文档,如果没有找到指定的文档,则创建一个新文档。
# 初始值
db.products.find({_id:100}).pretty()
{
"_id" : 100,
"sku" : "abc123",
"quantity" : 250,
"instock" : true,
"reorder" : false,
"details" : {
"model" : "14Q2",
"make" : "xyz"
},
"tags" : [
"apparel",
"clothing"
],
"ratings" : [
{
"by" : "ijk",
"rating" : 4
}
]
}
# 更新
db.products.update(
{ _id: 100 },
{ $set:
{
quantity: 500,
details: { model: "14Q3", make: "xyz" },
tags: [ "coats", "outerwear", "clothing" ]
}
}
)
## 结果
db.products.find({_id:100}).pretty()
{
"_id" : 100,
"sku" : "abc123",
"quantity" : 500,
"instock" : true,
"reorder" : false,
"details" : {
"model" : "14Q3",
"make" : "xyz"
},
"tags" : [
"coats",
"outerwear",
"clothing"
],
"ratings" : [
{
"by" : "ijk",
"rating" : 4
}
]
}
- $unset 删除指定的字段
# 删除tags和reorder字段
db.products.update(
{_id : 100},
{$unset : {tags : "",reorder : ""}})
# 结果
db.products.find({_id:100}).pretty()
{
"_id" : 100,
"sku" : "abc123",
"quantity" : 500,
"instock" : true,
"details" : {
"model" : "14Q3",
"make" : "xyz"
},
"ratings" : [
{
"by" : "ijk",
"rating" : 4
}
]
}
- $inc 在原数值基础上增加数值大小
# quantity的值-2
db.products.update({_id:100},{$inc:{quantity:-2}})
# 结果
db.products.find({_id:100}).pretty()
{
"_id" : 100,
"sku" : "abc123",
"quantity" : 498,
"instock" : true,
"details" : {
"model" : "14Q3",
"make" : "xyz"
},
"ratings" : [
{
"by" : "ijk",
"rating" : 4
}
]
}
- $rename 更改字段名称
# 将sku字段名称更改为StockKeepingUnit
db.products.update({_id:100},{$rename:{"sku":"StockKeepingUnit"}})
# 结果
db.products.find({_id:100}).pretty()
{
"_id" : 100,
"quantity" : 498,
"instock" : true,
"details" : {
"model" : "14Q3",
"make" : "xyz"
},
"ratings" : [
{
"by" : "ijk",
"rating" : 4
}
],
"StockKeepingUnit" : "abc123"
}
修改数组
- $push 在数组中插入值
# 插入单个值
db.students.update(
{ _id: 1 },
{ $push: { scores: 89 } }
)
# 使用$each插入多个值
db.students.update(
{ name: "joe" },
{ $push: { scores: { $each: [ 90, 92, 85 ] } } }
)
- $pull 在数组中删除值
# 初始值
db.stores.find();
{ "_id" : 1, "fruits" : [ "apples", "pears", "oranges", "grapes", "bananas" ], "vegetables" : [ "carrots", "celery", "squash", "carrots" ] }
{ "_id" : 2, "fruits" : [ "plums", "kiwis", "oranges", "bananas", "apples" ], "vegetables" : [ "broccoli", "zucchini", "carrots", "onions" ] }
# 使用pull删除fruits中apples和oranges的值,以及vegetables中carrots的值
db.stores.update(
{ },
{ $pull: { fruits: { $in: [ "apples", "oranges" ] }, vegetables: "carrots" } },
{ multi: true }
)
# 删除后的结果
db.stores.find()
{ "_id" : 1, "fruits" : [ "pears", "grapes", "bananas" ], "vegetables" : [ "celery", "squash" ] }
{ "_id" : 2, "fruits" : [ "plums", "kiwis", "bananas" ], "vegetables" : [ "broccoli", "zucchini", "onions" ] }
- $pop 删除数组中的第一个或最后一个值
# 删除_id为1的文档中,fruits数组的最后一个值和vegetables数组的第一个值
db.stores.update({_id:1},{$pop:{"fruits":1,"vegetables":-1}})
# 结果
db.stores.find()
{ "_id" : 1, "fruits" : [ "pears", "grapes" ], "vegetables" : [ "squash" ] }
{ "_id" : 2, "fruits" : [ "plums", "kiwis", "bananas" ], "vegetables" : [ "broccoli", "zucchini", "onions" ] }