【问题标题】:Mongodb Mapreduce join arrayMongodb Mapreduce 连接数组
【发布时间】: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 快得多,这似乎符合您想要的结果。
  • 我想为一个歌曲标题和艺术家姓名添加几周,主要是因为我想查看一首歌曲在几周内的变化。那么每周两个聚合有点苛刻。
  • 只需更改您的标准。如果你只匹配一首歌和艺术家,因为它是关键的一部分,那么结果中只会有那首歌,因为它出现的每一周。因为你不需要它,所以在最后删除限制。匹配部分只是一个标准查询,就像您要查找的那样。你不熟悉吗?还有更多问题,然后评论答案而不是您的问题。

标签: arrays mongodb mapreduce pymongo


【解决方案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 已发布查询中的错字。组语句中缺少右括号
    猜你喜欢
    • 2013-12-15
    • 1970-01-01
    • 1970-01-01
    • 2022-01-21
    • 2015-12-27
    • 2014-08-01
    • 2021-04-07
    • 2016-03-08
    • 1970-01-01
    相关资源
    最近更新 更多