Firestore 聊天应用程序:这是多收件人消息的有效文档结构吗?

Posted

技术标签:

【中文标题】Firestore 聊天应用程序:这是多收件人消息的有效文档结构吗?【英文标题】:Firestore chat-app: Is this a valid document structure for multi-recipient messages? 【发布时间】:2019-06-19 13:26:21 【问题描述】:

假设一个聊天应用有 1000 万 Firebase 用户和数亿条消息。

我有一个 Firestore 集合,其中包含按时间序列表示为文档的消息,并且这些消息中的每一个都可能被多达 100 个这些用户接收和查看。请注意,这些用户并没有组织在稳定的组中,因为每条消息可能有一组完全不同的用户接收它。

我需要能够非常有效地(在时间和成本方面)找到, 某个特定时间后的所有消息,定向到某个特定用户。

我的第一次失败尝试是在 recipients 数组字段中列出收件人用户,例如:

sender: user3567381
dateTime : 2019-01-24T20:37:28Z
recipients : [user1033029, user9273842, user8293413, user6273581]

但是,这不会让我有效地进行查询。

作为second failed attempt,由于 Firestore 是无模式的,我想过让每个用户成为一个字段,如下所示:

sender: user3567381
dateTime : 2019-01-24T20:37:28Z
user1033029 : true
user9273842 : true
user8293413 : true
user6273581 : true

然后,例如,如果我想知道今天下午 3:00 之后用户 8293413 的所有消息,我可以这样做:

messages.where("user8293413", "==", true).where("dateTime", ">=", "2019-01-24T15:00:00Z")

这是一个复合索引查询,每个用户需要一个索引。不幸的是,每个数据库有 200 composite-indexes 的限制。

为了解决这个问题,我当前的尝试是将日期转换为用户字段的值,如下所示:

sender: user3567381
dateTime : 2019-01-24T20:37:28Z
user1033029 : 2019-01-24T20:37:28Z
user9273842 : 2019-01-24T20:37:28Z
user8293413 : 2019-01-24T20:37:28Z
user6273581 : 2019-01-24T20:37:28Z

现在,如果我想知道今天下午 3:00 之后用户 8293413 的所有消息,我可以这样做:

messages.where("user8293413", ">=", "2019-01-24T15:00:00Z")

请注意,这现在是一个单字段索引

从文档中我知道 Firestore 将为所有字段创建单字段索引,因此这意味着它将专门为 user8293413 创建索引。 这意味着搜索会很快,对吧?并且阅读次数将保持在最低限度(每条消息阅读一次)。

但是,由于我有 1000 万用户,Firestore 将不得不为整个数据库创建 1000 万个单字段索引(假设所有用户都收到消息)。

来自documentation Firestore 有以下限制:

数据库的最大复合索引数:200 数据库的最大单字段索引豁免数:200 每个文档的最大索引条目数:40,000(一个文档的索引条目数是以下各项之和:单字段索引条目数+复合索引数条目) 索引条目的最大大小:7.5 KiB 文档索引条目大小的最大总和:8 MiB(总大小是文档以下各项的总和:文档的单字段索引条目大小的总和+ 文档的复合索引条目大小的总和) 索引字段值的最大大小:1500 字节(超过 1500 字节的字段值将被截断。涉及截断字段值的查询可能会返回不一致的结果。)

通过阅读以上内容,这些引起了我的注意:

每个文档的最大索引条目数:40,000 文档索引条目大小的最大总和:8 MiB

但是,他们声明限制是针对每个文档,而不是针对每个数据库。而且我只有数百万个数据库索引,而不是每个文档。

这是个问题吗?这么多索引会影响性能吗?所有这些索引的存储成本如何? Firebase 是否为每个数据库的大量索引做好了准备?

【问题讨论】:

【参考方案1】:

尽管几个月后,对于任何未来的用户来说,第一次尝试似乎效果最好。

时间戳使用单个静态字段,对收件人使用单个静态字段意味着索引将保持可忽略不计,您不必考虑它们。

查找用户的所有消息,这似乎是您的目标:

例如,如果我想知道用户 8293413 之后的所有消息 今天下午 3:00,我可以这样做:

这在伪代码中看起来就像这样:

firestore.collection('messages').where('recipient', 'array_contains', userId).where('time', '>', '3pm today'.get()

这在性能上应该很容易,Firebase 已针对其提供的运算符进行了优化,例如'==', '>=', 'array_contains'

【讨论】:

为什么这个 array_contains 方法比索引方法更有效?特定索引中的搜索数据性能较差?如果您使用 array_contains 方法,您可能会受到 Firebase 规则的限制。目前,你不能使用你不能使用 arrayContains 作为规则。 ***.com/questions/32108880/…***.com/questions/30160086/… 这篇文章不要求遵守规则。您想制作并注意您不需要的索引吗?数组包含方法非常适合他的用例。 array_contains 方法效果很好,但是就本例的性能而言,array_contains 方法比索引更高效?是的,你是对的,问题不在于规则。但我认为,如果您想使用 Firebase 规则,那么考虑对 array_contains 方法极为重要。

以上是关于Firestore 聊天应用程序:这是多收件人消息的有效文档结构吗?的主要内容,如果未能解决你的问题,请参考以下文章

当聊天线程有未读消息时如何显示徽章React JS,Firestore

如何在 android studio 的聊天应用程序中显示来自接收者的消息?

Firestore Streams 订阅

Firestore如何在回收站视图中从firestore检索数据

收到消息时将徽章添加到聊天按钮

MYSQL在一个语句中多对多选择(聊天,最新消息,用户名)