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

因为Firebase(据我所知)没有内置的本机函数来实现这样的查询,所以我的第一个 idea 是使用查询游标来 Select 随机的开始和结束索引,前提是我有集合中的文档数.

这种方法会起作用,但只会以有限的方式起作用,因为每次都会按顺序将每个文件与其相邻的文件一起提供;然而,如果我能够通过父集合中的索引来 Select 文档,我可以实现随机文档查询,但问题是我找不到任何文档来描述如何实现这一点,甚至无法实现这一点.

以下是我想做的,考虑下面的FixStand模式:

root/
  posts/
     docA
     docB
     docC
     docD

然后在我的客户机(我在Swift环境中)中,我想写一个查询来实现这一点:

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

我能做点什么吗?或者,有没有其他方法可以以类似的方式 Select 随机文档?

请帮忙.

推荐答案

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

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

  1. 如何生成随机索引
  2. 如何查询随机索引
  3. Select 多个随机文档
  4. 为持续的随机性重新 seeder

如何生成随机索引

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

自动识别版本

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

在后面的查询部分中,您生成的随机值是一个新的自动id(iOSAndroidWeb),您查询的字段是__name__字段,后面提到的"低值"是一个空字符串.这是迄今为止生成随机索引最简单的方法,无论使用何种语言和平台,它都能正常工作.

默认情况下,文档名(__name__)仅按升序编制索引,并且除了删除和重新创建之外,您也无法重命名现有文档.如果您需要这两种方法中的任何一种,您仍然可以使用此方法,只需将自动id存储为名为random的实际字段,而不是为此目的重载文档名.

随机整数版本

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

你应该考虑你需要哪些语言,因为会有不同的考虑.虽然Swift很简单,但JavaScript显然有一个问题:

这将创建一个索引,对文档进行随机排序.在后面的查询部分中,您生成的随机值将是这些值中的另一个,后面提到的"低值"将是-1.

如何查询随机索引

现在您有了一个随机索引,您将需要查询它.下面我们来看一些简单的变体,可以 Select 1个随机文档,以及 Select 多个文档的选项.

对于所有这些选项,您都希望生成一个新的随机值,其形式与编写文档时创建的索引值相同,由下面的变量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)

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

双向的

环绕方法易于实现,只需启用升序索引即可优化存储.缺点之一是价值观可能受到不公平的保护.例如,如果10K中的前3个文档(A、B、C)的随机索引值为A:409496、B:436496、C:818992,那么A和C被选中的几率仅为1/10K,而B实际上被A的接近度所屏蔽,只有大约1/160K的几率.

您可以在>=<=之间随机 Select ,而不是在单个方向上进行查询,并在找不到值时进行环绕,这将不公平屏蔽值的概率降低了一半,而代价是索引存储的两倍.

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

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)

Select 多个随机文档

通常,您会希望一次 Select 多个随机文档.有两种不同的方法来调整上述技术,具体取决于您想要的权衡.

Rinse & Repeat

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

这种方法将为您提供随机的文档序列,而不用担心重复看到相同的模式.

取舍是,它将比下一种方法慢,因为它需要每个文档单独往返于服务.

继续

在这种方法中,只需增加所需文档的数量.这有点复杂,因为您可能会在通话中返回0..limit个文档.然后,您需要以相同的方式获取丢失的文档,但限制仅限于差异.如果你知道总文档数超过你要的数量,你可以通过忽略在第二次通话(但不是第一次)时永远无法收回足够文档的边缘情况进行优化.

这种解决方案的权衡是重复的.虽然文档是随机排序的,但如果最终出现重叠区域,您将看到与以前相同的模式.下一节将讨论如何缓解这种担忧.

这种方法比"冲洗"更快;重复"因为在最好的情况下,你需要一个电话或最坏的情况下两个电话的所有文件".

为持续的随机性重新 seeder

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

请注意,插入的文档最终会交织在两者之间,从而逐渐改变概率,删除文档也是如此.考虑到文档的数量,如果插入/删除率太小,有几种策略可以解决这个问题.

多重随机

您可以 for each 文档创建多个随机索引,然后每次随机 Select 其中一个索引,而不用担心重新 seeder .例如,将字段random设置为包含子字段1到3的 map :

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

现在,您将对random进行查询.1.随机.2.随机.3随机性,创造更大的随机性传播.这实质上是用增加的存储空间来节省增加的计算量(文档写入量).

在写作上重新设定种子

每次更新文档时,都要重新生成random字段的随机值.这将在随机索引中移动文档.

在阅读中重新设定种子

如果生成的随机值不是均匀分布的(它们是随机的,所以这是意料之中的),那么同一个文档可能会被选取的时间不成比例.这很容易通过在随机 Select 的文档读取后使用新的随机值更新来抵消.

由于写入成本更高,而且可能成为热点,因此您可以 Select 只在读取时更新一部分时间(例如,if random(0,100) === 0) update;).

Swift相关问答推荐

自定义完整屏幕封面动画

SWIFT:使字典数组值可变

如何在SWIFT Memory中处理对VAR数组的更新(对COW感到困惑)

SwiftUI 拖放:如果没有上下文类型,则无法解析对成员‘first’的引用

SwiftUI如何能够在Text中使用字符串字面量来创建LocalizedStringKey?

列表不显示信息

在 Swift 的异步上下文中,如何指示我想要函数的同步版本?

在 SwiftUI 中将属性显式设置为其默认值

Pod lib lint 命令找不到 watchos 模拟器

从 Swift Vapor Server 中调用 ShazamKit

签署GoogleSignIn-GoogleSignIn需要开发团队

Swift初始化具有可变ID的重复值数组

在 Swift 5.5 中编写同步和异步函数

Swift 2 或 3 中的 Google Analytics 问题

FirebaseStorage:如何删除目录

'NSLog' 不可用:可变参数函数在 swift 中不可用

我可以在 Swift 中为泛型类型 T 分配默认类型吗?

Swift - 导入我的 swift 类

'类 'className' 的 NSManagedObject 必须具有有效的 NSEntityDescription.错误

如何快速显示数组的所有元素?