当前位置: 首页 > 面试题库 >

Firestore:如何在集合中获取随机文档

吴城
2023-03-14
问题内容

对于我的应用程序而言,至关重要的是能够从Firebase的集合中随机选择多个文档。

由于Firebase(我知道)没有内置本机函数来实现执行此操作的查询,因此我的第一个想法是使用查询游标选择随机的起始索引和终止索引,前提是我拥有​​其中的文档数集合。

这种方法行之有效,但只能以有限的方式进行,因为每次每次文档都会与其相邻文档一起依次送达。但是,如果我能够通过其父集合中的索引选择一个文档,则可以实现随机文档查询,但是问题是我找不到任何描述如何执行此操作甚至可以执行此操作的文档。

这是我想做的,请考虑以下Firestore模式:

root/
  posts/
     docA
     docB
     docC
     docD

然后在我的客户端(我处于Swift环境中)中,我想编写一个查询来做到这一点:

db.collection("posts")[0, 1, 3] // would return: docA, docB, docD

无论如何,我可以做些类似的事情吗?或者,是否可以通过其他方式以类似方式选择随机文档?

请帮忙。


问题答案:

使用随机生成的索引和简单查询,您可以从Cloud Firestore中的集合或集合组中随机选择文档。

该答案分为4个部分,每个部分中都有不同的选项:

  1. 如何生成随机索引
  2. 如何查询随机索引
  3. 选择多个随机文档
  4. 重新播种进行中的随机性

该答案的基础是创建一个索引字段,该字段在按升序或降序排序时会导致所有文档都被随机排序。有多种创建方法,因此让我们看一下2,从最容易获得的开始。

自动编号版本

如果您使用的是我们客户库中提供的随机生成的自动ID,则可以使用同一系统随机选择一个文档。在这种情况下,随机排序的索引 文档ID。

在我们的查询部分的后面,您生成的随机值是一个新的自动ID(iOS,Android,Web),而您查询的__name__字段是该字段,后面提到的“低值”是一个空字符串。到目前为止,这是生成随机索引的最简单方法,无论使用哪种语言和平台,它都可以工作。

默认情况下,文档名称(__name__)仅按升序索引,并且您也不能在删除和重新创建后重命名现有文档。如果您需要其中任何一个,您仍然可以使用此方法,仅将自动ID存储为一个实际的字段,random而不是为此目的而重载文档名称。

随机整数版本

编写文档时,首先生成一个有界范围内的随机整数,并将其设置为称为的字段random。根据您期望的文档数量,您可以使用其他有界范围来节省空间或降低发生冲突的风险(这会降低该技术的有效性)。

您应该考虑所需的语言,因为会有不同的考虑。尽管Swift很简单,但是JavaScript显然有一个陷阱:

  • 32位整数:适用于小型(〜10K 不太可能发生碰撞)数据
  • 64位整数:大型数据集(注:JavaScript的本身并不支持,但)

这将创建一个索引,其中您的文档被随机排序。在我们的查询部分的后面,您生成的随机值将是这些值中的另一个,而稍后提到的“低值”将是-1。

如何查询随机索引

现在您有了一个随机索引,您将要查询它。下面我们看一些选择1个随机文档的简单变体,以及选择1个以上随机文档的选项。

对于所有这些选项,您将希望以与编写文档时创建的索引值相同的形式生成一个新的随机值,由random下面的变量表示。我们将使用此值在索引上找到随机点。

环绕式

现在您有了一个随机值,就可以查询一个文档:

let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)

检查是否已返回文档。如果不是,请再次查询,但对随机索引使用“低值”。例如,如果您做了随机整数,lowValue则为0

let postsRef = db.collection("posts")
queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: lowValue)
                   .order(by: "random")
                   .limit(to: 1)

只要您有一个文档,就可以保证至少返回1个文档。

双向的

环绕式方法易于实现,并允许您仅启用升序索引来优化存储。缺点之一是价值观受到不公正保护的可能性。例如,如果10K中的前3个文档(A,B,C)具有随机索引值A:409496,B:436496,C:818992,则A和C的被选中机会不到1
/ 10K,而B被A的接近有效地屏蔽,只有大约1 / 160K的机会。

您可以在>=和之间进行随机选择,而不必在一个方向上进行查询并四处寻找,而是可以在和之间随机选择<=,这将不正确屏蔽的值的概率降低了一半,但代价是索引存储量增加了一倍。

如果一个方向未返回结果,请切换到另一方向:

queryRef = postsRef.whereField("random", isLessThanOrEqualTo: random)
                   .order(by: "random", descending: true)
                   .limit(to: 1)

queryRef = postsRef.whereField("random", isGreaterThanOrEqualTo: random)
                   .order(by: "random")
                   .limit(to: 1)

选择多个随机文档

