实验准备
打开实验环境,在终端输入如下命令启动 MongoDB 服务,进入命令行交互客户端:
$ cd
$ sudo service mongodb start
$ mongo
条件操作符
在前面的课程中,我们学到了查询文档使用 find 函数来完成:
find() 查询集合中的全部文档
find({name: 'James'}) 查询 name 字段值为 'James' 的文档
find({name: 'James', age: 22}) 查询 name 字段值为 'James' 且 age 字段值为 22 的文档
find({}, {_id: 0, name: 1}) 只查询全部文档的 name 字段
接下来我们学习 find 函数的其它用法,首先使用 query 数据创建 stu 集合:
> use query
switched to db query
> db.createCollection('stu')
{ "ok" : 1 }
向集合中插入数据:
> db.stu.insertMany([
... { "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 },
... { "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 },
... { "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 },
... { "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 },
... { "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 },
... { "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 },
... { "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
... ])
{
"acknowledged" : true,
"insertedIds" : [
ObjectId("5e4df26740faabaac0fd3de6"),
ObjectId("5e4df26740faabaac0fd3de7"),
ObjectId("5e4df26740faabaac0fd3de8"),
ObjectId("5e4df26740faabaac0fd3de9"),
ObjectId("5e4df26740faabaac0fd3dea"),
ObjectId("5e4df26740faabaac0fd3deb"),
ObjectId("5e4df26740faabaac0fd3dec")
]
}
>
可以复制下面的代码到实验环境:
db.stu.insertMany([
{ "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 },
{ "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 },
{ "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 },
{ "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 },
{ "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 },
{ "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 },
{ "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
])
MongoDB 中的条件操作符有:
$gt:大于
$lt:小于
$gte:大于等于
$lte:小于等于
查询数学成绩大于 90 分的数据:
> db.stu.find(
... {数学: {$gt: 90}}
... )
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
>
查询语文成绩小于 90 分的男学生的数据:
> db.stu.find(
... {语文: {$lt: 90},
... gender: '男'
... }
... )
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
>
操作截图如下:
image.png
将 {$gt: 90} 作为 “数学” 的 value 值,即可查询 “数学” 字段的值大于 90 的文档。
$type 操作符
MongoDB 提供很多数据类型支持,下表所示为常见的几种数据类型:
image.png
其中的代号和别名有什么用呢?我们来举例说明。
首先创建一些数据,在 query 数据库的 type 集合中添加如下数据:
> db.type.insert([
... {value: 3.14},
... {value: '三点一四'},
... {value: true},
... {value: new Date()}
... ])
BulkWriteResult({
"writeErrors" : [ ],
"writeConcernErrors" : [ ],
"nInserted" : 4,
"nUpserted" : 0,
"nMatched" : 0,
"nModified" : 0,
"nRemoved" : 0,
"upserted" : [ ]
})
> db.type.find()
{ "_id" : ObjectId("5e4e05dc551706ec190a070e"), "value" : 3.14 }
{ "_id" : ObjectId("5e4e05dc551706ec190a070f"), "value" : "三点一四" }
{ "_id" : ObjectId("5e4e05dc551706ec190a0710"), "value" : true }
{ "_id" : ObjectId("5e4e05dc551706ec190a0711"), "value" : ISODate("2020-02-20T04:06:52.250Z") }
>
如上所示,我们在 type 集合中添加了四条数据,其中 value 字段的值的数据类型分别是:浮点数、字符串、布尔值、日期。其中 new Date() 是用来创建当前时间的 Date 对象的方法。
如果要查询 type 集合中 value 字段值的数据类型为字符串的数据,可以使用 $type 操作符:
> db.type.find(
... {value: {$type: 'string'}}
... )
{ "_id" : ObjectId("5e4e05dc551706ec190a070f"), "value" : "三点一四" }
>
其中 $type 后面的 'string' 就是上表中字符串 “别名” 那一列的值。
同样的查询还可以这样写:
> db.type.find(
... {value: {$type: 2}}
... )
{ "_id" : ObjectId("5e4e05dc551706ec190a070f"), "value" : "三点一四" }
>
其中 type 操作符的作用就是查询某数据类型的数据。那么,如果要查询 value 字段值是浮点数或字符串的类型的数据呢?可以这样:
> db.type.find(
... {value:
... {$type: [1, 2]}
... })
{ "_id" : ObjectId("5e4e05dc551706ec190a070e"), "value" : 3.14 }
{ "_id" : ObjectId("5e4e05dc551706ec190a070f"), "value" : "三点一四" }
>
以上操作的截图如下:
image.png
限制查询数量
当我们查询数据的时候,如果只想看其中的几条数据,可以对查询结果进行限制,这是数据库的基本功能。在 MongoDB 中自然也提供了这样的函数。
limit 函数限制查询数量
查询 query 数据库的 stu 集合中前两条数据:
> db.stu.find()
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
> db.stu.find().limit(2)
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
>
查询几条数据,就将数字作为 limit 函数的参数。
skip 略过一些数据
如果要查询第 3、4 、5 条数据,也就是要略过前两条数据后,查询前 3 条数据,可以使用 skip 函数:
> db.stu.find().skip(2).limit(3)
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
>
其中 limit 和 skip 函数的顺序可以随意调换,上面的需求可以这样写:
> db.stu.find().limit(3).skip(2)
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
排序
使用 find 函数查询的结果默认根据 _id 字段升序(由小到大)排序,我们可以使用 sort 函数设置排序规则。用法是:{$sort: {字段名: ±1}} ,下面举例说明。
查询 stu 集合中的全部数据,根据数学成绩升序(由小到大)排序:
> db.stu.find().sort(
... {数学: 1}
... )
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
>
使用 sort 函数可以实现多级排序。
查询 stu 集合中的全部数据,首先根据数学成绩升序(由小到大)排序,次级排序规则是根据语文成绩由大到小降序排序:
> db.stu.find().sort(
... {数学: 1, 语文: -1}
... )
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de7"), "name" : "大黄", "gender" : "女", "语文" : 83, "数学" : 20 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de9"), "name" : "大绿", "gender" : "女", "语文" : 85, "数学" : 34 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de6"), "name" : "大红", "gender" : "男", "语文" : 83, "数学" : 81 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3dea"), "name" : "大橙", "gender" : "男", "语文" : 86, "数学" : 88 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3deb"), "name" : "大青", "gender" : "女", "语文" : 87, "数学" : 93 }
{ "_id" : ObjectId("5e4df26740faabaac0fd3de8"), "name" : "大蓝", "gender" : "男", "语文" : 84, "数学" : 99 }
>
仔细观察这两个命令的查询结果,后一个实现了多级排序,即如果数学成绩相同,语文成绩好的排在前面。
综合使用的例子
查询语文成绩最好的学生数据:
> db.stu.find().sort(
... {语文: -1}
... ).limit(1)
{ "_id" : ObjectId("5e4df26740faabaac0fd3dec"), "name" : "大紫", "gender" : "男", "语文" : 99, "数学" : 20 }
>
查询数学成绩最差的两位学生的名字和数学成绩:
> db.stu.find({}, {_id: 0, name: 1, 数学: 1}).sort(
... {数学: 1}
... ).limit(2)
{ "name" : "大黄", "数学" : 20 }
{ "name" : "大紫", "数学" : 20 }
>
操作截图如下:
image.png
索引
MongoDB 在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可能要花费几十秒甚至几分钟,这无疑对网站的性能是非常致命的。
索引是特殊的数据结构,存储在一个易于遍历读取的数据集合中。索引是对数据库集合中一个文档或多个文档的值进行排序的一种结构,通常能够极大地提高查询的效率。
作为 MongoDB 基础课程,仅对索引的作用和最简单的操作进行讲解。
查询索引
索引针对字段进行设置,默认情况下,集合的 _id 字段会被添加索引。使用 getIndexes 函数查看集合中的索引:
> db.stu.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "query.stu"
}
]
>
添加索引
使用集合的 ensureIndex 函数为字段添加索引。举例说明,为 stu 集合的 name 字段添加索引:
> db.stu.createIndex({name: 1})
{
"createdCollectionAutomatically" : false,
"numIndexesBefore" : 1,
"numIndexesAfter" : 2,
"ok" : 1
}
> db.stu.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "query.stu"
},
{
"v" : 2,
"key" : {
"name" : 1
},
"name" : "name_1",
"ns" : "query.stu"
}
]
>
操作截图如下:
查看索引所占空间
既然添加索引有诸多好处,为何不为每个字段都默认设置索引呢?天下没有免费的午餐,添加索引是需要付出存储空间成本的。
查看当前 stu 集合的索引所占用的存储空间,单位是比特:
> db.stu.totalIndexSize()
32768
>
占用的空间是 32KB 。
删除索引
删除索引使用 dropIndex 函数:
> db.stu.dropIndex({name: 1})
{ "nIndexesWas" : 2, "ok" : 1 }
> db.stu.getIndexes()
[
{
"v" : 2,
"key" : {
"_id" : 1
},
"name" : "_id_",
"ns" : "query.stu"
}
]
> db.stu.totalIndexSize()
16384
>
网友评论