OPTIONS
翻译或纠错本页面

邮政编码数据集的聚合

本章节的例子使用了名为 zipcode 的集合。这个集合位于 media.mongodb.org/zips.json. 可以使用 mongoimport 将这个集合的数据导入到你的 mongod 实例中。

数据模型

zipcode 集合中的文档格式如下:

{
  "_id": "10280",
  "city": "NEW YORK",
  "state": "NY",
  "pop": 5574,
  "loc": [
    -74.016323,
    40.710537
  ]
}

_id 字段保存邮政编码,使用字符串格式。

city 字段保存城市名称。一个城市对应多个邮编:不同的区使用不同的邮政编码。

state 字段保存州名称的简称(使用了两个字母)。

pop 字段保存了人口总数。

loc 字段使用经纬度对保存位置信息。

所有的例子都是使用 mongoaggregate() 方法。 aggregate() 封装了 aggregate 命令。具体的接口说明可以查看对应你使用的 driver 的文档。

查找人口超过1000万的州

查找所有的人口超过1000万的州,使用下面的聚合方法:

db.zipcodes.aggregate( { $group :
                         { _id : "$state",
                           totalPop : { $sum : "$pop" } } },
                       { $match : {totalPop : { $gte : 10*1000*1000 } } } )

aggregate() 方法会读取集合 zipcodes 中所有的文档。 aggregate() 方法的聚合过程由多个 pipeline 操作符联接而成。

在这个例子中,集合 zipcodes 的所有文档都会经过下面的这些步骤:

  • $group 操作会读取集合中所有的文档,并为每个州创建一个文档保存输出结果。

    新生成的关于州的文档包含了两个字段: _idtotalPoptotalPop 字段保存了使用 $sum 命令对源文档中 pop 字段进行求和的值。

    使用 $group 操作后的文档内容类似于:

    {
      "_id" : "AK",
      "totalPop" : 550043
    }
    
  • $match 操作会对输出结果进行过滤,仅保留 totalPop 值大于等于1000万的记录。

    $match 操作不会对文档做任何修改,它的输出仍然是 $group 输出的文档内容。

等价的 SQL 语句是:

SELECT state, SUM(pop) AS totalPop
       FROM zipcodes
       GROUP BY state
       HAVING totalPop >= (10*1000*1000)

计算所有州的城市人口平均值

要计算所有州的城市人口平均值,可以使用下面的聚合操作:

db.zipcodes.aggregate( [
   { $group : { _id : { state : "$state", city : "$city" }, pop : { $sum : "$pop" } } },
   { $group : { _id : "$_id.state", avgCityPop : { $avg : "$pop" } } }
] )

aggregate() 方法会读取集合 zipcodes 中的所有文档。 aggregate() 方法的聚合过程由多个 pipeline 操作符联接而成。

在这个例子中,集合 zipcodes 的所有文档都会经过下面的这些步骤:

  • $group 操作读取所有的文档,并使用每个源文档的 citystate 字段来创建一个新的文档。

    这个步骤完成后,得到的文档应该是:

    {
      "_id" : {
        "state" : "CO",
        "city" : "EDGEWATER"
      },
      "pop" : 13154
    }
    
  • 第二步的 $group 操作会读取所有文档中的 state 字段的值,并使用 $avg 来计算出 avgCityPop 字段的值。

该聚合操作的最终输出应该是:

{
  "_id" : "MN",
  "avgCityPop" : 5335
},

查找人口最多和最少的州

要查找人口最多和最少的州,使用下面的聚合操作:

db.zipcodes.aggregate( { $group:
                         { _id: { state: "$state", city: "$city" },
                           pop: { $sum: "$pop" } } },
                       { $sort: { pop: 1 } },
                       { $group:
                         { _id : "$_id.state",
                           biggestCity:  { $last: "$_id.city" },
                           biggestPop:   { $last: "$pop" },
                           smallestCity: { $first: "$_id.city" },
                           smallestPop:  { $first: "$pop" } } },

                       // the following $project is optional, and
                       // modifies the output format.

                       { $project:
                         { _id: 0,
                           state: "$_id",
                           biggestCity:  { name: "$biggestCity",  pop: "$biggestPop" },
                           smallestCity: { name: "$smallestCity", pop: "$smallestPop" } } } )

aggregate() 方法会读取集合 zipcodes 中的所有文档。 aggregate() 方法的聚合过程由多个 pipeline 操作符联接而成。

在这个例子中,集合 zipcodes 的所有文档都会经过下面的这些步骤:

  • $group 操作读取所有的文档,并使用每个源文档的 citystate 字段来创建一个新的文档。

    通过设置 _id 字段为一个包含了 statecity 字段的子文档,这样就可以保留 state 字段方便后面的操作使用。这个步骤输出的文档还有一个字段 pop ,它的值是使用 $sum 操作符累加源文档中 pop 字段的值而来。

    完成这步后,输出的文档是这样的:

    {
      "_id" : {
        "state" : "CO",
        "city" : "EDGEWATER"
      },
      "pop" : 13154
    }
    
  • $sort 操作会对文档按 pop 字段值从大到小进行排序,排序操作不会修改文档。

  • 第二步的 $group 操作会根据文档中 _id 内嵌文档的 state 字段值,来对前面的输出文档进行分组。

    $group 操作输出的每个州的文档中包含了4个字段:使用 $last 表达式计算出的 biggestcitybiggestpop 字段,代表了本州中人口最多的城市名和该城市人口总数;使用 $first 表达式计算出的 smallestcitysmallestpop 字段,代表了本州中人口最少的城市名和该城市人口总数;

    本阶段的输出文档内容如下:

    {
      "_id" : "WA",
      "biggestCity" : "SEATTLE",
      "biggestPop" : 520096,
      "smallestCity" : "BENGE",
      "smallestPop" : 2
    }
    
  • 最后的步骤是 $project,它会把 _id 字段名重新命名为 state ,并把 biggestCity , biggestPop 移动到子文档 biggestCity 中,把 smallestCity , smallestPop 移动到子文档 smallestCity 中。

本次聚合操作的输出是:

{
  "state" : "RI",
  "biggestCity" : {
    "name" : "CRANSTON",
    "pop" : 176404
  },
  "smallestCity" : {
    "name" : "CLAYVILLE",
    "pop" : 45
  }
}