实验准备
打开实验环境,在终端输入如下命令启动 MongoDB 服务,进入命令行交互客户端:
$ cd
$ sudo service mongodb start
$ mongo
创建数据
首先创建 shiyanlou 数据库和 player 集合,在其中插入一些描述球员的数据文档:
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
> use shiyanlou
switched to db shiyanlou
> db.player.insert([
... {name: 'James', age: 33, addr: ['Mim', 'Cle']},
... {name: 'Wade', age: 34, addr: ['Mim', 'Chi']},
... {name: 'Curry', age: 35, addr: ['Gsw']}
... ])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 3,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> show dbs
admin 0.000GB
config 0.000GB
local 0.000GB
shiyanlou 0.000GB
> show collections
player
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
>
如上所示,我们已经创建了 shiyanlou 数据库和 player 集合,并且添加了三条数据(也就是文档)。
pretty 函数
使用 pretty 函数可以使查询结果的展示更加美观:
> db.player.find().pretty()
{
"_id" : ObjectId("5e4b6d358b5daaaae4d74f88"),
"name" : "James",
"age" : 33,
"addr" : [
"Mim",
"Cle"
]
}
{
"_id" : ObjectId("5e4b6d358b5daaaae4d74f89"),
"name" : "Wade",
"age" : 34,
"addr" : [
"Mim",
"Chi"
]
}
{
"_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"),
"name" : "Curry",
"age" : 36,
"addr" : [
"Gsw"
]
}
>
这种带缩进的打印数据的格式可以设置为默认格式。
在操作系统安装 MongoDB 时,会在家目录 /home/shiyanlou 下创建一个隐藏文件 .mongorc.js ,它是用来设置 MongoDB 客户端的配置文件,该文件默认是空文件。退出 MongoDB 客户端,执行如下命令添加配置项到此文件,重新打开 MongoDB 客户端即可生效。
操作步骤如下:
输入 exit 命令敲回车退出 MongoDB 客户端,回到终端
在终端执行如下命令添加配置项:
$ echo "DBQuery.prototype._prettyShell = true" >> ~/.mongorc.js
执行 mongo 命令再次进入 MongoDB 客户端
执行 use shiyanlou 命令进入 shiyanlou 数据库
执行 find 函数查看数据
操作截图如下:
如上图所示,find 函数在使用时增加了一个参数 {name: 'Wade'} ,下面的步骤会讲到。
以上操作是为了告诉大家如何添加配置使得查询数据时打印信息更加美观。通常这一项配置是不需要的,我们可以按照 JavaScript 的注释方法,在需要注释的代码行前面加双斜线对其进行注释:
update 函数
查询全部数据我们可以使用 db.Collection.find() 函数,但是在数据量极大的情况下,这个函数是特别耗内存的操作,有一定可能会导致程序卡死。通常而言,我们会查询一部分符合要求的数据。例如查询名字是 Wade 的球员数据:
> db.player.find({name: 'Wade'})
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
>
其中参数为键值对格式,提供查询条件,当然也可以提供多个查询条件。例如查询 name 为 Curry 且 age 为 35 的球员:
> db.player.find(
... {name: 'Curry', age: 35}
... )
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
>
更新文档可以使用 update 函数,它需要两个参数:第一个用来确定更新哪一条或哪几条文档,第二个用来提供更新的字段及其值。
举例说明,将 name 为 Curry 的文档中的 age 修改为 36(原值为 35):
> db.player.update(
... {name: 'Curry'},
... {$set:
... {age: 36}
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.player.find( {name: 'Curry'} ).pretty()
{
"_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"),
"name" : "Curry",
"age" : 36,
"addr" : [
"Gsw"
]
}
>
如上所示,update 函数的第一个参数很好理解,它与 find 函数所用参数的一样。第二个参数为 {set 叫做操作符,它作为 key 用来设置字段值,value 是一个嵌套键值对。在 MongoDB 中有很多操作符。
save 函数
通常我们创建文档时使用 insert 函数,此外还可以使用 save 函数。举例如下:
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 36, "addr" : [ "Gsw" ] }
> db.player.save(
... {name: 'Kobe', age: 37, addr: ['Tor', 'Los']}
... )
WriteResult({ "nInserted" : 1 })
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 36, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
>
在创建文档时,save 函数与 insert 函数的作用和用法相同。那么既然有了 insert 方法,为何还要提供 save 方法呢?因为后者除了创建文档,还能修改文档。
与 update 函数不同,save 函数修改文档只需要提供一组键值对,也就是说,创建和修改都只需要提供一组键值对。那么区别在哪里呢?就是键值对中有没有 _id 字段。如果没有,则创建新的文档,如果有,就修改 _id 字段所属的文档。
下面我们修改 _id 值为 ObjectId("5e4b6d358b5daaaae4d74f8a") 的文档,将 age 从 36 改为 35 :
> db.player.save(
... {
... _id: ObjectId("5e4b6d358b5daaaae4d74f8a"),
... name: 'Curry',
... age: 35,
... addr: ['Gsw']
... }
... )
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
> db.player.find( {name: 'Curry'} ).pretty()
{
"_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"),
"name" : "Curry",
"age" : 35,
"addr" : [
"Gsw"
]
}
>
注意,save 函数的本质是保存文档,参数键值对中需要提供该文档的所有数据,新的数据会覆盖原有数据。
查询操作
在使用 find 函数查询文档时,需要提供一组键值对:
> db.player.find(
... {name: 'James'}
... )
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
> db.player.find(
... {name: 'James', age: 33}
... )
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
> db.player.find(
... {name: 'James', age: 44}
... )
>
如上所示,参数键值对中有多组 Key-Value 时,需要同时满足全部条件,否则查询结果为空。如果要查询 name 为 James 或 age 为 35 的文档呢?在我们创建的数据中,有两条符合的文档。这时候要用到 set 操作符,它的作用是修改字段值。这里的 $or 操作符的作用就是实现 “或” 关系,使得 find 函数的参数可以接受两组键值对,满足任意一项即可。
具体用法如下:
> db.player.find(
... { $or: [
... {name: 'James'},
... {age: 35}
... ]}
... )
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
>
需要注意的是匹配条件以多个键值对的形式存在,并且放在一个列表里作为 $or 的 value 。这样查询结果就是符合任意条件的全部文档。
现在有一个问题,每次查询的结果都是每条文档显示全部字段值,如果要只想查询 age 为 33 的文档中 name 的值,要怎么做呢?
在使用 find 函数查询操作时,还可以提供第二个参数限定打印信息。如果只打印查询结果的 name 字段值,提供 {name: 1} 作为第二个参数即可。需要注意的是文档的 _id 字段默认是会打印出来的,所以还要额外将其设置为 0 。
具体操作如下:
> db.player.find(
... {age: 33},
... {name: 1}
... )
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James" }
> db.player.find(
... {age: 33},
... {_id: 0, name: 1}
... )
{ "name" : "James" }
>
删除文档
前面的步骤中我们已经学习了文档的创建、修改、查询的基本操作,最后介绍一下如何删除文档。
删除文档使用 deleteOne 和 deleteMany 函数,顾名思义,它们用于删除一条或多条数据。参数和查询函数 find 一样,提供一组键值对作为限制条件,将查询结果进行删除操作。
首先创建一组具有同一字段值的文档,我们使用 save 函数来实现:
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
> db.player.save({name: 'Test'})
WriteResult({ "nInserted" : 1 })
> db.player.save({name: 'Test'})
WriteResult({ "nInserted" : 1 })
> db.player.save({name: 'Test'})
WriteResult({ "nInserted" : 1 })
> db.player.save({name: 'Test'})
WriteResult({ "nInserted" : 1 })
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
{ "_id" : ObjectId("5e4b8480e009482777d42429"), "name" : "Test" }
{ "_id" : ObjectId("5e4b8480e009482777d4242a"), "name" : "Test" }
{ "_id" : ObjectId("5e4b8481e009482777d4242b"), "name" : "Test" }
{ "_id" : ObjectId("5e4b8487e009482777d4242c"), "name" : "Test" }
>
如上所示,save 函数的参数只有一个 name 字段,没有 _id 字段,所有每次调用此函数都会新建文档。
现在执行 deleteOne 删除一条 name 值为 Test 的文档:
> db.player.deleteOne(
... {name: 'Test'}
... )
{ "acknowledged" : true, "deletedCount" : 1 }
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
{ "_id" : ObjectId("5e4b8480e009482777d4242a"), "name" : "Test" }
{ "_id" : ObjectId("5e4b8481e009482777d4242b"), "name" : "Test" }
{ "_id" : ObjectId("5e4b8487e009482777d4242c"), "name" : "Test" }
>
执行 deleteMany 函数删除全部符合条件的文档:
> db.player.deleteMany(
... {name: 'Test'}
... )
{ "acknowledged" : true, "deletedCount" : 3 }
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
>
这就是删除操作,大家可以思考一下,对于更复杂的删除需求,如何使用 $or 操作符来实现。
例如删除 name 为 James 或 age 等于 35 的文档,删除多条数据首先要选择 deleteMany 函数,可以这样操作:
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f88"), "name" : "James", "age" : 33, "addr" : [ "Mim", "Cle" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f8a"), "name" : "Curry", "age" : 35, "addr" : [ "Gsw" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
> db.player.deleteMany(
... {$or: [
... {name: 'James'},
... {age: 35}
... ]}
... )
{ "acknowledged" : true, "deletedCount" : 2 }
> db.player.find()
{ "_id" : ObjectId("5e4b6d358b5daaaae4d74f89"), "name" : "Wade", "age" : 34, "addr" : [ "Mim", "Chi" ] }
{ "_id" : ObjectId("5e4b7ab4e009482777d42425"), "name" : "Kobe", "age" : 37, "addr" : [ "Tor", "Los" ] }
>