node.js - Firestore,如何构建“likedBy”查询

标签 node.js firebase google-cloud-firestore

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

users: {
   'A123': {
      'name':'Adam'
   },
   'B234': {
      'name':'Bella'
   },
   'C345': {
      'name':'Charlie'
   }
}


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

我想构建一个“喜欢”表(或等效的Firestore),以便我列出尚未喜欢或不喜欢的人。我最初的想法是在用户表中使用这样的布尔值创建一个“喜欢”对象:

users: {
   'A123': {
      'name':'Adam',
      'likedBy': {
         'B234':true,
      },
      'disLikedBy': {
         'C345':true
      }
   },
   'B234': {
      'name':'Bella'
   },
   'C345': {
      'name':'Charlie'
   }
}


这样,如果我是Charlie并且知道我的ID,则可以列出尚未被我喜欢或不喜欢的用户:

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


这不起作用(每个人都被列出),所以我怀疑我的方法是错误的,尤其是“ == false”部分。有人能指出我正确的方向吗?额外的一个问题是,如果有人更名会怎样?我是否需要更改所有嵌入的“ likedBy”数据?还是可以使用云功能来实现这一目标?

谢谢!

最佳答案

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

选项:过扫描与欠扫描

请记住,Cloud Firestore仅允许查询的扩展独立于数据集的总大小。

这对于防止您构建可以在10个文档中进行测试的东西确实很有帮助,但是一旦您投入生产并大受欢迎,它就会立即崩溃。不幸的是,这种类型的问题不适合那种可扩展的模式,您拥有的配置文件越多,人们创建的喜欢度越高,回答您想要的查询所需的时间就越长。

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


过扫描->进行更广泛的查询,然后在客户端进行过滤
欠扫描->进行一个或多个狭窄的查询,可能会遗漏一些结果。


过扫描

在“过扫描”选项中,您基本上是在交易成本增加而获得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个示例)。

欠扫描

在“欠扫描”选项中,您基本上是在以准确性为代价来获得更窄(因此价格更便宜)的结果集。

这种方法比较复杂,因此,如果出于某种原因,喜欢的比率与不喜欢的比率不像我在“过扫描”选项中所怀疑的那样,您可能只想考虑使用它。

基本想法是,如果您确实喜欢某人,则将其排除在外,并接受可能还会排除您尚未喜欢的某人的折衷方案-是的,基本上是Bloom filter

在每个用户配置文件中,存储从truefalse0 / m值的映射(稍后将介绍m),其中所有内容最初都设置为false

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

因此,假设我们使用C345m = 4散列为0110,则您的地图将如下所示:

likedBy: { 
   0: false,
   1: true,
   2: true,
   3: false }


现在,要查找您肯定不喜欢的人,您需要使用相同的概念针对地图中的每一位进行查询。对于您的哈希为true的0m的任何位,查询它是否为false:

var usersRef = firebase.firestore().collection('users')
.where('likedBy.1','==',false)


等等(将来我们支持OR查询时,这会变得更加容易)。在用户的ID哈希到false的某个点上具有true值的任何人肯定不会被他们喜欢。

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

假设大多数配置文件被喜欢500次或更少,您可以使用m = 1675将误报率保持在20%或更少。

有方便的在线计算器可帮助您确定每个配置文件的喜欢率,所需的误报率以及mfor example here

过扫描-奖金

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

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

欠扫描-奖金

我怀疑您使用欠扫描选项会遇到的一个问题是非常受欢迎的配置文件。如果某个人几乎总是被喜欢,那么如果该配置文件的大小不适合保存在单个文档中,则可能会超出Bloom过滤器的用途(您希望m小于8000以避免运行)纳入Cloud Firestore中的每个文档索引限制)。

对于此问题,您只想为这些配置文件组合“过扫描”选项。使用Cloud Functions,将映射的x%设置为true以上的任何配置文件都会将popular标志设置为true。对流行标志上的每个人进行过扫描,并将其编织到“欠扫描”结果中(请记住进行丢弃设置)。

关于node.js - Firestore,如何构建“likedBy”查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49010760/

相关文章:

javascript - 如何使用 firebase 数据填充 Accordion ?

node.js - 为什么永远的错误消息没有被记录

javascript - Axios Http 客户端 - 如何使用表单参数构造 Http Post url

javascript - 基本 JS - 声明 require var 并在类内部使用

android - 具有新依赖项的 Firestore 崩溃应用程序

android - 复制到 APK lib/x86/librealm-jni.so 中的重复文件

firebase - 如何为firebase firestore配置获取googleappid?

android - 无法使用 azure 通知中心接收特定用户推送通知

java - Firestore 查询中函数调用的顺序重要吗?

database - 如果两个用户尝试几乎同时更新 Firestore 中的文档,会发生什么情况?