第10回 MongoDB中的聚集aggregation 处理

本文永久链接地址:https://www.askmaclean.com/archives/mongodb-aggregation.html

10  MongoDB中的aggregation 聚集处理

MongoDB中的aggregation 聚集处理的概要

 

一般而言,在NoSQL的程序中,没有RDB的SQL这种Group以及Sum函数等聚集功能。要执行聚集的话,需要在应用上独立地写代码。

但是,MongoDB的开发方针是是一边维持NoSQL的性能,一边实装类似于RDB的功能,关于聚集功能早就实装了。在MongoDB中执行聚集处理的方法有三种。

  1. Aggregation Framework

 

SQL中提供Group By语句以及Sum函数。可以从Mongo Shell中与查询一样实施。一部分的处理($group与$sort)就对应sharding,用各shard进行处理。

 

  1. MongoDB的Map/Reduce功能

 

独立定义Map函数/Reduce函数,执行聚集处理。在Aggregation Framework中无法做到的复杂的聚集处理,使用这种方法。因为对应sharding,所以可以执行分散处理。

 

  1. 与其他聚集处理软件/框架的合作

为了执行大规模的聚集处理,也可以与其他聚集处理软件/框架合作。在这次的文章中,我将介绍与Hadoop的合作。

 

那么马上让我们来使用Aggregation Framework,来试着实际操作处理吧。

Aggregation Framework

首先来准备要聚集的数据。这次使用假设的网页服务器访问日志数据。将下述样本数据保存在MongoDB中。

 

 db.httplogs.insert({"url_path":"/", "status":"200"});
 db.httplogs.insert({"url_path":"/", "status":"200"});
 db.httplogs.insert({"url_path":"/", "status":"500"});
 db.httplogs.insert({"url_path":"/", "status":"500"});
 db.httplogs.insert({"url_path":"/top", "status":"200"});
 db.httplogs.insert({"url_path":"/top", "status":"200"});
 db.httplogs.insert({"url_path":"/top", "status":"404"});
 db.httplogs.insert({"url_path":"/user", "status":"200"});
 db.httplogs.insert({"url_path":"/user", "status":"500"});
 db.httplogs.insert({"url_path":"/user", "status":"500"});

 

 

用Aggregation Framework来聚集

 

那么让我们来试着执行Aggregation Framework。通过使用之前保存的访问日志的数据,首先让我们来聚集每个URL的访问数吧。在此我们应该无视HTTP步骤代码。利用HTTP步骤代码的复数key聚集,我将在之后介绍。

 

将每个URL的访问数的聚集用Aggregation Framework实施的话,请用以下代码。

 

db.httplogs.aggregate (  { $group   : { "_id"  : "$url_path", "count" : { "$sum" : 1 } } });

 

请试着执行。

 

db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } });db.httplogs.aggregate ( { $group : { "_id" : "$url_path", "count" : { "$sum" : 1 } } });
{
 "result" : [
 {
 "_id" : "/user",
 "count" : 3
 },
 {
 "_id" : "/top",
 "count" : 3
 },
 {
 "_id" : "/",
 "count" : 4
 }
 ],
 "ok" : 1
}

 

实现了聚集。

之后让我们将URL以及HTTP步骤代码作为key来进行聚集吧。要用多个key进行聚集的话,$group operator的”_id”的值如下所示进行修正。

db.httplogs.aggregate(  { $group   : { "_id"  : { "url_path" : "$url_path",  "status" 
:"$status" }, "count" : { "$sum" : 1 } } });

 

试着执行。

 

> db.httplogs.aggregate(  { $group   : { "_id"  : { "url_path" : "$url_path",  "status" :"$status" }, "count" : { "$sum" : 1 } } });db.httplogs.aggregate(  { $group   : { "_id"  : { "url_path" : "$url_path",  "status" :"$status" }, "count" : { "$sum" : 1 } } });
{
        "result" : [
                {
                        "_id" : {
                                "url_path" : "/user",
                                "status" : "500"
                        },
                        "count" : 2
                },
                {
                        "_id" : {
                                "url_path" : "/user",
                                "status" : "200"
                        },
                        "count" : 1
                },
                {
                        "_id" : {
                                "url_path" : "/top",
                                "status" : "200"
                        },
                        "count" : 2
                },
                {
                        "_id" : {
                                "url_path" : "/",
                                "status" : "500"
                        },
                        "count" : 2
                },
                {
                        "_id" : {
                                "url_path" : "/top",
                                "status" : "404"
                        },
                        "count" : 1
                },
                {
                        "_id" : {
                                "url_path" : "/",
                                "status" : "200"
                        },
                        "count" : 2
                }
        ],
        "ok" : 1
}

 

在此也完成了聚集。

 

可以用Aggregation Framework做到的事

 

这样,如果是这样单纯的聚集,可以用Aggregation Framework来实施。

mongodb中也有Aggregation Framework中对于聚集非常方便的operator,可以组合起来使用。

例如如下所示,我们考虑使用求测试的平均分的SQL。

List1  求测试平均分的SQL

 

SELECT name as '_id', AVG(score) as 'average' FROM scores WHERE year = 'junior' GROUP BY = name

 

 

用Aggregation Framework表现上述SQL的话就如下所示。

 

List2 求测试平均分的Aggregation Framework

db.scores.aggregate(  { $match   : { "year" : "junior" } },  { $project : { "name" : 1, "score" : 1 } },  { $group   : { "_id"  : "$name",                 "average" : { "$avg" : "$score" } } });

 

 

这样将operator作为Filter一样使用,通过执行Pipeline处理,使其实现更加高度的聚集处理。(参照图1)。

图1  Aggregation Framework的Pipeline处理

 

 

