第一次熟悉MongoDB时,问题出现在分组数据时。
给出两天的数据:
db.test.insert({
"_id" : ObjectId("13edebb315d8952400407343"),
"create_at" : ISODate("2012-12-19T12:00:00.000Z"),
"item" : {
"tags" : [
"aaaa"
],
"event" : "accepted",
}
});
db.test.insert({
"_id" : ObjectId("13edebb39e60c73800b35727"),
"create_at" : ISODate("2012-12-19T12:05:00.000Z"),
"item" : {
"tags" : [
"aaaa"
],
"event" : "delivered"
}
});
db.test.insert({
"_id" : ObjectId("13edebb315d8952400407344"),
"create_at" : ISODate("2012-12-19T13:40:00.000Z"),
"item" : {
"tags" : [
"bbbb"
],
"event" : "accepted",
}
});
db.test.insert({
"_id" : ObjectId("13edebb39e60c73800b35728"),
"create_at" : ISODate("2012-12-19T13:45:00.000Z"),
"item" : {
"tags" : [
"bbbb"
],
"event" : "delivered"
}
});
db.test.insert({
"_id" : ObjectId("13edebb315d8952400407345"),
"create_at" : ISODate("2012-12-20T16:30:00.000Z"),
"item" : {
"tags" : [],
"event" : "accepted",
}
});
db.test.insert({
"_id" : ObjectId("13edebb39e60c73800b35729"),
"create_at" : ISODate("2012-12-20T16:35:00.000Z"),
"item" : {
"tags" : [],
"event" : "delivered"
}
});
输出需要得到结果:
{
"total_count": 6
"items": [
{
"total_count": 2,
"created_at": "Wed, 19 Dec 2012 00:00:00 GMT",
"tags": {
"aaaa": 1,
"bbbb": 1
},
"event": "sent"
},
{
"total_count": 2,
"created_at": "Wed, 19 Dec 2012 00:00:00 GMT",
"tags": {
"aaaa": 1,
"bbbb": 1
},
"event": "delivered"
},
{
"total_count": 1,
"created_at": "Wed, 20 Dec 2012 00:00:00 GMT",
"tags": {},
"event": "sent"
},
{
"total_count": 1,
"created_at": "Wed, 20 Dec 2012 00:00:00 GMT",
"tags": {},
"event": "delivered"
}
}
仍设法构成必要数据的一部分,请求:
db.test.aggregate([
{$group:
{
_id:{event:'$item.event', doy:{$dayOfYear:'$create_at'} },
total_count:{$sum:1},
created_at:{$first: '$create_at'},
tags: {$addToSet: '$item.tags'}
},
},
{$project:{total_count:1, _id:0, event:'$_id.event', created_at:1, tags:1}}
])
但是如何获得有关标签的必要信息以及标签数组及其编号? 以及指示00:00:00开始的日期?
答案 0 :(得分:0)
这是一个简单的聚合查询,可帮助您入门。它可以获得你想要的大部分内容,但形式有点不同。
> db.test.aggregate([
{ "$unwind" : "$item.tags" },
{ "$group" :
{ "_id" : {
"event" : "$item.event",
"day" : { "$dayOfYear" : "$create_at" },
"tag" : "$item.tags"
},
"total_count" : { "$sum" : 1 }
}
}
])
{ "_id" : { "event" : "delivered", "day" : 354, "tag" : "bbbb" }, "total_count" : 1 }
{ "_id" : { "event" : "accepted", "day" : 354, "tag" : "bbbb" }, "total_count" : 1 }
{ "_id" : { "event" : "delivered", "day" : 354, "tag" : "aaaa" }, "total_count" : 1 }
{ "_id" : { "event" : "accepted", "day" : 354, "tag" : "aaaa" }, "total_count" : 1 }
由于您希望按(事件,标记,日期)计算文档数量,因此此聚合查询计算一个文档,其中包含每个唯一三元组所需的计数(事件,标记,日期),这是最简单的方法做到这一点。总计数只是聚合结果的数量。
为了把这一天放回日期,我认为你必须做客户端的事情,因为我不知道任何产生日期的聚合管道运营商。尽管如此,它并不困难,因为您可以通过更改$group
阶段并使用$project
来输出所有相关日期信息。如果您愿意,可以在管道中重建字符串日期。
您会注意到没有标签的文件不会被计算在内。这是$unwind
的必然结果。最简单的方法是使用一个代表" no tag"的虚拟标签。纯粹通过聚合框架解决这个问题会很麻烦。
最后,我想指出的一件事是,如果你可能会忘记:如果你在$dayOfYear
上分组,如果你的数据跨越多年,你可以将不同年份的文档组合在一起。确保这是您的意图,或将更多日期信息添加到组密钥_id
。
答案 1 :(得分:0)
wdberkeley
在这个问题上做了很好的分析。我在下面添加我的:
tags:{tag:count, ...}
的形式,因此如果处于管道操作样式,将使用$ unwind运算符。输出需要具有空标记的文档仍需要保存,但$ unwind运算符将忽略这些文档。"created_at" : "Wed, 20 Dec 2012 00:00:00 GMT"
)。结论:使用mapReduce而不是聚合管道。 以下代码在mongo shell中传递。
function map() {
var date = this.create_at;
var dateStr = date.getFullYear() + "-" + (date.getMonth() + 1) + "-"
+ date.getDate();
var tags = {};
var tagsTemp = this.item.tags;
if (tagsTemp != null) {
for (var x = 0; x < tagsTemp.length; x++) {
var tag = tagsTemp[x];
var count = tags[tag];
count = (count == null) ? 1 : (count + 1);
tags[tag] = count;
}
}
emit({
event : this.item.event,
dateStr : dateStr
}, {
total_count: 1,
tags : tags
});
}
function reduce(key, values) {
var tags = {};
var total_count = 0;
values.forEach(function(value) {
for ( var tag in value.tags) {
var count = tags[tag];
if (count == null)
count = 0;
tags[tag] = count + value.tags[tag];
}
total_count += value.total_count;
});
return {
total_count: total_count,
tags : tags
};
}
function finalHandle(key, reduceValue) {
reduceValue.create_at = new Date(key.dateStr).toUTCString();
reduceValue.event = key.event;
return reduceValue;
}
var mr = db.test.mapReduce(map, reduce, {finalize: finalHandle, out:{inline:1}});
var total = 0;
var items = [];
mr.results.forEach(function(x) {
items.push(x.value);
total += x.value.total_count;
});
printjson({total_count: total, items: items});