Firebase 在firestore中使用子集合有什么好处吗?

Firebase 在firestore中使用子集合有什么好处吗?,firebase,google-cloud-firestore,Firebase,Google Cloud Firestore,我的应用程序的用户集合中的每个文档都有一个子集合。此子集合存储与用户相关的文档,但是它们也可以保存到主集合中,每个文档都有一个关联的userId 我选择这种结构,因为它在当时看起来是最明显的,但我可以想象,如果我需要进行数据库维护,它将使事情变得更加困难。例如,如果我想清理这些文档,我必须先查询每个用户,然后再查询每个用户的文档,而如果我有一个主集合,我可以只查询所有文档 这让我不禁要问,如果您可以将这些文档与一个ID关联起来,那么子集合的意义何在。它是否仅用于在您的文档接近1MB限制时进行扩展

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

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


这让我不禁要问,如果您可以将这些文档与一个ID关联起来,那么子集合的意义何在。它是否仅用于在您的文档接近1MB限制时进行扩展?

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

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
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
questionId
对象内的子集合。现在,让我们创建
标记
集合作为顶级集合,如下所示:

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
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
这两种方法的区别是:

  • 如果要查询数据库以获取特定问题的所有
    标记
    ,使用第一个模式非常简单,因为只需要
    集合引用
    (问题->问题ID->标记)。为了使用第二个模式实现同样的功能,需要使用
    查询
    ,而不是
    集合引用
    ,这意味着您需要查询整个
    标记
    集合,以仅获取与单个问题对应的标记
  • 使用第一个模式,一切都变得更有条理。除此之外,在Firestore。所以你可以利用这一点
  • 正如@RenaudTarnec在他的评论中提到的,CloudFireStore中的查询是肤浅的,它们只从运行查询的集合中获取文档。无法在单个查询中从顶级集合和其他集合或子集合获取文档。Firestore不支持一次查询不同集合。单个查询只能使用单个集合中文档的属性。因此,使用第一个模式无法获得所有问题的所有标记
这种技术称为数据库扁平化,在Firebase中是一种非常常见的做法。因此,只有在需要时才使用此技术。因此,在您的情况下,如果只需要显示单个问题的标记,请使用第一个模式。如果您想以某种方式显示所有问题的所有标记,建议使用第二种模式

是否只有这样,当您的文档接近1MB限制时,您才能进行扩展

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

编辑2019年10月1日:

根据@ShahoodulHassan的评论:

所以你不可能用第一个模式得到所有问题的所有标签

事实上,现在有了,我们可以通过使用获得所有问题的所有标签。需要注意的一点是,所有子集合都必须具有相同的名称,例如
标记

我能想到的子集合的唯一潜在技术优势与文档大小有关——它允许您省略对父元素的引用。(即,对于每个相关的
rootDocument->subCollection
,都有一个从根集合文档到子集合的指针)

次收集 当
标记
是子集合时,它们不需要保存
消息ID
,因为您可以通过消息文档访问标记:

collection("messages").document(messageId).collection("tags")
根集合 对于根
标记
集合,每个标记都需要存储
messageId
。这基本上改变了指针的方向

collection("tags").whereEqualTo("messageId", messageId)
总结 子集合:指针从
一个->多个


根集合:指针来自
多个->一个

这一点以前没有提到过,但子集合(在某些情况下)可以帮助绕过orderBy限制:

不能按等式(=)或in子句中包含的字段对查询排序

假设您希望获得用户最近10次登录的

顶级:

//We can't use .orderBy after .where('==')
USER_LOGINS.where('userId', '==', {uid}).limit(10) 
子集合:

//With a subcollection we can order and limit properly
USERS.doc({uid}).collection('LOGINS').orderBy('unixCreated', 'desc').limit(10);

将这些文档保存在子集合(而不是主集合)下的一个可能的缺点是,您无法跨多个子集合进行查询。对,这就是我所说的。我没有考虑转移到子集合,我正在考虑离开它们。我想知道是否有一个很好的理由首先使用子集合,所以本质上可以归结为请求子集合更快,而查询主集合更快flexible@HamishJohnson同样的速度。你想怎么做完全取决于你自己。我更喜欢第二种选择,以防以后我想在其他东西上使用标签。但是,它确实增加了定价。@Alex Mamo
因此,您无法使用第一个架构获取所有问题的所有标记。
对名为“标记”的集合进行集合组查询不会现在解决此问题吗?@ShahoodulHassan作为一个影响因素,使用。请注意,所有子集合都应具有相同的名称。我会更新我的答案。@MobileMon不,费用没有差别。您只需支付从查询中收到的单据。如果子集合组查询返回的文档数量与主集合相同,则成本相同。