mongodb中用户排行榜得分的最佳设计

时间:2014-04-06 16:05:25

标签: javascript mongodb mongoose

我在为我的游戏设计数据库时遇到问题,游戏的用户有3个得分参数加权不同,得分是特定行动的结果(做了P,或做了Q,或做了L)应用程序。所以用户会看起来像这样:

{
  "_id": { "$oid" : "531f14324fe3ba6360335230" },
  "p": 88,
  "q": 0,
  "l": 10,
  "totalScore":8.9
}

其中p,q,l是具有不同权重的不同分数。

我想根据用户的每周/每月活动查询信息,这意味着获得p,q,l得分最高分的用户将成为本周的最高用户。

我完全没有理解如何建立适当的架构以支持数周/月的日期查询,并支持以有效的方式加权计算得分,假设我还想与周围用户建立排行榜并拥有一个位置当前用户

-----------------q--------p--------l------
4. user121      25       5         0
5. currentUser  5        7        28
6. user77       3        2        43
-----

非常感谢帮助。

UPD: 所以这是设计的草稿

我有动作集合,其中包含每个使用过的行为的文档,如下所示:

{
    id: ObjectId,
    type:'q',
    time: DATE,
    userId: USER_ObjectId,
    entityId: ENTITY_ObjectId
}

其他类型也是如此。

因此,当游戏中的操作发生时 - 我创建此类文档并在用户的分数文档中增加适当的类型字段。 在此之后,我根据我的公式计算totalScore字段:

totalscore = q + p/10 + l/100

现在我可以按总分进行查询和排序,但它没有关于此分数是否为过去七天的任何数据,仍然不知道如何实现这一目标。

1 个答案:

答案 0 :(得分:1)

根据上次更新,这似乎是在正确的轨道上,但我可能会为设计提供一些“调整”,并解决问题的日期范围部分。

虽然你可以使用mongoose与populate做一些好事,但仍然值得记住,这样做的“加入”只是模拟,因为实际发生的是对数据库进行额外的查询为了检索“相关”项目。

因此,在使用MongoDB时,最好将要使用的信息保存在同一个集合中。因此,对于这里的简单示例,我将在这些“事件”类型的条目中保留“用户名”。我这样做是因为这是在总结时使用的信息。因此,“事件”(我称之为“事件”)的文档看起来像这样:

{ 
    "userId": 123, 
    "username": "user1", 
    "type": "q", 
    "time": ISODate("2014-04-07T03:56:33.488Z")
},
{ 
    "userId": 123, 
    "username": "user1", 
    "type": "p", 
    "time": ISODate("2014-04-07T03:56:33.488Z")
},
{ 
    "userId": 456, 
    "username": "user2", 
    "type": "p", 
    "time": ISODate("2014-04-07T03:56:33.488Z")
}

当然还有其他领域,但我们将关注这些领域。

要在日期范围内将这些组合在一起,您可以执行以下操作:

db.events.aggregate([
    // Match the date range required
    { "$match": {
        "time": { 
            "$gte": new Date("2014-04-07"), 
            "$lt": new Date("2014-04-14") 
        }
    }},

    // Group and Reshape the matching documents
    { "$group": {
        "_id": "$userId",
        "username": { "$first": "$username" },
        "q" : { "$sum": { "$eq": [ "$type", "q" ] } },
        "p" : { "$sum": { "$eq": [ "$type", "p" ] } },
        "l" : { "$sum": { "$eq": [ "$type", "l" ] } }
    }},

    // Project to get a "score" value
    { "$project": {
        "username": 1,
        "p": 1,
        "q": 1,
        "l": 1,
        "score": { "$add": [
            "$q",
            { "$cond": [
                "$p",
                { "$divide": [ "$p", 10 ] },
                0
            ]},
            { "$cond": [
                "$l",
                { "$divide": [ "$l", 10 ] },
                0
            ]},
        ]} 
    }},

    // Sort the results by the "score" descending
    { "$sort": { "score": -1 } },

    // Optionally limit the results
    { "$limit": 10 }

])

因此,所有这些都根据时间段内基于“事件”的条目对每个用户的结果进行分组,计算总分(小心不要除以0),然后对结果进行排序,以便最高得分位于顶部

您甚至可以执行类似的查询,以便从每周或其他时间间隔中找到排名最高的用户:

db.events.aggregate([
    // Match the date range required - one year as a sample
    { "$match": {
        "time": { 
            "$gte": new Date("2014-01-01"), 
            "$lt": new Date("2015-01-01") 
        }
    }},


    // Group and Reshape the matching documents
    { "$group": {
        "_id": { 
            "week": { "$week": "$time" },
            "userId": "$userId"
        },
        "username": { "$first": "$username" },
        "q" : { "$sum": { "$eq": [ "$type", "q" ] } },
        "p" : { "$sum": { "$eq": [ "$type", "p" ] } },
        "l" : { "$sum": { "$eq": [ "$type", "l" ] } }
    }},

    // Project to get a "score" value
    { "$project": {
        "username": 1,
        "p": 1,
        "q": 1,
        "l": 1,
        "score": { "$add": [
            "$q",
            { "$cond": [
                "$p",
                { "$divide": [ "$p", 10 ] },
                0
            ]},
            { "$cond": [
                "$l",
                { "$divide": [ "$l", 10 ] },
                0
            ]},
        ]} 
    }},

    // Sort by highest score for each week
    { "$sort": { "_id.week": 1, "score": -1 } },

    // Group again to get the highest value in each week
    { "$group": {
        "_id": "$_id.week",
        "userId": { "$first": "$_id.userId" },
        "username": { "$first": "$username" },
        "q": { "$first": "$q" },
        "p": { "$first": "$p" },
        "l": { "$first": "$l" },
        "score": { "$first": "$score" }, 
    }}

])

如果您需要运行总计,那么您可能最好将这些结果“预先汇总”到另一个集合中,并在整体性能方面保持谨慎的周数,甚至确实将运行总数保持一周或任何最适合您的时间范围

所以为了参考,这里有一点需要考虑,但可以查看主要文档的各种aggregation framework operators,尤其是Date aggregation operators,因为这些将允许您“组合”在一起日期的不同组成部分,包括周,月,日,小时或年。