我为我的应用的用户集合中的每个文档都有一个子集合。此子集合存储与用户相关的文档,但是它们也可以保存到主集合中,每个文档都有一个关联的 userId。
我选择了这种结构,因为它在当时似乎是最明显的,但我可以想象,如果我需要进行数据库维护,它将使事情变得更加困难。例如,如果我想清理这些文档,我必须先查询每个用户,然后再查询每个用户的文档,而如果我有一个主集合,我可以只查询所有文档。
这让我不禁要问,如果可以将这些文档与一个ID关联起来,那么子集合到底有什么意义呢?如果你的文档接近1MB的限制,你可以扩展它吗?
很惊讶这之前没有提到过,但子集合可以(在某些情况下)帮助绕过订单通过限制:
不能按等式 (==) 或子句中包含的字段对查询进行排序。
假设您要获取用户最近 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);
我发现子集合的一个最大优点是它们有自己的写速率限制,因为每个子集合都有自己的索引(假设没有集合组索引)。对于小型应用程序来说,这可能不是一个问题,但是对于中型/大型应用程序来说,这可能非常重要。
想象一个聊天应用程序,其中每个聊天都有一系列消息。您需要按时间戳为邮件编制索引,以便按时间顺序显示邮件。顺序值的 Firestore 写入限制为 500/秒,这绝对是中型应用所能达到的(特别是如果您考虑恶意用户编写脚本消息的可能性 - 目前使用安全规则不容易防止)
// root collection
/messages {
chatId: string
timeSent: timestamp // the entire app would be limited to 500/second
}
// sub-collection
/chat/{chatId}/messages {
timeSent: timestamp // each chat could safely write up to 500/second
}
编辑: 2021年10月29日:
要明确文档中存在的以下句子:
如果您不根据具有顺序值的字段进行查询。
时间戳不能被视为连续的。但是,它仍然可以被认为是连续的。同样的规则也适用于按字母顺序(Customer1,Customer2,Customer3,...),或者几乎所有可以被视为可预测生成价值的东西。
Firestore索引中的这种顺序数据最有可能被写入存储介质上的物理邻近区域,因此存在这种限制。
话虽如此,请注意Firestore使用一种机制将文档映射到相应的位置。这意味着,如果值不是随机分布的,则写入操作将不会正确分布在各个位置上。这就是这种限制存在的原因。
另请注意,您在特定时间内可以向此类位置写入多少数据是有物理限制的。可预测的键/值很可能最终会出现在同一个位置,这实际上是不好的。因此需要进行更多更改才能达到限制。
编辑:2021年7月16日:
由于这个答案听起来有点老套,我将尝试添加使用随时间推移发现的子集合的更多优点:
就目前而言,如果我发现了其他好处,我会更新答案。
让我们举一个例子。让我们假设我们有一个测验应用程序的数据库模式,如下所示:
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
其中< code>tags是< code>questionId对象中的子集合。现在,让我们将< code>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
这两种方法之间的区别是:
标记
,使用第一个模式非常简单,因为只需要集合引用
(问题-这种技术称为数据库扁平化,在Firebase中是一种非常常见的做法。因此,仅在需要时使用此技术。因此,在您的情况下,如果您只需要显示单个问题的标签,请使用第一个模式。如果您想以某种方式显示所有问题的所有标签,建议使用第二个模式。
如果你的文档接近1MB的限制,你可以扩展它吗?
如果文档中有对象的子集合,请注意,该子集合的大小不计入1 MiB限制。仅计算存储在文档属性中的数据。
编辑2019年10月1日:
根据@ShahoodulHassan的评论:
因此,您无法使用第一个架构获取所有问题的所有标记?
实际上,现在有了,我们可以使用Firestore集合组查询获得所有问题的所有标签。需要注意的一点是,所有子集合必须具有相同的名称,例如标记
。
问题内容: 我的应用程序中有一些内存泄漏。它们都起源于一个特定的视图集群,我花了大量时间进行调整,并尝试减少尽可能多的上下文传递。这使我相信群集中使用的位图就是问题所在。因此,我考虑将WeakReferences用于所有对视图使用的位图的引用。我从未使用过WeakReference,并且不确定这是否是一个好的应用程序。任何机构都可以提供有用的指示或技巧吗? 问题答案: 因此,我考虑将WeakRef
我正在尝试为firestore中的社交媒体应用程序组织数据。为帖子创建一个新集合或将其放入用户的子集合更好吗? 深度应该是一样的,但是一种方式比另一种方式有什么优势吗? 创建新集合: 职位(集合) 用户(集合) 用户中的子集合: 用户(集合)
本文向大家介绍使用黄瓜有什么好处?,包括了使用黄瓜有什么好处?的使用技巧和注意事项,需要的朋友参考一下 下面列出了使用黄瓜的一些优点- Cucumber是一种开源工具,不需要许可。 黄瓜可以通过Eclipse等IDE轻松配置。 黄瓜弥合了开发人员,测试人员,业务分析师,客户和产品所有者之间的理解和沟通差距。 黄瓜使没有技术知识的业务利益相关者参与其中。 黄瓜提供纯文本表示形式,使团队中的非技术人员
问题内容: 我正在浏览以下代码示例: 我想知道使用Iterator接口的实现而不是使用普通的for-each循环有什么好处? 如果此示例不相关,那么当我们应该使用Iterator时会是一个好的情况吗? 谢谢。 问题答案: 该 for-each循环 与Java 5中引入的,所以它不是那么“老”。 如果只想迭代一个集合,则应为每个循环使用 但是有时“普通的” 迭代器的 方法对于检查 迭代器 是否还有更
问题内容: 我只是想知道什么是确定的利益/用途 用于在数据类型? 问题答案: 选择带有类型的列时,它将用零填充字段的显示值,直到字段定义中指定的显示宽度。大于显示宽度的值不会被截断。请注意,的使用还意味着。 使用和显示宽度不会影响数据的存储方式。它仅影响其显示方式。 以下是一些示例SQL演示了如何使用: 结果:
我的FiRecovery数据库中有一个名为Reports的集合,我已经在其中添加了文档。但我现在的问题是我想在报表中添加一个带有子集合的文档,有什么想法吗?