邮政编码数据集的聚合¶
本章节的例子使用了名为 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 字段使用经纬度对保存位置信息。
所有的例子都是使用 mongo 的 aggregate() 方法。 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 操作会读取集合中所有的文档,并为每个州创建一个文档保存输出结果。
新生成的关于州的文档包含了两个字段: _id 和 totalPop 。 totalPop 字段保存了使用 $sum 命令对源文档中 pop 字段进行求和的值。
使用 $group 操作后的文档内容类似于:
{ "_id" : "AK", "totalPop" : 550043 }
$match 操作会对输出结果进行过滤,仅保留 totalPop 值大于等于1000万的记录。
等价的 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 操作读取所有的文档,并使用每个源文档的 city 和 state 字段来创建一个新的文档。
这个步骤完成后,得到的文档应该是:
{ "_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 操作读取所有的文档,并使用每个源文档的 city 和 state 字段来创建一个新的文档。
通过设置 _id 字段为一个包含了 state 和 city 字段的子文档,这样就可以保留 state 字段方便后面的操作使用。这个步骤输出的文档还有一个字段 pop ,它的值是使用 $sum 操作符累加源文档中 pop 字段的值而来。
完成这步后,输出的文档是这样的:
{ "_id" : { "state" : "CO", "city" : "EDGEWATER" }, "pop" : 13154 }
$sort 操作会对文档按 pop 字段值从大到小进行排序,排序操作不会修改文档。
第二步的 $group 操作会根据文档中 _id 内嵌文档的 state 字段值,来对前面的输出文档进行分组。
$group 操作输出的每个州的文档中包含了4个字段:使用 $last 表达式计算出的 biggestcity 和 biggestpop 字段,代表了本州中人口最多的城市名和该城市人口总数;使用 $first 表达式计算出的 smallestcity 和 smallestpop 字段,代表了本州中人口最少的城市名和该城市人口总数;
本阶段的输出文档内容如下:
{ "_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
}
}