Cloud Firestore 对子集合的规则
Cloud Firestore rules on subcollection
我正在开发一个 iOS 应用程序,它具有(哇哦,惊喜!)聊天功能。整个应用程序大量使用 Firebase 工具,对于数据库,我使用新的 Cloud Firestore 解决方案。
目前我正在使用数据库规则加强安全性,但我对自己的数据模型有点吃力 :) 这可能意味着我的数据模型选择不当,但我真的很满意,除了实施规则部分。
模型的对话部分如下所示。在我的数据库的根目录下,我有一个 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.
...
}
然后我在每个对话中都有一个名为 messages 的子集。消息文档非常简单,只包含有关每条已发送消息的信息。
/conversations/$conversationId/messages/$messageId
- body
- sender
- ts
以及模型截图:
对话文档中的规则相当简单易行:
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()
调用的情况下评估规则。任何想法都非常受欢迎。
老实说,我认为您对自己的结构没问题,get
按原样调用。原因如下:
如果您要获取子集合中的一堆文档,Cloud Firestore 通常足够智能,可以根据需要缓存值。例如,如果您要求获取 "conversions/chat_abc/messages" 中的所有 200 个项目,Cloud Firestore 将只执行一次该获取操作并将其重新用于整个批处理操作。所以你最终会得到 201 次读取,而不是 400 次。
一般来说,我不喜欢在您的安全规则中针对定价进行优化。是的,您最终可能会在每次操作中进行一两次额外的读取,但这可能不会像编写糟糕的 Cloud Function 那样给您带来麻烦。这些是您最好优化的领域。
如果你想保存那些额外的读取,你实际上可以在custom claims.
的基础上实现一个"cache"
例如,您可以将用户有权访问的聊天记录保存在对象 "conversations" 下的自定义声明中。请记住,自定义声明的限制为 1000 字节,如其文档中所述。
限制的一个解决方法是只保存自定义声明中的最近对话,例如前 50 个。然后在安全规则中,您可以这样做:
allow read, write: if request.auth.token.conversations[conversationId] || get(/databases/$(database)/documents/conversations/$(conversationId)).data.members[(request.auth.uid)] == true;
如果您已经在使用云功能在发布消息后审核消息,这尤其有用,您只需更新自定义声明
我正在开发一个 iOS 应用程序,它具有(哇哦,惊喜!)聊天功能。整个应用程序大量使用 Firebase 工具,对于数据库,我使用新的 Cloud Firestore 解决方案。
目前我正在使用数据库规则加强安全性,但我对自己的数据模型有点吃力 :) 这可能意味着我的数据模型选择不当,但我真的很满意,除了实施规则部分。
模型的对话部分如下所示。在我的数据库的根目录下,我有一个 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.
...
}
然后我在每个对话中都有一个名为 messages 的子集。消息文档非常简单,只包含有关每条已发送消息的信息。
/conversations/$conversationId/messages/$messageId
- body
- sender
- ts
以及模型截图:
对话文档中的规则相当简单易行:
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()
调用的情况下评估规则。任何想法都非常受欢迎。
老实说,我认为您对自己的结构没问题,get
按原样调用。原因如下:
如果您要获取子集合中的一堆文档,Cloud Firestore 通常足够智能,可以根据需要缓存值。例如,如果您要求获取 "conversions/chat_abc/messages" 中的所有 200 个项目,Cloud Firestore 将只执行一次该获取操作并将其重新用于整个批处理操作。所以你最终会得到 201 次读取,而不是 400 次。
一般来说,我不喜欢在您的安全规则中针对定价进行优化。是的,您最终可能会在每次操作中进行一两次额外的读取,但这可能不会像编写糟糕的 Cloud Function 那样给您带来麻烦。这些是您最好优化的领域。
如果你想保存那些额外的读取,你实际上可以在custom claims.
的基础上实现一个"cache"例如,您可以将用户有权访问的聊天记录保存在对象 "conversations" 下的自定义声明中。请记住,自定义声明的限制为 1000 字节,如其文档中所述。
限制的一个解决方法是只保存自定义声明中的最近对话,例如前 50 个。然后在安全规则中,您可以这样做:
allow read, write: if request.auth.token.conversations[conversationId] || get(/databases/$(database)/documents/conversations/$(conversationId)).data.members[(request.auth.uid)] == true;
如果您已经在使用云功能在发布消息后审核消息,这尤其有用,您只需更新自定义声明