Mongodb Mapreduce 连接数组
Posted
技术标签:
【中文标题】Mongodb Mapreduce 连接数组【英文标题】:Mongodb Mapreduce join array 【发布时间】:2014-04-08 03:36:36 【问题描述】:我收藏了大量歌曲,并希望以数组形式收集每周播放次数最多的歌曲。例如:
"_id" :
"title" : "demons savaites hitas",
"name" : "imagine dragons"
,
"value" :
"weeks" : [
"played" : 56,
"week" : 9,
"year" : 2014
]
有时会变成:
"_id" :
"title" : "",
"name" : "top 15"
,
"value" :
"played" : 1,
"week" : 8,
"year" : 2014
我从中获取数据的集合被命名为歌曲,并且在添加歌曲时会一直添加新字段。没有唯一的艺术家姓名或歌名,集合中的每个文档都如下所示:
"_id" : ObjectId("530536e3d4ca1a783342f1c8"),
"week" : 8,
"artistname" : "City Shakerz",
"songtitle" : "Love Somebody (Summer 2012 Mix Edit)",
"year" : 2014,
"date" : ISODate("2014-02-19T22:57:39.926Z")
我现在想做一个 mapreduce,将新的一周添加到数组中。它现在覆盖它。 我还注意到,当尝试使用新的 mapreduce 更改为数组时,并不是所有播放的都被计算在内。
新的 mapreduce 几周后无法正常工作:
map = function ()
if (this.week == 9 && this.year == 2014) emit(title:this.songtitle.toLowerCase(), name:this.artistname.toLowerCase(), played:1, week:this.week, year:this.year);
reduce = function(k, values)
var result = ;
result.weeks = new Array();
var object = played:0, week: 0, year: 0;
values.forEach(function(value)
object.played += value.played;
object.week = value.week;
object.year = value.year;
);
result.weeks.push(object);
return result;
db.songs.mapReduce(map,reduce,out: reduce:"played2")
这是我使用的旧的,是每周和歌曲集合中的一个新字段:
map = function ()
if (this.week == 10 && this.year == 2014) emit(title:this.songtitle.toLowerCase(), name:this.artistname.toLowerCase(), week:this.week, year:this.year, count:1);
reduce = function(k, values)
var result = count: 0,;
values.forEach(function(value)
result.count += value.count;
);
return result;
db.songs.mapReduce(map,reduce,out: merge:"played")
我现在从 play2 那里得到来自 toplist 的信息,如下所示:
db.played2.find('_id.week': 9,'_id.year': 2014).sort(array("value.count" => -1)).limit(50)
上面的行可以包含任何错字,因为我使用 mongoclient for php 并且需要为您将其更改为 javascript 语法。
我做错了什么?
【问题讨论】:
您能否介绍一下您的原始集合的结构。我的观点是我不认为你需要 mapReduce 并且可能有更好的方法。 @NeilLunn - 我已经编辑了文档中的问题以及该集合的工作原理。它只是一个很长的 feed-collection,包含最后播放的歌曲,新歌一直在添加,大约每秒 10 首。 试试答案中的聚合语句。聚合管道的运行速度比 map reduce 快得多,这似乎符合您想要的结果。 我想为一个歌曲标题和艺术家姓名添加几周,主要是因为我想查看一首歌曲在几周内的变化。那么每周两个聚合有点苛刻。 只需更改您的标准。如果你只匹配一首歌和艺术家,因为它是关键的一部分,那么结果中只会有那首歌,因为它出现的每一周。因为你不需要它,所以在最后删除限制。匹配部分只是一个标准查询,就像您要查找的那样。你不熟悉吗?还有更多问题,然后评论答案而不是您的问题。 【参考方案1】:我发现我可以像上面的代码 sn-p 那样执行 mapreduce,然后只在查询中获取本周,在前一周获取另一个,然后使用 if 执行简单的 double 以在本周与前一周的位置进行更新。
我在 python 中编写了脚本,我也将它作为我的 mapreduce 作为 cronjob 运行。例如:
if len(sys.argv) > 1 and sys.argv[1] is not None:
week = int(sys.argv[1])
else:
week = (datetime.date.today().isocalendar()[1]) - 1
year = datetime.date.today().year
previous_week = week - 1
client = MongoClient()
db = client.db
played = db.played
print "Updating it for week: " + str(week)
previous = played.find("_id.week": previous_week, "_id.year": year).sort("value.count", -1).limit(50)
thisweek = played.find("_id.week": week, "_id.year": year).sort("value.count", -1).limit(50)
thisplace = 1
for f in thisweek:
previous.rewind() # Reset second_collection_records's iterator
place = 1
if previous.count() > 0:
checker = bool(1)
for s in previous:
if s["_id"]["name"] == f["_id"]["name"] and s["_id"]["title"] == f["_id"]["title"]:
result = played.update("_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"], "$set": "place.previous_week":place, "place.this_week":thisplace)
checker = bool(0)
print result
place = place + 1
if checker is True:
result = played.update("_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"], "$set": "place.previous_week":0, "place.this_week":thisplace)
print result
else:
result = played.update("_id.week": f["_id"]["week"], "_id.year": f["_id"]["year"], "_id.title": f["_id"]["title"], "_id.name": f["_id"]["name"], "$set": "place.previous_week":0, "place.this_week":thisplace)
print result
thisplace = thisplace + 1
print "done."
这似乎工作得很好。希望 mongodb 增加对仅更新字段或 mapreduce 中的任何内容的支持,以便将信息添加到文档而不覆盖它。
【讨论】:
【参考方案2】:我正在根据您的输入字段对您的集合结构进行测试,但我认为 mapReduce 不是您想要的工具。使用aggregate 可以实现您明显期望的输出:
db.collection.aggregate([
// Match a specific week and year if you want - remove if you want all
"$match": "year": inputYear, "week": inputWeek ,
// Group to get the total number of times played
"$group":
"_id":
"title": "$toLower": "$songtitle" ,
"name": "$toLower": "$artistname" ,
"week": "$week",
"year": "$year"
,
played: "$sum": 1
,
// Sort the results by the most played in the range
"$sort": "year": -1, "week": -1, "played": -1 ,
// Optionally limit to the top 15 results
"$limit": 15
])
这基本上就是您似乎正在尝试做的事情。所以这将“出场次数”总结为播放次数。然后我们采取额外的步骤对结果进行排序,并且可选地(如果您可以忍受一次查找一周)将结果限制为一个设定的数字。最后两个步骤是 mapReduce 无法完成的。
如果您最终要寻找每周的“前十名”,作为单个查询结果,那么您可以look at this 进行讨论(以及实现方法),我们称之为“topN”结果问题。
【讨论】:
我在这个查询中得到Error: Line 13: Unexpected token
@HåkanNylén 已发布查询中的错字。组语句中缺少右括号以上是关于Mongodb Mapreduce 连接数组的主要内容,如果未能解决你的问题,请参考以下文章