我在为我的游戏设计数据库时遇到问题,游戏的用户有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
现在我可以按总分进行查询和排序,但它没有关于此分数是否为过去七天的任何数据,仍然不知道如何实现这一目标。
答案 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,因为这些将允许您“组合”在一起日期的不同组成部分,包括周,月,日,小时或年。