作为索引的快速概述,有两种类型 - 复合索引(用于将多个字段连接在一起)或单字段索引。默认情况下,对于普通集合查询,会为文档的每个字段自动创建单字段索引,而对于集合组查询,则会禁用单字段索引。如果字段的值是一个数组,则该数组的值将被添加到 Array Contains 索引中。如果字段的值是原始值(字符串、数字、时间戳等),则文档将添加到该字段的 Ascending 和 Descending 索引中。
要启用对集合组查询的access 字段的查询,您必须创建索引。虽然您可以单击查询时收到的错误消息中的链接,但这只会为您正在查询的用户添加索引(例如access.alice、access.bob 等)。相反,您应该使用 Firebase Console 告诉 Cloud Firestore 为 access 创建索引(这将为它的每个子项创建索引)。打开控制台后,使用“添加豁免”按钮(考虑使用“豁免”来表示“覆盖默认设置”)定义新的索引规则:
在第一个对话框中,使用以下设置:
Collection ID: "documents"
Field Path: "access"
Collection: Checked ✔
Collection group: Checked ✔
在第二个对话框中,使用以下设置:
|
Collection Scope |
Collection Group Scope |
| Ascending |
Enabled |
Enabled |
| Descending |
Enabled |
Enabled |
| Array Contains |
Enabled |
Enabled |
在您的安全规则中,您还应该在进行相等之前检查目标用户是否在access 映射中。访问缺少的属性时会抛出“对象上的属性未定义”。拒绝访问的错误,如果您稍后将语句与|| 组合在一起,这将成为一个问题。
要解决此问题,您可以使用:
match /{path=**}/documents/{documentId} {
allow list, get: if request.auth.uid in resource.data.access
&& resource.data.access[request.auth.uid] == true;
}
或在未找到所需键时提供备用值:
match /{path=**}/documents/{documentId} {
allow list, get: if resource.data.access.get(request.auth.uid, false) == true;
}
作为违反规则的示例,假设您希望“员工”用户能够阅读文档,即使他们不在该文档的 access 映射中。
这些规则总是会出错并失败(如果工作人员的用户 ID 不在 access 映射中):
match /{path=**}/documents/{documentId} {
allow list, get: if resource.data.access[request.auth.uid] == true
|| get(/databases/$(database)/documents/users/$(request.auth.uid)).data.get("staff", false) == true;
}
然而,这些规则会起作用:
match /{path=**}/documents/{documentId} {
allow list, get: if resource.data.access.get(request.auth.uid, false) == true
|| get(/databases/$(database)/documents/users/$(request.auth.uid)).data.get("staff", false) == true;
}
附带说明,除非您需要查询用户无权访问的文档,否则从访问映射中简单地省略该用户会更简单。
{
title: "A Great Story",
content: "Once upon a time ...",
access: {
alice: true,
david: true,
// ...
}
}