通常,您一次要选择多个随机文档。有两种不同的方法可以根据所需的权衡来调整上述技术。

冲洗并重复

这种方法很简单。只需重复此过程,包括每次选择一个新的随机整数。

此方法将为您提供随机的文档序列,而不必担心重复看到相同的模式。

权衡是,它将比下一种方法慢,因为它需要为每个文档单独进行服务往返。

保持它来

用这种方法,只需增加所需文档的数量即可。由于您可能会0..limit在通话中返回文档,因此稍微复杂一点。然后,您将需要以相同的方式获取丢失的文档,但是将限制减少到仅差异。如果您知道总的文档数量超过了所需数量,则可以通过忽略在第二次调用(而不是第一次调用)中永远不会取回足够文档的极端情况进行优化。

这种解决方案的权衡是重复进行的。尽管文档是随机排序的,但是如果最终出现重叠范围,则会看到与以前相同的模式。在缓解播种的下一节中将讨论减轻这种担忧的方法。

这种方法比“漂洗和重复”的速度更快,因为您将在一次调用的最佳情况下或两次调用的最坏情况下请求所有文档。

重新播种进行中的随机性

尽管如果文档集是静态的,此方法会为您随机提供文档,但是返回每个文档的概率也将是静态的。这是一个问题,因为根据获得的初始随机值,某些值可能具有不公平的低或高概率。在许多用例中,这很好,但是在某些情况下,您可能希望增加长期随机性,以便有更统一的机会返回任何1个文档。

请注意,插入的文档最终将在中间编织,逐渐更改概率,而删除文档也会如此。如果给定文档数,如果插入/删除率太小,则有几种解决方法。

多随机

不必担心重新播种,您始终可以为每个文档创建多个随机索引,然后每次都随机选择这些索引之一。例如,让该字段random成为具有1到3子字段的地图:

{'random': {'1': 32456, '2':3904515723, '3': 766958445}}

现在,您将随机查询random.1,random.2,random.3,从而扩大随机性。从本质上讲,这需要交换增加的存储空间,以节省因重新设置种子而增加的计算量(文档写入)。

重新写入种子

每当您更新文档时,请重新生成该random字段的随机值。这将使文档在随机索引中移动。

重新读取种子

如果生成的随机值不是均匀分布的(它们是随机的,那么这是可以预期的),那么可能会在不适当的时间内选择同一文档。通过在读取后用新的随机值更新随机选择的文档,可以轻松解决此问题。

由于写入操作较为昂贵且可能会引起热点,因此您可以选择仅在部分时间读取时进行更新(例如if random(0,100) === 0) update;)。



 类似资料:
  • 我现在有一个firestore,看起来像这样: 我使用的是,我试图获取“报告”集合中的所有文档。 我可以得到一个报告: 但我无法得到整个子集合 这可能吗?我是新手,所以这可能是一个愚蠢的问题。 谢谢 编辑控制台中收到的错误消息是: 我不确定权限,我没有访问权限,但如果是权限问题,我很困惑,我可以从集合中获取一个文档,但找不到获取所有文档的方法

  • 问题内容: 我的Firestore数据库结构: 现在我想从中获取文件清单。将有一个包含以下数据的列表。我需要它是因为我想制作一个微调器并将这些数据放入微调器中。然后,用户将选择一些文档并下载。 我尝试使用下面的代码: 但是代码只有在我拥有没有文档集合的数据结构时才起作用。在其他情况下,中没有任何文档。 谢谢! 问题答案: 要在中包含所有文档名称的列表,请使用以下代码: 您的logcat中的结果将是

  • 我使用的是Vue.js和Firebase FiRecovery。现在我有两个集合和。在订单集合中,我已经存储了集合的每个文档的id。我现在必须从中获取相应用户的详细信息。我应该怎么做?这是我迄今为止所做的 我需要在userInfo中存储用户数据。提前谢谢

  • 我有一个包含10个文档的产品根集合。每个文档都有100个文档的子集合,没有其他字段。 现在如果我阅读产品收藏的文件 将收取多少次读取费用,由于我正在获取具有10个文档的产品集合的文档,我将收取10次读取费用?或者每个文档都有100个文档的子集合,因此我将收取10*100 =1000文档读取费用?

  • 是否有方法获取将文档添加到集合后生成的文档id? 如果我将文档添加到代表社交媒体软件中“帖子”的集合中,我想获取该文档ID并将其用作不同集合中另一个文档中的字段。 如果我无法获取添加文档后生成的文档Id,我是否应该计算一个随机字符串,并在创建文档时提供Id?这样我就可以使用与其他文档中的字段相同的字符串了? 快速结构示例:

  • 我想获得firestore集合中的文档总数,我正在制作一个论坛应用程序,所以我想显示每个讨论中的当前评论量。有之类的东西吗?