编辑: 2021 年 10 月 29 日:
要清楚文档中存在的以下句子:
如果您不根据具有顺序值的字段进行查询。
时间戳只能不被认为是连续的。但是,它仍然可以被认为是连续的。相同的规则适用于按字母顺序排列的(Customer1、Customer2、Customer3、...)或几乎一切可以被视为可预测生成的值。
Firestore 索引中的此类顺序数据,最有可能写入存储介质上的物理邻近位置,因此存在这种限制。
话虽如此,请注意 Firestore 使用一种机制将文档映射到相应的位置。这意味着如果这些值不是随机分布的,则写入操作将不会正确分布在这些位置上。这就是存在这种限制的原因。
另请注意,您可以在特定时间内写入此类位置的数据量存在物理限制。可预测的键/值很可能最终会出现在同一个位置,这实际上很糟糕。所以有更多的变化来达到这个限制。
编辑: 2021 年 7 月 16 日:
由于这个答案听起来有点陈旧,我将尝试添加一些使用随着时间的推移而发现的子集合的优势:
- 子集合将始终为您提供更结构化的数据库架构,因为您始终可以引用与特定文档仅相关的子集合。因此,您只能嵌套与特定文档相关的数据。
- 如前所述,maximum depth of a subcollection is 100。所以这里的一个重要特性是,Firestore 查询在 1 级时与在 100 级时一样快。所以不应该担心深度。此功能已经过测试。
- 默认情况下对子集合中的查询进行索引,就像顶级集合的情况一样。
- 就速度而言,Query是顶级集合、子集合还是集合组都无所谓,速度总是一样的,只要Query返回相同数量的文档.发生这种情况是因为查询性能取决于您请求的文档数量,而不取决于您搜索的文档数量。因此查询子集合与查询顶级集合的效果相同,完全没有缺点。
- 在子集合中存储文档时,请注意无需将文档 ID 存储为字段,因为默认情况下它是参考的一部分。这意味着您可以在子集合中存在的文档中存储更少的数据。更重要的是,如果您将相同的数据保存在顶级集合中,并且您需要创建一个包含两个 whereEqualTo() 调用 + 一个 orderBy() 调用的查询,然后是一个 index would be required。
- 在安全性方面,子集合允许继承安全规则,这很有用,因为我们可以编写越来越少的代码来保护数据库。
暂时就这样,如果我发现其他好处,我会更新答案。
让我们举个例子。假设我们有一个用于测验应用程序的数据库模式,如下所示:
Firestore-root
|
--- questions (collections)
|
--- questionId (document)
|
--- questionId: "LongQuestionIdOne"
|
--- title: "Question Title"
|
--- tags (collections)
|
--- tagIdOne (document)
| |
| --- tagId: "yR8iLzdBdylFkSzg1k4K"
| |
| --- tagName: "History"
| |
| --- //Other tag properties
|
--- tagIdTwo (document)
|
--- tagId: "tUjKPoq2dylFkSzg9cFg"
|
--- tagName: "Geography"
|
--- //Other tag properties
其中tags 是questionId 对象中的子集合。现在让我们创建 tags 集合作为顶级集合,如下所示:
Firestore-root
|
--- questions (collections)
| |
| --- questionId (document)
| |
| --- questionId: "LongQuestionIdOne"
| |
| --- title: "Question Title"
|
--- tags (collections)
|
--- tagIdOne (document)
| |
| --- tagId: "yR8iLzdBdylFkSzg1k4K"
| |
| --- tagName: "History"
| |
| --- questionId: "LongQuestionIdOne"
| |
| --- //Other tag properties
|
--- tagIdTwo (document)
|
--- tagId: "tUjKPoq2dylFkSzg9cFg"
|
--- tagName: "Geography"
|
--- questionId: "LongQuestionIdTwo"
|
--- //Other tag properties
这两种方法的区别在于:
- 如果您想查询数据库以获取特定问题的所有
tags,使用第一个模式非常容易,因为只需要CollectionReference(问题 -> questionId -> 标签)。要使用第二个模式实现相同的目的,而不是 CollectionReference,需要 Query,这意味着您需要查询整个 tags 集合以仅获取与单个问题对应的标签。
- 使用第一个模式,一切都更有条理。除此之外,在 Firestore Maximum depth of subcollections: 100。因此,您可以利用这一点。
- @RenaudTarnec 在他的评论中也提到,Cloud Firestore 中的查询很浅,它们只从运行查询的集合中获取文档。无法在单个查询中从顶级集合和其他集合或子集合中获取文档。 Firestore 不支持一次性跨不同集合进行查询。单个查询只能使用单个集合中文档的属性。因此,您无法使用第一个模式获取所有问题的所有标签。
这种技术称为数据库扁平化,在 Firebase 中是一种非常常见的做法。因此,仅在需要时才使用此技术。因此,在您的情况下,如果您只需要显示单个问题的标签,请使用第一个模式。如果你想以某种方式显示所有问题的所有标签,建议使用第二种模式。
它是否只是为了在您的文档接近 1MB 限制时进行扩展?
如果您在文档中有对象的子集合,请注意子集合的大小不计入 1 MiB 限制。仅计算存储在文档属性中的数据。
2019 年 10 月 1 日编辑:
根据@ShahoodulHassan 的评论:
那么你没有办法使用第一个模式获取所有问题的所有标签吗?
其实现在有,我们可以使用Firestore collection group query获取所有问题的所有标签。需要注意的一点是,所有子集合必须具有相同的名称,例如 tags。