# 三、MongoDB CRUD
参考:
- https://docs.mongoing.com/mongodb-crud-operations
- https://docs.mongodb.com/manual/crud/
# 1. 数据库 database
# 1.1 创建数据库
use dbname
数据库不在则会自动创建。
数据库命名规则
数据库名可以是满足以下条件的任意 UTF-8 字符串:
- 不能是空字符串
- 不得含有
''
(空格)、.
、$
、/
、\
和\0
(空字符) - 应全部小写
- 最多 64 字节
有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库:
- admin:从权限的角度看,这是个 “root” 数据库。要是一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
- local:这个数据库永远不会被复制,可以用来存储限于本地单台服务器的任意集合。
- config:当 mongo 用于分片配置时,config 数据库在内部使用,用于保存分片的相关信息。
# 1.2 删除数据库
db.dropDatabase()
主要用来删除提交持久化的数据库。
# 1.3 查看数据库
show dbs
:查看所有数据库,只有当 db 里面有数据了,才会被持久化到磁盘当中,否则只在内存里面的话,用 show dbs 是查不到的。db
:查看当前使用的数据库。
MongoDB 默认的数据库为 test,如果你没有选择数据库,集合将存放在 test 数据库中。
# 2. 集合 collection
# 2.1 创建集合
# 2.1.1 显式创建
db.createCollection(name)
# 2.1.2 隐式创建
- 当向一个集合中插入一个文档的时候,如果集合不存在,则会自动创建集合。
集合命名规范
- 集合名不能是空字符串""。
- 集合名不能含有 \0 字符(空字符),这个字符表示集合名的结尾。
- 集合名不能以 "system."开头,这是为系统集合保留的前缀。
- 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现 $。
# 2.2 删除集合
db.collectionName.drop()
如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。
# 2.2 查看集合
show collections
show tables
# 3. 文档 document
# 3.1 插入文档
db.collection.insertOne()
:插入单个db.collection.insertMany()
:插入多个db.collection.insert()
:插入单个或多个db.collection.save()
: 插入,底层调用 insert
db.collection.insert(
<document or array of document>
{
writeConcern: <document>, # 写关注,可选
ordered: <boolean> # 是否按顺序插入,默认为 true
# true:有一个文档出现错误,则停止插入,返回还没处理的剩余的文档
# false:如果出错,则继续处理后面的文档
}
)
补充
创建或插入操作会将新文档添加到集合中。 如果该集合当前不存在,则插入操作将创建该集合。
在MongoDB中,插入操作针对单个集合。 MongoDB中的所有写操作都是单个文档级别的原子 (opens new window)操作。
In MongoDB, a write operation is atomic (opens new window) on the level of a single document, even if the operation modifies multiple embedded documents within a single document.
在 MongoDB 中,存储在集合中的每个文档都需要一个唯一的
_id
字段作为主键。 如果插入的文档省略_id
字段,则 MongoDB 驱动程序会自动为_id
字段生成ObjectId
。插入当前日期,用
new Date()
。MongoDB 中的数字,默认情况下是
double
类型,如果要存整型,必须使用函数NumberInt(整型数字)
,否则取出来就有问题了。如果某字段没值,可以赋值为 null,或不写该字段。
insertOne、insertMany、insert 的区别
- 三者返回的错误信息不一样;
- insertOne、insertMany 不支持 db.collection.explain() 命令。
# 3.1.1 插入一个文档
> db.hedon.insertOne({ name: "hedon", age: 18, address: "beijing", tags: ["student", "software developer"], body: {weight: 70, height: 199} })
{
"acknowledged" : true,
"insertedId" : ObjectId("6115e5c33e8322a9d326c23f")
}
insertOne
返回一个文档,其中包含新插入的文档的 _id
字段值。
查看刚刚插入的文档:
> db.hedon.find({name: "hedon"})
{ "_id" : ObjectId("6115e5c33e8322a9d326c23f"), "name" : "hedon", "age" : 18, "address" : "beijing", "tags" : [ "student", "software developer" ], "body" : { "weight" : 70, "height" : 199 } }
注意
- 文档中的键值对是有序的。
- 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
- MongoDB 区分类型和大小写。
- MongoDB 的文档不能有重复的键。
- 文档的键是字符串。除了少数例外情况,键可以使用任意 UTF-8 字符。
文档键命名规范:
- 键不能含有
\0
(空字符),这个字符用来表示键的结尾; .
和$
有特别的意义,只有在特定环境下才能使用;- 以下划线
"_"
开头的键是保留的(不是严格要求的)。
# 3.1.2 插入多个文档
> db.hedon.insertMany([
... { name: "jack"},
... { name: "windy"},
... { name: "wuyifan"}
... ])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("6115e6633e8322a9d326c240"),
ObjectId("6115e6633e8322a9d326c241"),
ObjectId("6115e6633e8322a9d326c242")
]
}
# 3.1.3 主键
ObjectId
自定义主键
复合主键
# 3.2 查询文档
db.collection.find(<query>, [projection])
Parameter | Type | Description |
---|---|---|
query | document | 可选。使用查询运算符指定选择筛选器。若要返回集合中的所有文档,请省略此参数或传递空文档 ( {} )。 |
projection | document | 可选。指定要在与查询筛选器匹配的文档中返回的字段(投影)。若要返回匹配文档中的所有字段, 请省略此参数。 |
方便下面案例,请先插入数据:
db.inventory.insertMany([ { item: "journal", qty: 25, size: { h: 14, w: 21, uom: "cm" }, status: "A" }, { item: "notebook", qty: 50, size: { h: 8.5, w: 11, uom: "in" }, status: "A" }, { item: "paper", qty: 100, size: { h: 8.5, w: 11, uom: "in" }, status: "D" }, { item: "planner", qty: 75, size: { h: 22.85, w: 30, uom: "cm" }, status: "D" }, { item: "postcard", qty: 45, size: { h: 10, w: 15.25, uom: "cm" }, status: "A" } ]);
# 3.2.1 查询所有
> db.inventory.find()
{ "_id" : ObjectId("6115ec3e3e8322a9d326c243"), "item" : "journal", "qty" : 25, "size" : { "h" : 14, "w" : 21, "uom" : "cm" }, "status" : "A" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c244"), "item" : "notebook", "qty" : 50, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "A" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c245"), "item" : "paper", "qty" : 100, "size" : { "h" : 8.5, "w" : 11, "uom" : "in" }, "status" : "D" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c246"), "item" : "planner", "qty" : 75, "size" : { "h" : 22.85, "w" : 30, "uom" : "cm" }, "status" : "D" }
{ "_id" : ObjectId("6115ec3e3e8322a9d326c247"), "item" : "postcard", "qty" : 45, "size" : { "h" : 10, "w" : 15.25, "uom" : "cm" }, "status" : "A" }
# 3.2.2 条件查询
EQUAL
db.inventory.find( { status: "D"} )
GT/LT/GTE/LTE
db.inventory.find( { qty: { $lt: 30 } } )
IN
db.inventory.find( { status: { $in: [ "A", "D" ] } } )
尽管可以使用
$or
操作符表示此查询,但在对同一字段执行相等性检查时,请使用$in
操作符而不是$or
操作符,因为$in
可以走索引,查询效率更高。AND
db.inventory.find( { status: "A", qty: { $lt: 30 } } )
OR
db.inventory.find( { $or: [ { status: "A" }, { qty: { $lt: 30 } } ] } )
AND & OR
db.inventory.find( { status: "A", $or: [ { qty: { $lt: 30 } }, { item: "journal" } ] } )
REGEX
db.inventory.find( { status: "A", $or: [ { qty: { $lt: 30 } }, { item: /^p/ } ] } )
MongoDB支持正则表达式
$regex
查询来执行字符串模式匹配。
# 3.2.3 投影查询
默认情况下,MongoDB 中的查询返回匹配文档中的所有字段。 要限制 MongoDB 发送给应用程序的数据量,可以包含一个 projection
文档以指定或限制要返回的字段。
fieldName: 1
:显示filedName
字段,_id
字段默认为 1fieldName: 0
:不显示fieldName
字段
如果 projection
中只有 0
,那就是排除掉指定的 field
。
# 仅查询 _id, item, status
db.inventory.find( { status: "A" }, { item: 1, status: 1 } )
# 仅查询 item, status
db.inventory.find( { status: "A" }, { item: 1, status: 1, _id: 0 } )
# 不要 status 和 instock 字段
db.inventory.find( { status: "A" }, { status: 0, instock: 0 } )
# 返回嵌入式文档中的特定字段
db.inventory.find(
{ status: "A" },
{ item: 1, status: 1, "size.uom": 1 }
)
# 禁止嵌入文档中的特定字段
db.inventory.find(
{ status: "A" },
{ "size.uom": 0 }
)
# 返回数组中的项目特定数组元素,-1 表示最后一个元素
db.inventory.find( { status: "A" }, { item: 1, status: 1, instock: { $slice: -1 } } )
对于包含数组的字段,MongoDB 提供以下用于操纵数组的投影运算符:
$elemMatch
、$slice
、$
。$elemMatch
、$slice
和$
是 projection 要包含在返回数组中的特定元素的唯一方法。 例如,您不能使用数组索引来投影特定的数组元素。 例如 {“ instock.0”: 1} 是不会返回数组的第一个元素的。
# 3.2.4 嵌入式文档查询
db.inventory.insertMany( [
{ item: "journal", instock: [ { warehouse: "A", qty: 5 }, { warehouse: "C", qty: 15 } ] },
{ item: "notebook", instock: [ { warehouse: "C", qty: 5 } ] },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 15 } ] },
{ item: "planner", instock: [ { warehouse: "A", qty: 40 }, { warehouse: "B", qty: 5 } ] },
{ item: "postcard", instock: [ { warehouse: "B", qty: 15 }, { warehouse: "C", qty: 35 } ] }
]);
- 上面插入的结构中,
instock
是一个数组,其中每一个元素都是一个 document。我们可以直接对数据里面的元素进行条件查询。
db.inventory.find( { "instock": { warehouse: "A", qty: 5 } } )
整个嵌入式/嵌套文档上的相等匹配要求与指定文档(包括字段 顺序)完全匹配。 例如,以下查询与 inventory 中的任何文档都不匹配
db.inventory.find( { "instock": { qty: 5, warehouse: "A" } } )
# 3.2.5 数组查询
数据准备:
db.inventory.insertMany([ { item: "journal", qty: 25, tags: ["blank", "red"], dim_cm: [ 14, 21 ] }, { item: "notebook", qty: 50, tags: ["red", "blank"], dim_cm: [ 14, 21 ] }, { item: "paper", qty: 100, tags: ["red", "blank", "plain"], dim_cm: [ 14, 21 ] }, { item: "planner", qty: 75, tags: ["blank", "red"], dim_cm: [ 22.85, 30 ] }, { item: "postcard", qty: 45, tags: ["blue"], dim_cm: [ 10, 15.25 ] } ]);
# 查询 tags 值是按指定顺序恰好具有两个元素"red" 和"blank"的数组
db.inventory.find( { tags: ["red", "blank"] } )
# 查询一个同时包含元素“red”和“blank”的数组,而不考虑顺序或该数组中的其他元素,用 $all
db.inventory.find( { tags: { $all: ["red", "blank"] } } )
# 查询元素中的 tag 有 “red” 的数组
db.inventory.find( { tags: "red" } )
# 查询数组 dim_cm 包含至少一个值大于25的元素的所有文档
db.inventory.find( { dim_cm: { $gt: 25 } } )
# 查询在 dim_cm 数组中包含至少一个同时大于 ($gt)22 和小于 ($lt) 30的元素的文档,用 $elemMatch
db.inventory.find( { dim_cm: { $elemMatch: { $gt: 22, $lt: 30 } } } )
# 查询数组 dim_cm 中第 2 个元素大于 25 的所有文档,用数组下表
db.inventory.find( { "dim_cm.1": { $gt: 25 } } )
# 数组 tags 具有 3 个元素的文档,用 $size
db.inventory.find( { "tags": { $size: 3 } } )
# 3.2.6 空字段和不存在的字段查询
平等过滤器
# {item:null} 查询匹配包含值是 null 的 item 字段或不包含 item 字段的文档 db.inventory.find( { item: null } )
类型检查
# {item:{$ type:10}} 查询只匹配包含 item 字段值为 null 的文档; 即 item 字段的值为 BSON Type 为 Null(类型编号 10): db.inventory.find( { item : { $type: 10 } } )
存在检查
# {item:{$ exists:false}} 查询与不包含 item 字段的文档匹配 db.inventory.find( { item : { $exists: false } } )
# 3.3 更新文档
db.collection.updateOne(<filter>, <update>, <options>)
:更新一个,没有则创建db.collection.updateMany(<filter>, <update>, <options>)
:更新多个,没有则创建db.collection.replaceOne(<filter>, <update>, <options>)
:替换一个db.collection.update(<filter>, <update>, <options>)
:更新一个或多个或替换,默认单个
db.collection.update(
<query>, <update>, {
upsert: <boolean>,
multi: <boolean>, # 是否更新多个
writeConcern: <document>,
collation: <document>,
arrayFilters: [ <filterdocument1>, ... ],
hint: <document|string> # MongoDB 4.2 后可用
} )
更新示例:
db.inventory.updateOne(
{ item: "paper" },
{
$set: { "size.uom": "cm", status: "P" },
$currentDate: { lastModified: true }
}
)
$set
:修改一个字段,如果该字段不存在,则创建该字段$currentDate
:将lastModified
字段更改为当前时间值,如果该字段不存在,则创建
替换示例:
- 要替换
_id
字段以外的文档的全部内容,可以将一个全新的文档作为第二个参数传递给db.collection.replaceOne
。
# 替换了 inventory 集合中的第一个文件,其中 item 为 "paper"
db.inventory.replaceOne(
{ item: "paper" },
{ item: "paper", instock: [ { warehouse: "A", qty: 60 }, { warehouse: "B", qty: 40 } ] }
)
注意
- 替换
- 当替换一个文档时,替换文档必须只包含字段/值对,即不包括更新操作符表达式。
- 替换文档可以具有与原始文档不同的字段。在替换文档中,由于
_id
字段是不可变的,因此可以省略_id
字段。但是,如果你确实包含_id
字段,则它必须与当前值具有相同的值。
- 原子性
- MongoDB 中的所有写操作都是单个文档级别上的原子操作。
- _id
_id
值不可变,也无法将现有文档替换为具有不同_id
值的文档
# 3.4 删除文档
db.collection.deleteMany(<filter>)
:删除多个db.collection.deletOne(<filter>)
:删除单个db.collection.remover(<filter>, <justone>)
:删除单个或多个,用justone
字段来指定是否删除单个
db.inventory.deleteOne( { status: "D" } )