Cloud Firestore对子集合进行规则

时间:2017-12-14 09:07:21

标签: firebase firebase-security google-cloud-firestore

我正在开发一款具有(令人惊讶!)聊天功能的iOS应用。整个应用程序大量使用Firebase工具,对于我正在使用新的Cloud Firestore解决方案的数据库。

目前我正在使用数据库规则来加强安全性,但我在自己的数据模型上苦苦挣扎:)这可能意味着我的数据模型选择不当,但我和#39;除了实施规则部分外,我对它非常满意。

模型的对话部分看起来像这样。在我的数据库的根目录中,我有一个conversations集合:

/conversations/$conversationId
        - owner // id of the user that created the conversation
        - ts // timestamp when the conversation was created
        - members: {
                $user_id_1: true // usually the same as 'owner'
                $user_id_2: true // the other person in this conversation
                ...
          }
        - memberInfo: {
                // some extra info about user typing, names, last message etc.
                ...
          }

然后我在每个对话中都有一个名为消息的子集。消息文档是一个非常简单且只是保存有关每个已发送消息的信息。

/conversations/$conversationId/messages/$messageId
        - body
        - sender
        - ts

该模型的截图: the model

会话文档的规则相当简单易行:

match /conversations/{conversationId} {
  allow read, write: if resource.data.members[(request.auth.uid)] == true;

  match /messages/{messageId} {
        allow read, write: if get(/databases/$(database)/documents/conversations/$(conversationId)).data.members[(request.auth.uid)] == true;
  }
}

问题

我的问题在于该对话中的消息子集合。上述工作,但我不喜欢在那里使用get()调用。 每个get()来电都会执行阅读操作,因此会影响我月底的帐单,请参阅documentation

...

如果我正在构建的应用程序将成为一个成功可能会成为一个问题,文档读取的内容实际上是最小的,但每次用户打开对话时这样做似乎有点低效。我非常喜欢我的模型中的子集合解决方案,但不知道如何在这里有效地实现规则。

我打开任何数据模型更改,我的目标是在没有这些get()调用的情况下评估规则。任何想法都非常受欢迎。

2 个答案:

答案 0 :(得分:4)

老实说,我认为您的结构和get按原样调用是对的。这就是原因:

  1. 如果您在子集合中提取大量文档,Cloud Firestore通常足够智能,可根据需要缓存值。例如,如果您要求获取" conversions / chat_abc / messages"中的所有200个项目,则Cloud Firestore将仅执行该操作一次并将其重新用于整个批处理操作。所以你最终会得到201个读数,而不是400个。

  2. 作为一般理念,我不喜欢在您的安全规则中优化定价。是的,每次操作最终可能会有一两次额外的读取,但它可能不会以同样的方式给您带来麻烦,比如写一个写得不好的云函数。这些是您优化优化的领域。

答案 1 :(得分:1)

如果要保存这些额外的读取,则实际上可以基于custom claims实施“缓存”。

例如,您可以将用户有权访问的聊天记录保存在自定义声明中的“对话”对象下。请记住,自定义声明的文档中提到的限制为1000个字节。

一种解决方法是,将最新的对话仅保存在自定义声明中,例如前50个对话中。然后在安全规则中可以执行以下操作:

allow read, write: if request.auth.token.conversations[conversationId] || get(/databases/$(database)/documents/conversations/$(conversationId)).data.members[(request.auth.uid)] == true;

如果您在发布消息后已经使用云功能来审核消息,这特别好,那么您所需要做的就是更新自定义声明