Firestore,如何构建“likedBy”查询

Jos*_*osh 1 node.js firebase google-cloud-firestore

我在思考如何最好地构建我的(非常简单的)Firestore 应用程序时遇到了一些麻烦。我有一组这样的用户:

users: {
   'A123': {
      'name':'Adam'
   },
   'B234': {
      'name':'Bella'
   },
   'C345': {
      'name':'Charlie'
   }
}
Run Code Online (Sandbox Code Playgroud)

...并且每个用户都可以“喜欢”或“不喜欢”任意数量的其他用户(例如 Tinder)。

我想构建一个“喜欢”表(或 Firestore 等效表),以便我可以列出我还没有喜欢或不喜欢的人。我最初的想法是在用户表中创建一个“喜欢”对象,其布尔值如下所示:

users: {
   'A123': {
      'name':'Adam',
      'likedBy': {
         'B234':true,
      },
      'disLikedBy': {
         'C345':true
      }
   },
   'B234': {
      'name':'Bella'
   },
   'C345': {
      'name':'Charlie'
   }
}
Run Code Online (Sandbox Code Playgroud)

这样,如果我是 Charlie 并且我知道我的 ID,我可以列出我还没有喜欢或不喜欢的用户:

var usersRef = firebase.firestore().collection('users')
.where('likedBy.C345','==',false)
.where('dislikedBy.C345','==',false)
Run Code Online (Sandbox Code Playgroud)

这不起作用(每个人都被列出)所以我怀疑我的方法是错误的,尤其是“==false”部分。有人可以指出我如何构建这个的正确方向吗?作为一个额外的问题,如果有人改变了他们的名字会发生什么?我是否需要更改所有嵌入的“likedBy”数据?或者我可以使用云功能来实现这一点吗?

谢谢!

Dan*_*ath 6

这个问题没有完美的解决方案,但是您可以根据需要进行哪些权衡取舍。

选项:过扫描与欠扫描

请记住,Cloud Firestore 只允许与数据集总大小无关的查询。

这对于防止您构建一些在测试中可以使用 10 个文档的东西非常有帮助,但是一旦您投入生产并变得流行起来就会爆炸。不幸的是,这种类型的问题不适合这种可扩展的模式,您拥有的个人资料越多,人们创建的喜欢越多,在此处回答您想要的查询所需的时间就越长。

然后,解决方案是找到一个或多个可扩展且最能代表您想要的内容的查询。我可以想到两个选项,它们以不同的方式进行权衡:

  1. Overscan --> 做一个更广泛的查询,然后在客户端过滤
  2. 欠扫描 --> 执行一个或多个可能会错过一些结果的更窄的查询。

过扫描

在 Overscan 选项中,您基本上是通过增加成本来获得 100% 的准确度。

鉴于您的用例,我想这实际上可能是您的最佳选择。由于配置文件的总数可能比个人喜欢的配置文件数量大几个数量级,因此过度扫描增加的成本可能无关紧要。

只需选择与您拥有的任何其他条件匹配的所有配置文件,然后在客户端过滤掉用户已经喜欢的任何配置文件。

首先,获取用户喜欢的所有配置文件:

var likedUsers = firebase.firestore().collection('users') .where('likedBy.C345','==',false)

然后获取所有用户,检查第一个列表并丢弃任何匹配的内容。

var allUsers = firebase.firestore().collection('users').get()

根据规模,您可能希望优化第一步,例如,每次用户喜欢某人时,为该用户为其喜欢的每个人更新单个文档中的数组。通过这种方式,您可以在第一步中简单地获得一个文档。

var likedUsers = firebase.firestore().collection('likedUsers') .doc('C345').get()

由于此查询确实按结果集的大小进行缩放(通过将结果集定义为数据集),Cloud Firestore 可以回答它,而无需进行大量隐藏的不可扩展的工作。不可扩展的部分留给您优化(上面有 2 个示例)。

欠扫描

在欠扫描选项中,您基本上是在交易准确性以获得更窄(因此更便宜)的结果集。

这种方法更复杂,因此如果由于某种原因喜欢与不喜欢的比率不像我在 Overscan 选项中怀疑的那样,您可能只想考虑它。

基本思想是排除您肯定喜欢的人,并接受可能也排除您尚未喜欢的人的权衡 - 是的,基本上是Bloom filter

在每个用户的配置文件存储地图的true/false从价值观0m(我们会得到什么m就是后来的),这里的一切设置为false初始。

当用户喜欢该配置文件时,计算用户 ID 的哈希值以插入 Bloom 过滤器并将映射中的所有这些位设置为true

因此C345,如果我们使用 0110的哈希值m = 4,那么您的地图将如下所示:

likedBy: { 
   0: false,
   1: true,
   2: true,
   3: false }
Run Code Online (Sandbox Code Playgroud)

现在,要找到您绝对不喜欢的人,您需要使用相同的概念对地图中的每一位进行查询。对于任何一点0m你的散列是真实的,查询它是假的:

var usersRef = firebase.firestore().collection('users')
.where('likedBy.1','==',false)
Run Code Online (Sandbox Code Playgroud)

等等(当我们将来支持 OR 查询时,这会变得更容易)。任何false对您的用户 ID 哈希值有一定价值的人true肯定不会被他们喜欢。

由于您不太可能希望显示所有配置文件,仅显示一个页面就足够了,因此您可能会随机选择一个 ID 的哈希位为真,然后对其进行查询。如果您的配置文件用完,只需选择另一个正确的配置文件并重新启动。

假设大多数个人资料被点赞 500 次或更少,您可以使用m = 1675.

还有,以帮助您制定出每个配置文件喜欢,需要的假阳性率的比值,和方便的在线计算器m例如这里

过扫描 - 奖金

您会很快意识到,在 Overscan 选项中,每次运行查询时,都会显示用户上次不喜欢的相同配置文件。我假设你不想要那样。更糟糕的是,用户喜欢的所有内容都将在查询的早期出现,这意味着您最终将不得不一直跳过它们并增加成本。

有一个简单的解决方法,使用我在这个问题上描述的方法Firestore: How to get random documents in a collection。这将使您能够从集合中提取随机配置文件,为您提供更均匀的分布并减少在许多以前喜欢的配置文件上绊倒的机会。

欠扫描 - 奖金

我怀疑您在使用欠扫描选项时会遇到的一个问题是非常流行的配置文件。如果某人几乎总是受到喜欢,并且如果该配置文件的大小不适合保留在单个文档中,则您可能会开始超出布隆过滤器的用处(您将希望m小于说8000以避免遇到每个文档的索引限制云防火墙)。

对于此问题,您希望仅针对这些配置文件组合 Overscan 选项。使用 Cloud Functions,任何超过x%地图设置为 true 的配置文件都会获得一个popular设置为 true的标志。过扫描流行标志上的每个人,并将它们从欠扫描中编织到您的结果中(记得进行丢弃设置)。