聚合框架:只是在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()
是游标方法,其他的游标方法有:pretty()
, count()
。
游标方法不适用于:存储在数据库中的数据,它应用于位于游标中的结果集。
这两个一般结合使用,先执行 sort
再执行 limit
,如果``cursor.limit().sort()了,内部执行的还是
cursor.sort().limit()` 。
sort排序语法:db.COLLECTION_NAME.find().sort({KEY:1,key2:-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
是什么
作用:使查询高效
总结:简单来说,索引是一种 优化查询速度 的 数据结构
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是什么:更新和插入混合体。
语法:db.collection_name.updateOne( {<query>}, {<update>}, {"upsert":true})
。
upsert
为 false
,如果为 true
则可以执行更新/插入操作。有则更新,无则插入。