文档创建、更新、删除
# 插入并保存文档
直接使用inset命令插入即可。这里我们插入了 one
数据库
db.one.insert({"title":"小游"})
可以看到,数据库里面多了一条数据
# 删除文档
下面这个会删除title 为 小游 的文档,如果不加条件就会删除所有的集合
db.one.remove({"title":"小游"})
# 更新文档
使用 update
语句来更新文档,这个语句有两个参数,第一个是查询的文档,第二个是修改那些地方
db.one.update({"title":"小游"},{"title":"更新"})
这里会查询title为小游的文档,然后修改title为更新。这个更新操作是原子操作,并发的时候不会相互干扰
# 使用修改器
如果我们的文档只有一部分需要更新,我们则可以使用更新修改器,下面来介绍这些修改器。
# $set修改器
这个可以修改一个指定的键的值,如果不存在则会新建
{
"_id" : ObjectId("609bb9b56ac4daf1370a10c5"),
"name" : "小游"
}
2
3
4
我们在这个数据的基础上加上一个年龄字段
db.test.update({"name":"小游"},{"$set":{"age":0}})
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"age" : 0.0
}
2
3
4
5
我们修改年龄字段
db.test.update({"name":"小游"},{"$set":{"age":1}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"age" : 1.0
}
2
3
4
5
6
我们使用 unset
删除这个字段
db.test.update({"name":"小游"},{"$unset":{"age":1}})
# 增加和减少
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"age" : 2.0
}
2
3
4
5
6
我们使用inc来增加或修改值,比如我们+50
db.test.update({"name":"小游"},{"$inc":{"age":50}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"age" : 52.0
}
2
3
4
5
6
7
也可以减少值
db.test.update({"name":"小游"},{"$inc":{"age":-20}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"age" : 32.0
}
2
3
4
5
6
7
# 数组修改
上面两个只能修改基本数据类型,如果我们想修改数组怎么办?可以使用push。初始数据如下
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游"
}
2
3
4
5
如果指定的键不存在,则会新建,如果存在就加到数组末尾
db.test.update({"name":"小游"},{"$push":{"like":"写代码"}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
"写代码"
]
}
2
3
4
5
6
7
8
9
如果我们不想插入相同的数据,那么我们使用addToSet来实现。我们这里执行一次后可以看到,数据还是没有改变。
db.test.update({"name":"小游"},{"$addToSet":{"like":"写代码"}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
"写代码"
]
}
2
3
4
5
6
7
8
9
我们可以把addToSet与each进行结合,可以添加多个不同的值。这里addToSet可以避免我们添加重复的值,比如写代码这个重复的值
db.test.update({"name":"小游"},{"$addToSet":{"like":{"$each":["写代码","看番","玩游戏"]}}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
"写代码",
"看番",
"玩游戏"
]
}
2
3
4
5
6
7
8
9
10
11
如果想删除数组中某个元素,我们可以使用pull
db.test.update({"name":"小游"},{"$pull":{"like":"玩游戏"}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
"写代码",
"看番"
]
}
2
3
4
5
6
7
8
9
10
# 数组定位修改
如果数组有多个值,我们只想对其中的一部分进行操作,可以通过定位操作符来进行操作
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 10
},
{
"name" : "堀与宫村",
"score" : 20
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
比如我们修改第一个的评分为5
db.test.update({"name":"小游"},{"$set":{"like.0.score":5}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 20
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
但是很多情况下,我们不知道需要,这个时候我们可以使用定位操作符 $ 来修改,比如我们修改第二个评分为100
db.test.update({"like.name":"堀与宫村"},{"$set":{"like.$.score":100}})
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 100.0
}
]
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# upsert操作
upert是一种特殊的更新。要是没有文档符合更新条件,就会以这个条件和更新文档为基础创建一个新的文档。如果找到了匹配的文档,则正常更新。 upsert非常方便,不必预置集合,同一套代码可以既创建又更新文档。下面我们添加一条记录。想使用upsert,只需要在update语句第三个参数设置true就可以了
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 100.0
}
]
}
db.test.update({"name":"张三"},{"name":"张三","like":[]},true)
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 100.0
}
]
}
/* 2 */
{
"_id" : ObjectId("609bc3d3eb600e01fcfb175c"),
"name" : "张三",
"like" : []
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# 更新多个文档
如果我们想看一下多文档操作到底更新多少文档,可以执行getLastError命令,n表示更新的条数
db.runCommand({getLastError:1})
/* 1 */
{
"connectionId" : 34,
"n" : 0,
"syncMillis" : 0,
"writtenTo" : null,
"err" : null,
"ok" : 1.0
}
2
3
4
5
6
7
8
9
10
如果想返回已更新的文档,可以使用findAndModify操作
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 100.0
}
]
}
db.runCommand({
"findAndModify":"test",
"update": {"like":[]}
})
/* 1 */
{
"lastErrorObject" : {
"n" : 1,
"updatedExisting" : true
},
"value" : {
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"name" : "小游",
"like" : [
{
"name" : "关于我转成成为史莱姆这档事",
"score" : 5.0
},
{
"name" : "堀与宫村",
"score" : 100.0
}
]
},
"ok" : 1.0
}
/*最后查询的结果如下*/
/* 1 */
{
"_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
"like" : []
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
这个语句可以返回更新前的文档
# 瞬间完成
前面的三个操作(插入,删除和更新)都是瞬间完成的,不需要等等数据库响应(客户端发送后就不会管数据库有没有响应)
# 安全操作
MongoDB也提供了安全版本,避免数据插入出错。我们可以执行完毕后立即运行 getLastError
命令,来检查是否执行成功。
# 捕获常规错误
安全操作不仅能对付前面那种世界末日的场景,也是一种调试数据库“奇怪”行为的好方法。即便安全操作最后会在生产环境中移除,但是在开发过程中还是应该大量地使用。这样可以避免很多常见的数据库使用错误,最常见的就是键重复的错误。键重复错误经常发生在试图插入一个其"id值已被占用的文档。 MongoDB中不允许一个集合中有多个"_id"值一样的文档。
# 请求和连接
数据库会为每一个MongoDB数据库创建一个队列。当客户端放到请求的时候,会放到队尾。只有队列中请求都执行完毕。
注意,每个连接都有独立的队列,要是打开两个 shell,就有两个数据库连接。在个shel中执行插入,之后在另一个 shell中进行查询不一定能得到插入的文档。然而,在同一个 shell I中,插入后再进行查询是一定能查到的。手动复现这个行为并不容易,但是在繁忙的服务器上,交错的插入/査找就显得稀松平常了。当开发者用一个线程插入数据,用另一个线程检查是否成功插入时,就会经常遇到这种问题。有那么一两秒钟时间,好像根本就没插入数据,但随后数据又突然冒出来。