我是mongodb新手!我正在尝试处理一些高音扬声器数据。我的目标是在每个时间间隔(为简单起见,每日间隔)对用户进行分组,并计算当天的唯一主题标签。我的想法是构建新的DB,它只包含用户,日期和主题标签。这是数据格式:
> db.sampledDB.findOne()
{
"_id" : NumberLong("2334234"),
"replyid" : NumberLong(-1),
"userid" : NumberLong(21313),
"replyuserid" : NumberLong(-1),
"createdAt" : ISODate("2013-07-02T22:35:06Z"),
"tweettext" : "RT @BBCBreaking: Plane carrying Bolivia President Morales is diverted to Austria on suspicion US fugitive #Snowden is on board - Bolivian m…",
"screenName" : "x83",
"name" : "david x",
"retweetCount" : NumberLong(0),
"retweet_id" : NumberLong("12313223"),
"retweet_userid" : NumberLong(123123123),
"source" : "<a href=\"http://www.twitter.com\" rel=\"nofollow\">Twitter for Windows Phone</a>",
"hashtags" : [
{
"start" : 106,
"end" : 114,
"text" : "Snowden"
}
],
"mentions" : [
{
"start" : 3,
"end" : 15,
"id" : NumberLong(876678),
"screenName" : "BBCBreaking",
"name" : "BBC Breaking News"
}
],
"media" : [ ]
}
我像这样使用mapReduce: MAP:
map = function(){
//format date to year/month/day
var format = this.createdAt.getFullYear() + '/' + (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
var key = {userid:this.userid, date:format};
emit(key,{hashtags:this.hashtags}); }
REDUCE:
reduce = function(key,values){
var result = {a:[]};
for (var idx=0;idx<values.length;idx++){
result.a.push(values[idx].hashtag);
}
return result};
结果是:
{
"_id" : {
"userid" : NumberLong(7686787),
"date" : "2013/7/5"
},
"value" : {
"hashtag" : [
{
"start" : 24,
"end" : 44,
"text" : "SıkSöylenenYalanlar"
},
{
"start" : 45,
"end" : 60,
"text" : "ZimmermanTrial"
},
{
"start" : 61,
"end" : 84,
"text" : "ZaynMalikYouArePerfect"
},
{
"start" : 85,
"end" : 99,
"text" : "TrayvonMartin"
},
{
"start" : 100,
"end" : 110,
"text" : "Wimbledon"
},
{
"start" : 111,
"end" : 118,
"text" : "Футбол"
},
{
"start" : 119,
"end" : 127,
"text" : "Snowden"
},
{
"start" : 128,
"end" : 138,
"text" : "TFFistifa"
}
]
}
},
{
"_id" : {
"userid" : NumberLong(45666),
"date" : "2013/7/5"
},
"value" : {
"hashtag" : [
{
"start" : 24,
"end" : 44,
"text" : "SıkSöylenenYalanlar"
},
{
"start" : 45,
"end" : 60,
"text" : "ZimmermanTrial"
},
{
"start" : 61,
"end" : 84,
"text" : "ZaynMalikYouArePerfect"
},
{
"start" : 85,
"end" : 99,
"text" : "TrayvonMartin"
},
{
"start" : 100,
"end" : 110,
"text" : "Wimbledon"
},
{
"start" : 111,
"end" : 118,
"text" : "Футбол"
},
{
"start" : 119,
"end" : 127,
"text" : "Snowden"
},
{
"start" : 128,
"end" : 138,
"text" : "TFFistifa"
}
]
}
},
但我只想保留主题标签的 text 元素。我试图将reducer更改为 values [idx] .hashtag.text 或 values [idx] .hashtag [“text”] ,但没有帮助。
更新 我怀疑我的问题与MapReduce problem类似,但我不知道修复我的问题
答案 0 :(得分:1)
您也可以考虑使用聚合框架,它可以产生如下所示的结果。管道看起来与此类似:
{$project: {
userid: "$userid",
"hashtags": "$hashtags.text",
date: {
year: { $year: "$createdAt" },
month: { $month: "$createdAt"},
day: {$dayOfMonth: "$createdAt"} }}},
{$unwind: "$hashtags" },
{ $group: { _id : {
date: "$date",
userid: "$userid"},
hashtags: { $addToSet:"$hashtags" }
}} )
可能会产生如下结果:
[
{
"_id" : {
"date" : {
"year" : 2013,
"month" : 8,
"day" : 4
},
"userid" : NumberLong(362337301)
},
"hashtags" : [
"tagger",
"stackoverflow",
"twitter"
]
}, /* more */
汇总框架管道的简要说明:
$project
,只抓取通过管道其余部分重要的字段。在此之前,如果有一个特定的日期或范围,使用$match
将是一个很好的步骤,可以有效地过滤一些结果)。请注意,createdAt
字段已拆分为相应的部分,以便稍后在分组时忽略时间。投影发生后,示例中的新字段将被称为date
。这里,哈希标记已简化为text属性,名称重用为"hashtags"
。"hashtags"
此时是一个数组(例如:['tagger', 'stackoverflow', 'twitter']
),管道会为"hashtag"
数组中的每个元素创建一个新文档。userid
和date
的组合作为分组,并将所有唯一哈希标记添加到名为"hashtags"
的字段中。作为拆分日期的替代方法,您还可以将createdAt
字段视为字符串,并在管道中使用此字段来删除时间:
date: {$substr: ["$createdAt",0, 10] }
它会产生类似的东西:
2013-07-02
正如您所指出的,从聚合输出的文档中目前有16MB的限制。虽然计划在2.6版本的MongoDB中更改,但您也可以获得MapReduce。鉴于MapReduce不一定适用于此类工作,因此结果可能不一定是您想要的结果。
map = function() {
var format = this.createdAt.getFullYear() + '/'
+ (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
var key = {userid:this.userid, date:format};
var hashtags = this.hashtags || [];
for(var i=0, l=hashtags.length; i < l; i++) {
emit(key, hashtags[i].text);
}
};
reduce = function(key, values){
values = values || [];
var tag;
var tags = {};
for(var i=0, l=values.length; i<l ; i++) {
tag = values[i] || "";
if (tag.length > 0) {
tags[tag] = "";
}
};
values = [];
for(var t in tags) {
values.push(t);
}
return values.join(',');
};
它不会发出数组,而是发出map
中的每个哈希标记。 reduce使用简单的关联数组消除重复,然后返回带有所有哈希标记的连接字符串。 MongoDB不支持通过reduce
函数返回结果数组(想法是reduce应该提供一个结果,而不是结果数组)。
结果:
{
"_id" : {
"userid" : NumberLong(262317302),
"date" : "2013/7/2"
},
"value" : "Wisconsin,Space,Cheese"
}
如果您不需要经常这样做,您也可以在MongoDB控制台中编写一个shell脚本,将哈希标记提取到新的集合中。然后,只需在需要时运行它。
答案 1 :(得分:0)
这是我如何设法产生与上述答案相同的结果。只是为了呈现另一个解决方
map = function(){
var day = this.createdAt.getFullYear() + '/' + (this.createdAt.getMonth()+1) + '/' + this.createdAt.getDate();
var key = {userid:this.userid, date:day};
var values = {hashtags:[]};
for (var idx=0;idx<this.hashtags.length;idx++){
values.hashtags.push(this.hashtags[idx].text);
}
emit(key,values);
};
reduce = function(key,values){
hashtag_list = {hashtags: []} ;
for(var i in values) {
hashtag_list.hashtags= values[i].hashtags.concat(hashtag_list.hashtags);
}
return hashtag_list;
}
答案 2 :(得分:-1)
尝试:
值[IDX]的.text
hashtag不是对象的属性,但是text是。