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 连接数组的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB 最大连接数 设置失效的异常分析

关于MongoDB最大连接数的查看与修改

为什么MongoDB连接数满了?

为什么MongoDB连接数满了?

为什么 MongoDB 连接数被用满了?

MongoDB 连接数高产生原因及解决