【发布时间】:2020-05-23 20:33:39
【问题描述】:
tl;dr: 我认为Set 需要一种获取元素的方法 (set.toList()[0]),但也许我遗漏了什么!
你好!我正在使用带有大量小对象(信用卡交易)的 Firestore 开发预算应用程序。为了限制读取次数,将每个事务存储为单独的文档是没有意义的,因为用户可能一次需要大约数百个事务。
相反,我有一个容器来保存许多事务,如下所示:
/user/{user_id}/transactions/{container_id}
container: {
transactions: {
transaction_id_1: {
amount: 8.25,
note: 'chipotle lunch'
},
transaction_id_2: {
amount: 12.01
}
}
}
这很好用,但我认为安全规则不适用于写入。我想允许用户修改某些字段(note),但不允许修改其他字段(amount)。如果每个事务都是一个文档,我们可以用MapDiff 来做这件事,但是嵌套让它变得更难了。
由于我们不能编写 for 循环,如果我们将自己限制为每次写入一个更新的事务,这应该完全可以使用这样的嵌套 MapDiff:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents/{document=**} {
function allowTransactionUpdate() {
let transactionId = <transaction ID of the single transaction being updated>;
// Limit fields updated for container.
return request.resource.data.diff(resource.data).changedKeys()
.hasOnly(['transactions']) &&
// Make sure only one transaction changed.
request.resource.data.transactions.diff(resource.data.transactions)
.changedKeys().hasOnly([transactionId]) &&
// Verify the transaction already exists.
transactionId in resource.data.transactions &&
// Only allow certain fields to be updated on that transaction.
request.resource.data.transactions[transactionId]
.diff(resource.data.transactions[transactionId]).affectedKeys()
.hasOnly(['note']);
}
match /transactions/{transMonthId} {
allow update: if allowTransactionWrite();
}
allow read, write: if false;
}
}
这会很好用...如果我们可以使用 MapDiff 来获取在 container.transactions 映射中更改的事务:
let transactionId = request.resource.data.transactions
.diff(resource.data.transactions).changedKeys()[0];
关键缺失部分是最后一位:[0]。目前,Sets 没有提供获取元素的方法,这意味着将某些内容转换为 Set(以及因此使用 MapDiff 进行的任何内容)是一个死胡同:您永远无法真正知道 Set 中的值是什么。它只能与其他集合进行比较。
否则……我错过了什么吗?还有其他方法可以限制嵌套更新中的字段吗?
其他选项是:
- 使用自定义后端进行此写入,这是可行的,但很遗憾,因为 Firestore 的一大优势是后端最少 + 安全规则的强制执行。
- 将用户可编辑的属性放在一个容器文档中,将不可编辑的属性放在另一个容器文档中,但这会增加读取次数并增加客户端订阅的烦人复杂性。
- 接受这是不可能的并在每个事务中使用一个文档,这将导致 100 倍以上的读取。 ;)
【问题讨论】:
标签: google-cloud-firestore firebase-security