我知道这是一个老问题,但我只是想分享我的解决方案以供将来参考。首先,Firebase 生态系统发生了很大变化,我假设当前的最佳实践(即 Firestore 和无服务器功能)。我个人在构建实际应用程序时考虑了这些解决方案,并最终选择了预定的近似等级。
实时排名(最新,但价格昂贵)
在准备用户排行榜时,我做了一些假设:
- 排行榜根据我以后称之为“得分”的数字对用户进行排名
- 新用户在排行榜上排名最低,因此在创建用户时,他们的排名被设置为总用户数(使用 Firebase 函数设置排名,但也会将“总用户”计数器增加 1)。
- 分数只能增加(也可以支持一些适应降低分数)。
- 已删除的用户会在排行榜上保留一个“幽灵”位置。
每当用户提高他们的分数时,Firebase 函数会通过查询所有被超越的用户(他们的分数 >= 用户的旧分数但 decreased 提高 1 . 用户自己的排名是increased 由前面提到的查询的大小。
现在可以在客户端读取时立即获得排名。然而,提议的函数内部的排名更新是相当多的读写。操作的确切数量很大程度上取决于您的应用程序,但对于我个人的应用程序,分数变化的频率很高并且分数的相对接近性使得这种方法效率太低。我很好奇是否有人找到了更有效(实时)的替代方案。
预定排名(最简单,但昂贵且周期性)
调度一个 Firebase 函数,通过升序对整个用户集合进行简单排序,然后写回每个用户集合的排名(在批量更新中)。此过程可以每天重复,或者根据您的应用程序更频繁/不频繁地重复。对于 N 个用户,该函数总是进行 N 次读取和 N 次写入。
预定的近似排名(最便宜,但不精确和周期性)
作为“计划排名”选项的替代方案,我建议使用近似技术:不是为每个计划更新写入每个用户的确切排名,而是将用户集合(仍然像以前一样排序)简单地分成 M 个块大小相同并且绑定这些块的分数被写入单独的“统计”集合。
因此,例如:如果我们为简单起见使用 M = 3,并且我们读取按升序排序的 60 个用户,那么我们有 3 个 20 个用户的块。对于每个(仍然排序的块),我们得到最后一个(块的最低分数)和第一个用户(块的最高分数)(即包含该块的所有分数的范围)的分数。假设得分最低的块的得分范围为 20-120,第二个块的得分为 130-180,得分最高的块的得分为 200-350。我们现在只需将这些范围写入“统计”集合(无论有多少用户,写入计数都会减少到 1!)。
在排名检索时,用户只需阅读最新的“统计”文档,并通过将范围与自己的分数进行比较来近似他们的百分排名。当然,用户得分可能高于前一次“统计”更新中的最高分或低于最低分,但我会认为他们分别属于最高得分组和最低得分组。
在我自己的应用程序中,我使用了 M = 20,因此可以以 5% 的准确度显示用户百分位排名,并使用线性插值甚至在该范围内进行估计(例如,如果用户得分为 450 并且落入 40% -45%-chunk 范围为 439-474,我们估计用户的百分等级为40 + (450 - 439) / (474 - 439) * 5 = 41.57...%)。
如果您想获得真正的幻想,您还可以通过将您的预期分数分布(例如正态分布)拟合到测量范围来估计精确的百分位排名。
注意:所有用户都需要阅读“统计”文档来估算他们的排名。但是,在大多数应用程序中,并非所有用户都实际查看统计信息(因为他们不是每天都在活动,或者只是对统计信息不感兴趣)。就个人而言,我还使用“stats”文档(名称不同)来存储用户之间共享的其他 DB 值,因此无论如何该文档已经被检索到。除此之外,读取比写入便宜 3 倍。最坏的情况是 2N 次读取和 1 次写入。