gpt4 book ai didi

swift - Firestore : How to get random documents in a collection

转载 作者:行者123 更新时间:2023-12-03 08:34:16 28 4
gpt4 key购买 nike

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

由于 Firebase 中没有内置的 native 函数(我知道)来实现这样的查询,我的第一个想法是使用查询游标来选择一个随机的开始和结束索引,前提是我有文档的数量集合。

这种方法可以工作,但只能以有限的方式工作,因为每次都将按顺序提供每个文档及其相邻文档;但是,如果我能够通过其父集合中的索引选择文档,我可以实现随机文档查询,但问题是我找不到任何描述如何执行此操作或即使您可以执行此操作的文档。

这是我希望能够做的事情,请考虑以下 Firestore 架构:

root/
posts/
docA
docB
docC
docD

然后在我的客户端(我在 Swift 环境中)我想编写一个可以执行此操作的查询:
db.collection("posts")[0, 1, 3] // would return: docA, docB, docD

无论如何我可以做一些类似的事情吗?或者,是否有不同的方式可以以类似的方式选择随机文档?

请帮忙。

最佳答案

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

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

  • 如何生成随机索引
  • 如何查询随机索引
  • 选择多个随机文档
  • 重新播种以获得持续的随机性

  • 如何生成随机索引

    这个答案的基础是创建一个索引字段,当按升序或降序排序时,会导致所有文档被随机排序。有不同的方法来创建它,所以让我们看看 2,从最容易获得的开始。

    自动识别版本

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

    在我们查询部分的后面,您生成的随机值是一个新的自动 ID( iOSAndroidWeb ),您查询的字段是 __name__ 字段,后面提到的“低值”是一个空字符串。这是迄今为止生成随机索引的最简单方法,并且不受语言和平台的影响。

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

    随机整数版本

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

    您应该考虑您需要哪些语言,因为会有不同的考虑。虽然 Swift 很简单,但 JavaScript 有一个明显的问题:
  • 32 位整数:非常适合小型(~10K unlikely to have a collision)数据集
  • 64 位整数:大型数据集(注意:JavaScript 本身不支持,yet)

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

    双向

    环绕方法易于实现,并且允许您仅启用升序索引来优化存储。一个缺点是值(value)被不公平地屏蔽的可能性。例如,如果 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)

    选择多个随机文档

    通常,您需要一次选择 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; )。

    关于swift - Firestore : How to get random documents in a collection,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46798981/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com