Swift/Firestore 在排名系统中查询集合

Swift/Firestore Query a Collection in a Ranking System

我逐渐意识到我从 SO 社区学到了很多东西,所以提前感谢您的宝贵时间。

以下是我需要完成的工作的摘要:

假设我在一个 Firestore 集合中有 1,000 个用户,每个用户都有一个关联的分数。用户也可以互相关注。假设 'User A' 需要 运行 查询 he/she 关注的 100 个用户,同时保持每个用户在全球 1,000 个用户中的排名。 IE。用户A正在关注排名#1的人、排名#237的人、排名#999的人等

我的数据目前结构如下:

user_information (Collection)
-- user_id (Document)
---- user_score (Int Field) <- the field in which the query is sorted by
users (Collectiion)
-- user_id (Document)
---- following_users (Collection) <- Running the query on these users
------ user_id (Document)

根据相对于彼此的分数对 following_users 进行排序很容易,但是如何在仅查询被关注的 100 个用户的同时保持全球排行榜中的排名?我需要采取不同的方法来完成这样的任务吗?

在我回答你的最后一个问题之前,让我分享一下:我的方法是通过以下方式调整你的数据库结构:

user_information (Collection)
-- user_id (Document)
---- user_score (Int Field) <- the field in which the query is sorted by
users (Collectiion)
-- user_id (Document)
---- following_users (Array Field) [Array field of user_id's this user is following]

通过使 following_users 成为数组而不是集合,您将不必在可能的数百个地方维护相同的 user_id 文档。

假设用户 A、R、D、S、T、(...) 都关注用户 H,这意味着您的用户 H 的文档在任何地方都应该相同。您将不得不使用事务来确保用户排名在任何地方都相同,无论何时发生变化。对于您添加到 user_id 文档的任何新字段,这可能是相同的。

将其扩展到仅 10,000 个用户,您的写入操作数量可能是您真正需要的数量的 100 倍。

通过仅引用数组中的关注用户,过程如下:

  1. 查询当前用户文档
  2. (可选):提供一个 UI 按钮以“显示我关注的用户”
  3. 请求该数组中的文档,并可能按排名排序。

如果你想跳过步骤#2,那么你可以直接查询关注的用户,按排名排序,例如限制为10个结果。然后滚动加载更多或单击下一步。 (搜索 'paginated firestore queries' 或 'firestore query cursors')。

现在,回答您的问题“但是我如何在仅查询被关注的 100 个用户的同时保持全球排行榜中的排名?”...

在这种情况下,我会这样做:

在您的 user_id 文档中引入一个新字段,名为:'followerCount',将其设为整数。

  1. 每次用户决定关注另一个用户时,我都会将 followed 添加到关注者的“following_users”数组字段中。
  2. 同时,我会increment被关注用户的'followerCount'
  3. 如果关注者决定取消关注,我会从“following_users”数组中删除被关注的 user_id 并减少被关注用户的“followerCount”。

旁注,我会使用“Firestore Transactions”(google 它)进行此类操作,以确保它们同时发生。

旁注 #2:在 Firestore 中,您可以递增 -1 来递减。

旁注 #3:在 following_users 数组字段上,我鼓励您使用 FieldValue.arrayUnion 添加 user_id 并使用 FieldValue.arrayRemove 删除它们。

以这种方式构建事物,在您的排行榜上执行查询将非常容易。示例:

await leaderboard_query = db.collection(user_information).where('followerCount', '>', 0).orderBy('user_score');