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

Gar*_*aye 31 database data-modeling firebase swift google-cloud-firestore

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

由于Firebase(我知道)没有内置本机函数来实现这样做的查询,我首先想到的是使用查询游标来选择随机的开始和结束索引,前提是我有多少文档集合.

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

这是我想要做的,考虑以下firestore架构:

root/
  posts/
     docA
     docB
     docC
     docD
Run Code Online (Sandbox Code Playgroud)

然后在我的客户端(我在Swift环境中)我想编写一个可以执行此操作的查询:

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

无论如何我能做到这一点吗?或者,有不同的方式我可以以类似的方式选择随机文档吗?

请帮忙.

Dan*_*ath 63

文档ID不可知版本

编写文档时,首先生成一个随机的64位整数,并将其添加为一个名为的字段__name__.

这将创建一个索引,随机排序您的文档.

现在,要选择随机文档,请生成另一个随机64位整数.我们将在查询中使用它来选择索引中的随机位置并读取下一个文档:

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

检查这是否已返回文档.如果没有,请反转方向并再试一次:

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

自动Id版本

如果您使用我们的客户端库中提供的随机生成的自动ID,您可以使用相同的系统随机选择文档.

在此系统中,生成要在查询中使用的新自动ID.现在选择集合中大于新自动ID的文档(限制1).

与不可知版本略有不同的是,您目前无法按降序排序文档ID.这意味着如果您的查询未返回文档,则需要生成新的自动ID并重试.

多个随机数

通常,您希望一次选择多个随机文档.根据您想要的权衡取舍,有两种不同的方法来调整上述技术.

冲洗并重复

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

此方法将为您提供随机的文档序列,而无需担心重复查看相同的模式.

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

坚持下去

在这种方法中,只需将限制数量增加到所需文档即可.这有点复杂,因为您可能会__name__在通话中返回文档.然后,您需要以相同的方式获取丢失的文档,但限制仅限于差异.如果您知道总共有多个文档而不是您要求的数字,那么您可以忽略永远无法获得足够文档的边缘情况.

与该解决方案的权衡是重复的顺序.虽然文档是随机排序的,但如果您最终重叠范围,您将看到之前看到的相同模式.尽管如此,有两种方法可以缓解这种担忧.首先,插入的文档将最终编织在中间,逐渐改变序列.其次,您可以通过random在每次更新时使用新数字更新字段来加速这一过程.

这种方法比"冲洗和重复"更快,因为您将在一次通话中请求所有文档.

  • 这么多工作,而不是简单地添加 `orderByRandom()` api :\ (7认同)
  • 这可以很好地添加到[解决方案](https://firebase.google.com/docs/firestore/solutions/)页面 (5认同)
  • 这是荒唐的 (3认同)
  • 非常酷的解决方案丹!事实上......这在实时数据库上也应​​该是可能的,不是吗? (2认同)

ajz*_*zbc 11

发布此信息可帮助以后遇到此问题的任何人。

如果您使用的是自动ID,则可以生成新的自动ID并查询最接近的自动ID,如Dan McGrath的Answer中所述

我最近创建了一个随机报价API,需要从Firestore集合中获取随机报价。
这就是我解决该问题的方法:

var db = admin.firestore();
var quotes = db.collection("quotes");

var key = quotes.doc().id;

quotes.where(admin.firestore.FieldPath.documentId(), '>=', key).limit(1).get()
.then(snapshot => {
    if(snapshot.size > 0) {
        snapshot.forEach(doc => {
            console.log(doc.id, '=>', doc.data());
        });
    }
    else {
        var quote = quotes.where(admin.firestore.FieldPath.documentId(), '<', key).limit(1).get()
        .then(snapshot => {
            snapshot.forEach(doc => {
                console.log(doc.id, '=>', doc.data());
            });
        })
        .catch(err => {
            console.log('Error getting documents', err);
        });
    }
})
.catch(err => {
    console.log('Error getting documents', err);
});
Run Code Online (Sandbox Code Playgroud)

查询的关键是这样的:

.where(admin.firestore.FieldPath.documentId(), '>', key)
Run Code Online (Sandbox Code Playgroud)

如果找不到文档,则以相反的操作再次调用它。

我希望这有帮助!
如果有兴趣,您可以在GitHub找到我的API的这一特定部分

  • 极不可能遇到文档 ID 的这个问题,但如果有人复制它并在更小的 ID 空间中使用它,我建议将第一个 where 子句从“&gt;”更改为“&gt;=”。这可以防止在它们只有 1 个文档的边缘情况下失败,并且以这样一种方式选择 `key`,使其恰好是 1 个文档的 id。 (4认同)
  • 感谢您在这里发布的精彩答案。我有一个问题,“admin.firestore.FieldPath.documentId()”到底指什么? (2认同)
  • 我正在使用 Flutter,这不会随机获取文档。返回相同文档的几率很高。最终它会得到随机文档,但 90% 的情况是相同的文档 (2认同)