【问题标题】:Best design for user leaderboard scores in mongodbMongoDB中用户排行榜分数的最佳设计
【发布时间】:2014-04-06 16:05:24
【问题描述】:

我在为我的游戏设计数据库时遇到问题,游戏的用户有 3 个不同权重的得分参数,得分是应用程序。所以用户看起来像这样:

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

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

我想根据用户的周/月活动来查询信息,也就是说p、q、l得分最高的用户将是当周的top用户。

假设我还想与周围的用户一起制作排行榜并拥有一个职位,我完全不明白如何制作适当的架构来支持按周/月进行日期查询并以有效的方式支持分数的加权计算当前用户数

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

非常感谢您的帮助。

更新: 所以这是设计的草稿

我有动作集合,其中包含每个用户生成的动作的文档,就像这样:

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

其他类型也是如此。

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

totalscore = q + p/10 + l/100

现在我可以按总分查询和排序,但是没有任何关于这个分数是否是过去7天的数据,仍然不知道如何实现这一点。

【问题讨论】:

  • 答案中是否有不符合您需求的内容?您还没有回复。

标签: javascript mongodb mongoose


【解决方案1】:

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

虽然您可以使用 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,因为它们可以让您在不同组件上“组合”在一起日期,可以是周、月、日、小时或年。

【讨论】:

  • 谢谢!但是我不明白这个设计如何让我通过分数来围绕用户?
猜你喜欢
  • 2022-01-09
  • 2022-01-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多