我有一个 Django 模型,如下所示:
class LeaderboardScore(models.Model):
score = models.FloatField()
player_account = models.ForeignKey(PlayerAccount, on_delete=models.CASCADE)
timestamp = models.DateTimeField()
这代表游戏中的最高分。分数按照分数降序和时间戳升序排列,因此如果两个分数相同,则最早的分数排名更高。
每次查询排行榜时,我都希望在查询结果中包含每个实例的全球排名。当我像这样查询所有分数时,我可以成功做到这一点:
from django.db.models import F, Window
from django.db.models.functions import DenseRank
lq = LeaderboardScore.objects.filter(leaderboard__id=1).annotate(
rank=Window(
expression=DenseRank(),
order_by=[F('score').desc(), F('timestamp').asc()]
)
)
这将在排名字段中正确显示排名。但是如果我接受上面的查询并按player_account过滤它,如下所示:
# rank will be 1, but should be the global ranking
lq.filter(player_account__id=123)[0].rank
然后重置排名,以便查询中的第一个项目的排名为 1,即使其全局排名应该不同。如何保持全球排名?
我不知道如何执行 Django 的 ORM,但我能够使用 SQL 作为临时解决方案:
WITH ranked_leaderboard AS (
SELECT
id,
score,
timestamp,
player_account_id,
DENSE_RANK() OVER (ORDER BY score DESC, timestamp ASC) AS rank
FROM
leaderboards_leaderboardscore
WHERE
leaderboard_id = 1
)
SELECT
id,
score,
timestamp,
rank
FROM
ranked_leaderboard
WHERE
player_account_id = 123
ORDER BY
score DESC, timestamp ASC
这不是一个理想的解决方案,因为它仅在我搜索特定玩家时才有效 - 我还有其他查询可能由任意玩家列表组成,我对此使用了非最佳解决方案。