在Firestore中使用子集合有什么好处?

Ham*_*son 5 firebase google-cloud-firestore

我的应用程序用户集合中的每个文档都有一个子集合。该子集合存储与用户相关的文档,但是也可以将它们保存到主集合中,每个文档都具有关联的userId。

我选择了这种结构,因为它在当时似乎最为明显,但是我可以想象,如果我需要进行数据库维护,它将使事情变得更加艰难。例如,如果要清理这些文档,则必须先查询每个用户,然后再查询每个用户的文档,而如果我拥有主集合,则可以查询所有文档。

That lead me to question what is the point of subcollections at all, if you can just associate those docs with an ID. Is it solely there so that you can expand if your doc becomes close to the 1MB limit?

Ale*_*amo 7

让我们举个例子。假设我们有一个测验应用程序的数据库架构,如下所示:

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
Run Code Online (Sandbox Code Playgroud)

其中tagsquestionId对象内的子集合。现在让我们将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
Run Code Online (Sandbox Code Playgroud)

这两种方法之间的差异是:

  • 如果要查询数据库以获取所有tags特定问题,则使用第一个模式非常简单,因为只CollectionReference需要一个(问题-> questionId->标记)。要使用第二个模式实现相同的目的,而不是a CollectionReferenceQuery则需要a,这意味着您需要查询整个tags集合以仅获取与单个问题对应的标签。
  • 使用第一个模式,一切都将井井有条。除此之外,在Firestore 中子集合的最大深度:100。因此,您可以利用它。
  • 正如@RenaudTarnec在他的评论中提到的那样,Cloud Firestore中的查询很浅,它们仅从运行查询的集合中获取文档。无法通过单个查询从顶级集合以及其他集合或子集合中获取文档。Firestore不一次性支持跨不同集合的查询。单个查询只能使用单个集合中的文档属性。因此,您无法使用第一个架构来获取所有问题的所有标签。

这项技术称为数据库扁平化,是Firebase的一种非常普遍的做法。因此,仅在需要时才使用此技术。因此,在您的情况下,如果只需要显示单个问题的标签,请使用第一个模式。如果您想以某种方式显示所有问题的所有标签,则建议使用第二种模式。

它是否仅存在于其中,以便您的文档接近1MB的限制时可以扩展?

如果文档中有对象的子集合,请注意,该子集合的大小不计入该1 MiB限制。仅计算存储在文档属性中的数据。

编辑2019年10月1日:

根据@ShahoodulHassan的评论:

因此,您无法使用第一个模式获取所有问题的所有标签吗?

实际上,现在有了Firestore集合组查询,我们可以获得所有问题的所有标签。要注意的一件事是,例如,所有子团队必须具有相同的名称tags

  • @Alex Mamo`因此,您无法使用第一个模式获取所有问题的所有标签。`现在对名为“标签”的集合进行集合组查询是否可以解决此问题? (3认同)
  • @MobileMon 不,成本没有区别。您只需为从查询中收到的文档付费。如果子集合组查询返回与主​​集合相同数量的文档,则其成本相同。 (2认同)

geg*_*geg 7

我发现子集合的最大优点是它们有自己的写入速率限制,因为每个子集合都有自己的索引(假设您没有集合索引)。对于小型应用程序来说,这可能不是问题,但对于中/大型应用程序来说,这可能非常重要。

想象一个聊天应用程序,其中每个聊天都有一系列消息。您需要按时间戳对消息进行索引,以便按时间顺序显示它们。Firestore 的顺序值写入限制为 500/秒,这对于中型应用程序来说绝对是可以承受的(特别是如果您考虑到流氓用户编写消息脚本的可能性——目前使用安全规则不容易阻止这种情况)

// root collection

/messages {
  chatId: string
  timeSent: timestamp // the entire app would be limited to 500/second
}
Run Code Online (Sandbox Code Playgroud)
// sub-collection

/chat/{chatId}/messages {
  timeSent: timestamp // each chat could safely write up to 500/second
}
Run Code Online (Sandbox Code Playgroud)