程序员书籍笔记 程序员书籍笔记
  • JavaScript
  • HTML/CSS
  • PHP
  • Python
  • Go
  • 数据库
  • 容器
  • 微服务
  • 消息队列
  • 搜索引擎
  • 大数据
  • 鸟哥Linux私房菜
其他
  • 大数据
  • 深度学习
APP下载 (opens new window)
GitHub (opens new window)
  • JavaScript
  • HTML/CSS
  • PHP
  • Python
  • Go
  • 数据库
  • 容器
  • 微服务
  • 消息队列
  • 搜索引擎
  • 大数据
  • 鸟哥Linux私房菜
其他
  • 大数据
  • 深度学习
APP下载 (opens new window)
GitHub (opens new window)
  • 数据库

    • MySQL必知必会

    • MongoDB权威指南

      • 入门
      • 文档创建、更新、删除
        • 插入并保存文档
        • 删除文档
        • 更新文档
          • 使用修改器
          • upsert操作
          • 更新多个文档
        • 瞬间完成
          • 捕获常规错误
        • 请求和连接
      • 查询
      • 索引
      • 聚合
  • 容器

  • 微服务

  • 消息队列

  • 搜索引擎

  • 大数据

  • 框架和软件
  • 数据库
  • MongoDB权威指南
小游
2021-05-12

文档创建、更新、删除

# 插入并保存文档

直接使用inset命令插入即可。这里我们插入了 one 数据库

db.one.insert({"title":"小游"})
1

可以看到,数据库里面多了一条数据

image-20210512185312845

# 删除文档

下面这个会删除title 为 小游 的文档,如果不加条件就会删除所有的集合

db.one.remove({"title":"小游"})
1

# 更新文档

使用 update 语句来更新文档,这个语句有两个参数,第一个是查询的文档,第二个是修改那些地方

db.one.update({"title":"小游"},{"title":"更新"})
1

这里会查询title为小游的文档,然后修改title为更新。这个更新操作是原子操作,并发的时候不会相互干扰

# 使用修改器

如果我们的文档只有一部分需要更新,我们则可以使用更新修改器,下面来介绍这些修改器。

# $set修改器

这个可以修改一个指定的键的值,如果不存在则会新建

{
    "_id" : ObjectId("609bb9b56ac4daf1370a10c5"),
    "name" : "小游"
}
1
2
3
4

我们在这个数据的基础上加上一个年龄字段

db.test.update({"name":"小游"},{"$set":{"age":0}})
1
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 0.0
}
1
2
3
4
5

我们修改年龄字段

db.test.update({"name":"小游"},{"$set":{"age":1}})
1
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 1.0
}
1
2
3
4
5
6

我们使用 unset 删除这个字段

db.test.update({"name":"小游"},{"$unset":{"age":1}})
1

# 增加和减少

/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 2.0
}
1
2
3
4
5
6

我们使用inc来增加或修改值,比如我们+50

db.test.update({"name":"小游"},{"$inc":{"age":50}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 52.0
}	
1
2
3
4
5
6
7

也可以减少值

db.test.update({"name":"小游"},{"$inc":{"age":-20}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "age" : 32.0
}
1
2
3
4
5
6
7

# 数组修改

上面两个只能修改基本数据类型,如果我们想修改数组怎么办?可以使用push。初始数据如下

/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游"
}
1
2
3
4
5

如果指定的键不存在,则会新建,如果存在就加到数组末尾

db.test.update({"name":"小游"},{"$push":{"like":"写代码"}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "like" : [ 
        "写代码"
    ]
}
1
2
3
4
5
6
7
8
9

如果我们不想插入相同的数据,那么我们使用addToSet来实现。我们这里执行一次后可以看到,数据还是没有改变。

db.test.update({"name":"小游"},{"$addToSet":{"like":"写代码"}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "like" : [ 
        "写代码"
    ]
}
1
2
3
4
5
6
7
8
9

我们可以把addToSet与each进行结合,可以添加多个不同的值。这里addToSet可以避免我们添加重复的值,比如写代码这个重复的值

db.test.update({"name":"小游"},{"$addToSet":{"like":{"$each":["写代码","看番","玩游戏"]}}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "like" : [ 
        "写代码", 
        "看番", 
        "玩游戏"
    ]
}
1
2
3
4
5
6
7
8
9
10
11

如果想删除数组中某个元素,我们可以使用pull

db.test.update({"name":"小游"},{"$pull":{"like":"玩游戏"}})
/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "like" : [ 
        "写代码", 
        "看番"
    ]
}
1
2
3
4
5
6
7
8
9
10

# 数组定位修改

如果数组有多个值,我们只想对其中的一部分进行操作,可以通过定位操作符来进行操作

/* 1 */
{
    "_id" : ObjectId("609bb9f16ac4daf1370a10c6"),
    "name" : "小游",
    "like" : [ 
        {
            "name" : "关于我转成成为史莱姆这档事",
            "score" : 10
        }, 
        {
            "name" : "堀与宫村",
            "score" : 20
        }
    ]
}
1
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
        }
    ]
}
1
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
        }
    ]
}
1
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" : []
}
1
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
}
1
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" : []
}
1
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中,插入后再进行查询是一定能查到的。手动复现这个行为并不容易,但是在繁忙的服务器上,交错的插入/査找就显得稀松平常了。当开发者用一个线程插入数据,用另一个线程检查是否成功插入时,就会经常遇到这种问题。有那么一两秒钟时间,好像根本就没插入数据,但随后数据又突然冒出来。

编辑 (opens new window)
上次更新: 2021/05/13, 16:14:14
入门
查询

← 入门 查询→

Theme by Vdoing | Copyright © 2021-2021 小游
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式