MongoDB - M001第五章 - 索引和聚合管道
聚合框架
-
聚合框架:只是在MongoDB中查询数据的另一种方式。聚合操作放在
[]
中 -
聚合框架语法:采用管道的形式,其中阶段按照它们列出的顺序执行。
-
MongoDB Aggregation Framework($group)(计算&重塑数据) > MongoDB Query Language(MQL)(过滤&更新数据)
-
MQL和聚合框架写法区别(Eg: 查找所有将 Wifi 作为便利设施之一的 文档。只在结果游标中包含价格 和 地址)
-
// MQL(MongoDB Query Language:) db.listingsAndReviews.find({ "amenities": "Wifi" }, { "price": 1, "address": 1, "_id": 0 }).pretty() // 聚合框架: db.listingsAndReviews.aggregate([ { "$match": { "amenities": "Wifi" } }, { "$project": { "price": 1, "address": 1, "_id": 0 }}]).pretty()
-
-
使用聚合而不是MQL查找的原因:
-
因为有时候可能用聚合:比如分组操作,以某种方式修改数据。
-
并不是总是只需要过滤出正确的文档。
-
可以执行除了 查找、投影数据 以外的操作,比如使用聚合进行计算。
-
它通过计算、重塑、重组数据的能力 超越了 MQL的 过滤能力。(聚合框架允许我们通过使用\(group、\)sum等阶段来计算和重塑数据。)
-
-
管道操作顺序
- 聚合框架用作管道,管道中的操作顺序很重要
-
聚合管道数据
-
数据在管道中是如何处理的?
- 我们在管道的一端,将数据提供给管道->我们描述了该管道将如何使用聚合阶段处理我们的数据->转换后的数据出现在管道末端。
-
聚合管道中的数据存在于管道内,本质上不会修改原始数据
-
聚合框架例子
-
Eg1: 每个_id按照不同类别的price去计数:
"total": { "$sum": "$price" }
-
Eg2: 在集合中查找一个文档,并且只 在结果游标中包含地址 字段。
db.listingsAndReviews.findOne({ },{ "address": 1, "_id": 0 })
-
Eg3: 仅 投影每个文档的地址 字段值,然后将所有文档分组为每个 address.country 值的一个文档,并为每个组中的每个文档计数一个。
-
db.listingsAndReviews.aggregate([ { "$project": { "address": 1, "_id": 0 }}, { "$group": { "_id": "$address.country", "count": { "$sum": 1 } } } ])
-
-
Eg4: What room types are present in the sample_airbnb.listingsAndReviews collection? 字段:room_type:"xxx"
-
db.listingsAndReviews.aggregate([ { "$project": { "room_type": 1}}, { "$group": { "_id": "$room_type", "count": { "$sum": 1 } } } ]) Answer: db.listingsAndReviews.aggregate([ { "$group": { "_id": "$room_type" } }]) output: ... [ { _id: 'Entire home/apt', count: 3489 }, { _id: 'Private room', count: 1983 }, { _id: 'Shared room', count: 83 } ]
-
-
Eg5: 以下哪个命令将返回sample_training.companies集合中5 家历史最悠久的公司的名称和成立年份 ?
-
// 错 db.companies.find({}, { "name": 1, "founded_year": 1 }). sort({ "founded_year": 1 }).limit(5) // 此命令缺少搜索条件,这意味着将包含空值。排除空值并不总是必要的,因为无论如何将这些类型的值存储在 MongoDB 中并不是最佳实践。所以有很多集合没有空值。然而,这个集合包含空值,我们需要从我们的游标中排除它们,否则在我们排序时它们将是我们的最小值。 // 对 db.companies.find({ "founded_year": {"$ne":null}},{"name":1,"founded_year": 1}).sort({ "found_year": 1 }).limit(5) // 我们首先必须过滤出成立年份不为空的文档,然后投影我们要查找的字段,即名称, 在本例中为founded_year。然后我们按升序对光标进行排序,因此第一个结果将具有found_year字段的最小值。最后,我们将结果限制在游标中的前5 个文档,从而获得 此集合中最古老的5 个公司。 // 对 db.companies.find({ "founded_year": {"$ne":null}},{"name":1,"founded_year": 1}).limit(5).sort({ "found_year": 1 }) // 虽然limit()和sort()方法未按正确顺序列出,但 MongoDB 在执行查询时会翻转它们的顺序,提供问题提示正在寻找的结果。 // 错 db.companies.find({ "name":1,"founded_year": 1}).sort({ "founded_year": 1 }).limit(5) // 看起来查询过滤器具有find()命令的投影部分 应该具有的值,并且此命令中没有使用投影。所以这个命令将返回所有 name 等于 1并且created_year也等于1 的公司,不幸的是它错过了问题提示的要求。
-
sort() & limit()
-
sort()
&limit()
是游标方法,其他的游标方法有:pretty()
,count()
。-
游标方法不适用于:存储在数据库中的数据,它应用于位于游标中的结果集。
-
这两个一般结合使用,先执行
sort
再执行limit
,如果``cursor.limit().sort()了,内部执行的还是
cursor.sort().limit()` 。
-
-
sort排序语法:
db.COLLECTION_NAME.find().sort({KEY:1,key2:-1});
- 其中 1 为升序排列,而-1为降序排列
-
Eg1:
-
// 按照人口递增顺序排序,返回第一个文档 = 取人口最少的文档 db.zips.find().sort({ "pop": 1 }).limit(1) // 按照人口递增减序排序,返回第一个文档 = 取人口最多的文档 db.zips.find().sort({ "pop": -1 }).limit(1) // 多个排序 db.zips.find().sort({ "pop": 1, "city": -1 })
-
-
Eg2: sample_training.trips 系列中最年轻的自行车骑手是哪一年出生的?
-
错:db.trips.find( {"birth year":{"$ne":null} } ).sort({"birth year":-1}).limit(1) 注意第一个花括号要写的 错:db.trips.find({ "birth year": {"$ne":null}},{"birth year": 1}).sort({ "birth year": -1 }).limit(1) 对: db.trips.find({ "birth year": { "$ne":"" } }, { "birth year": 1 }).sort({ "birth year": -1 }).limit(1) // 过滤空字符串 而不是null
-
index
-
是什么
- 索引是一种特殊的数据结构,以易于遍历的形式存储 集合数据集的一小部分
-
作用:使查询高效
- 有很多种方式可以优化查询速度,但是最有效的方式是 添加索引(Indexes)
-
总结:简单来说,索引是一种 优化查询速度 的 数据结构
-
Eg:
-
db.trips.find({ "birth year": 1989 }) // 创建单个字段索引 single field index = 单一字段 db.trips.createIndex({ "birth year": 1 }) //如果碰到db.trips.find({ "start station id": 476 }).sort( { "birth year": 1 } ) 这种情况 // 为了使第二次查询更高效,需使用复合索引 db.trips.createIndex({ "start station id": 1, "birth year": 1 })
-
数据建模
-
数据建模是什么:一种在文档中组织字段以支持应用程序性能和查询功能的方法。
-
默认情况下,MongoDB并不强制执行默认的数据组织方式。
-
那么如何决定用什么结构来存储数据?应该在哪创建子文件?应该在哪用数值数组?在哪一点上,数据应该得到自己的集合?对数据的形状和结构做出这些决定被称为数据建模。 数据建模是一种 在文件中组织字段以支持应用程序 性能和查询功能 的方法。
-
数据以使用的方式存储。在用MongoDB进行数据建模时,那些被一起访问的数据应该被存储在一起。
-
当应用程序在变化和发展时,你的数据模型也应该在不断发展。而MongoDB是为快速的数据模型变化和进化而建立的。
Upsert
-
Upsert是什么:更新和插入混合体。
-
语法:
db.collection_name.updateOne( {<query>}, {<update>}, {"upsert":true})
。- 默认情况下
upsert
为false
,如果为true
则可以执行更新/插入操作。有则更新,无则插入。
- 默认情况下