我在MongoDB中有一个日志集合,它的结构如下:
{
url : "http://example.com",
query : "name=blah,;another_param=bleh",
count : 5
}
其中"query"字段是请求的url中的查询参数。我想计算按查询参数"name"分组的计数总数。例如,对于此集合:
[{
url : "http://example.com",
query : "name=blah,;another_param=bleh",
count : 3
},
{
url : "http://example.com",
query : "name=blah,;another_param=xyz",
count : 4
},
{
url : "http://example.com",
query : "name=another_name,;another_param=bleh",
count : 3
}]
我需要这个输出:
[{
key : "blah",
count : 7
},
{
key : "another_name",
count : 3
}]
看起来我无法使用聚合框架来进行这种字符串操作。我可以通过映射减少来实现这一点,但是映射减少操作可以成为聚合管道的一部分吗?
聚合框架没有解析字符串内容并将其分解为此操作所需的键/值对所需的字符串操作运算符。当前唯一可用的字符串操作是$substr
,除非您处理的是固定长度的数据,否则这不会有任何帮助。
因此,目前唯一的服务器端方法是使用mapReduce,因为您只能使用JavaScript函数来进行正确的操作。类似这样的东西:
对于映射器:
function() {
var obj = {};
this.query.split(/,;/).forEach(function(item) {
var temp = item.split(/=/);
obj[temp[0]] = temp[1];
});
if (obj.hasOwnProperty('name')
emit(obj.name,this.count);
}
和减速器:
function(key,values) {
return Array.sum( values );
}
这是JavaScript函数的基本结构,需要将"名称"参数拆分出来,并将其用作聚合的"键",或对"键"出现次数进行一般计数。
因此,聚合框架本身不能执行任何JavaScript,因为它只是在数据上运行本机代码运算符。
不过,最好考虑更改数据的存储方式,以便在将文档插入MongoDB时,将元素分解为"对象"表示,而不是字符串。这允许不依赖JavaScript执行来操作数据的本地查询表单:
[{
"url": "http://example.com",
"query": {
"name": "blah",
"another_param": "bleh"
},
"count": 3
},
{
"url": "http://example.com",
"query": {
"name": "blah",
"another_param": "xyz"
},
"count": 4
},
{
"url": "http://example.com",
"query": {
"name": "another_name",
"another_param": "bleh"
},
"count": 3
}]
这使得$group
管道阶段非常简单,因为数据现在以可以本地处理的形式组织:
{ "$match": { "query.name": { "$exists": true } },
{ "$group": {
"_id": "$query.name",
"count": { "$sum": "$count" }
}}
因此,现在使用mapReduce,但最终要考虑更改数据记录,以从查询字符串中分离"令牌",并将其表示为结构化数据,可选地将原始字符串保留在另一个字段中。
聚合框架将比mapReduce更快地处理这一问题,因此这将是一个更好的持续选项。