关于其他的operator,在官方手册中,请参考SQL以及Aggregation Framework的Mapping表,我们将介绍一部分。

 

SQL Aggregation operator
WHERE $match
GROUP BY $group
HAVING $match
SELECT $project
ORDER BY $sort
LIMIT $limit
SUM() $sum
COUNT() $sum

 

官方手册中也写了其他对应各SQL的样本代码。

 

在上述operator中可能的范围可以用Aggregation Framework进行聚集。要执行以上复杂处理的情况下,可以使用Map/Reduce功能来定义map函数以及Reduce函数来实施。

 

那么让我们来试着用Map/Reduce进行聚集吧。

MongoDB的Map/Reduce

 

在此将刚刚用Aggregation Framework执行的聚集,这次再此使用Map/Reduce进行聚集。但这需要Map/Reduce中用JavaScript记述的map函数以及Reduce函数,我们来准备吧。

 

准备map 函数

 

 

Map函数大致来说,就是为了在Reduce函数中使用的,制成key以及value的处理。Map函数中还无法执行聚集处理。

在key之中,指定Grouping的key。与之前一样,首先聚集每个URL的访问数,所以无视HTTP步骤代码。HTTP步骤代码的利用的多个key的聚集我将在之后介绍。

为了在map函数内部制成key以及value,使用emit函数。在第一引数中记述key,在第二引数中记述聚集方法。在此,指定第一引数为this.url_path,第二引数为{count : 1}。

> map = function() {  emit(this.url_path, {count: 1});}

 

 Reduce函数的准备

在Reduce函数中,在用map函数内的emit函数指定的key 被grouping的状态下,这次为了数每个URL访问数,记述添加了count的处理。

reduce = function(key, values) {  var count = 0;  values.forEach(function(v) {    count += v['count'];  });  return {count: count};}

 

用map/Reduce进行聚集

 

作成了map函数与reduce函数的话,那么就来试着进行聚集吧。在聚集之中,聚集的collection的mapReduce函数的引数中第一引数中map函数,在第2引数中传送reduce函数。第3引数传送选项。通过指定{out: {inline:1}},结果就能在控制台中数据。那么让我们来试着实行mapReduce函数吧。

db.httplogs.mapReduce( map, reduce, {out: {inline:1}} );

{
        "results" : [
                {
                        "_id" : "/",
                        "value" : {
                                "count" : 4
                        }
                },
                {
                        "_id" : "/top",
                        "value" : {
                                "count" : 3
                        }
                },
                {
                        "_id" : "/user",
                        "value" : {
                                "count" : 3
                        }
                }
        ],
        "timeMillis" : 651,
        "counts" : {
                "input" : 10,
                "emit" : 10,
                "reduce" : 3,
                "output" : 3
        },
        "ok" : 1,
}

 

可以确认URL访问数的合计是用count来进行聚集的。

接着就来试着将URL以及HTTP步骤代码来作为key来进行聚集吧。

※)

在选项中,可以保存结果collection的指定以及最后实施的finalize函数。详细请参考官方手册。

用多个key进行聚集

 

为了用复数key进行聚集,用hash指定emit函数的第一引数。修正map函数。

为了将URL以及HTTP步骤代码作为key,将第一引数如下所示进行变更。

 

> map = function() {  emit({url_path: this.url_path, status: this.status}, {count: 1});}

 

Reduce函数如刚才那样就好了。让我们来试着执行mapReduce函数吧。

db.httplogs.mapReduce( map, reduce, {out: {inline:1}} );

db.httplogs.mapReduce( map, reduce, {out: {inline:1}} );
{
        "results" : [
                {
                        "_id" : "/",
                        "value" : {
                                "count" : 4
                        }
                },
                {
                        "_id" : "/top",
                        "value" : {
                                "count" : 3
                        }
                },
                {
                        "_id" : "/user",
                        "value" : {
                                "count" : 3
                        }
                }
        ],
        "timeMillis" : 33,
        "counts" : {
                "input" : 10,
                "emit" : 10,
                "reduce" : 3,
                "output" : 3
        },
        "ok" : 1,
}

 

我们在此可以确认URL以及HTTP步骤代码都变成了聚集的key。

用map/reduce可以做到的事情

在Map/reduce之中,有个很大的特征是可以使用JavaScript自由地对map函数以及reduce函数进行定义。无法用Aggregation Framework做到,可以实装if等防御语句以及其他的JavaScript的函数的处理。

那么最后我将介绍与hadoop的合作。

与其他聚集处理Middle(Hadoop)的合作

 

试着与Hadoop组合来使用

 

通过将MongoDB与Hadoop组合使用,可以实现一边在数据store中使用MongoDB,一边通过Hadoop的强力分散功能所带来的高效并列处理,(参照图2)。活用作为Replica set、sharding、mongo shell等可变通的询问这样的数据store的mongoDB的优点,也可以使用Hadoop的分散处理功能,从而实现互取长处。

 

图2 MongoDB与Hadoop的组合

 

 

 

这次的文章仅仅介绍了MongoDB Hadoop Adapter的介绍,实际的使用方法请参考官方手册。

总结以及下次的主题

 

这次介绍了用MongoDB实施聚集的方法。在MongoDB中,有类似于询问来实施Aggregation Framework的功能以及可以自身记录处理内容的map/reduce功能。可以根据用途不同来分开使用。

对于近年需求逐年增加的大数据的聚集处理中,有通过与hadoop来组合进行处理的方法。MongoDB开发元的10gen提供Hadoop用的连接

下次 我将介绍使用MongoDB的使用。请大家多多期待!

 

Comment

*

沪ICP备14014813号

沪公网安备 31010802001379号