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进行此操作,但是嵌套会增加难度。
由于我们无法编写循环,因此如果每次写入将自己限制为一个更新的事务,那么使用嵌套的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中的值。只能与其他集合进行比较。
否则...我错过了什么吗?还有另一种方法来限制嵌套更新上的字段吗?
其他选项是:
答案 0 :(得分:2)
您没有丢失任何东西。安全规则无法实现您要尝试执行的操作。
如果您打算收集数据项,并且要引用这些数据项并使用安全规则进行保护,则它们应该是集合或子集合中的单个文档。建议不要将它们全部塞在一个文档中,也不能扩展。如果您这样做是为了节省文档读取的内容,那么您很快就会发现,在涉及安全规则和管理这些单个项目时,这种“优化”实际上不是非常有用。将数据项作为单个文档保护比在单个文档中进行管理要容易得多,更直接。
如果您真的必须将所有内容存储在一起,建议您通过一些后端来限制写访问权限,在这里您可以编写自定义逻辑,并让您的客户端在需要执行写操作时调用后端。请记住,这是不可扩展的,并且您可能遇到的最大文档大小为1MB,这比开始时要解决的问题要昂贵得多。
答案 1 :(得分:0)
对于正在寻找嵌套对象和MapDiff示例的其他人
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function affectedKeys(keys){
return request.resource.data.diff(resource.data).affectedKeys().hasOnly(keys)
}
function affectedKeysObj(obj1Key, obj2Key, keys){
return request.resource.data[obj1Key].diff(resource.data[obj2Key]).affectedKeys().hasOnly(keys)
}
match /{document=**} {
allow read, write: if false;
}
match /users/{uid}{
allow get: if request.auth.uid == uid;
allow update: if request.auth.uid == uid
&& ! affectedKeys(["meta"])
&& affectedKeys(["userData"])
&& affectedKeysObj("userData", "userData", ["bio", "displayName"]);
}
}
}
在这种情况下,我希望用户能够在["bio", "displayName"]
地图内编辑userData
,但是我也想禁止编辑meta
地图。
无论如何,道格·史蒂文森斯(Doug Stevensons)是对的,我只是补充道,这就是我将MapDiff与嵌套对象一起使用的方式。