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查找的原因:

    1. 因为有时候可能用聚合:比如分组操作,以某种方式修改数据。

    2. 并不是总是只需要过滤出正确的文档。

    3. 可以执行除了 查找、投影数据 以外的操作,比如使用聚合进行计算。

    4. 它通过计算、重塑、重组数据的能力 超越了 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})

    • 默认情况下 upsertfalse ,如果为 true 则可以执行更新/插入操作。有则更新,无则插入
上一篇:ThinkPHP Migrate


下一篇:MySQL --- limit以及通用分页SQL