当前位置: 首页 > 知识库问答 >
问题:

如何强制FiRecovery客户端应用程序为集合维护正确的文档计数?

夏长卿
2023-03-14

Firestore无法查询集合的大小,因此如果客户端应用程序需要知道这个值,它需要对另一个集合中的文档进行一些维护来保存这个计数。但是,它要求客户端正确执行事务,以便在添加和删除文档时保持该计数最新。恶意的或损坏的客户端可以独立地修改集合或计数,并造成计数不准确的情况。

可以通过后端强制客户端执行此操作,或者使用云函数触发器自动维护计数(延迟一段时间后生效)。然而,我不想引入后端,我宁愿使用安全规则。我该怎么做?

共有2个答案

颛孙铭
2023-03-14

建议的解决方案仍然会失败,因为您只需向批处理写入添加其他文档即可。

但是,您可以将以下内容添加到邮件收集规则中:

<代码>

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /messages/{id} {
      allow read;
      allow create: if
        getAfter(/databases/$(database)/documents/messages-stats/data).data.count ==
             get(/databases/$(database)/documents/messages-stats/data).data.count + 1 
             && get(/databases/$(database)/documents/messages-stats/data).data.messageId == request.resource.id;
      allow delete: if
        getAfter(/databases/$(database)/documents/messages-stats/data).data.count ==
             get(/databases/$(database)/documents/messages-stats/data).data.count - 1
             && get(/databases/$(database)/documents/messages-stats/data).data.messageId == request.resource.id;
    }

    match /messages-stats/data {
      allow read;
      allow update: if (
        request.resource.data.count == resource.data.count + 1 &&
        existsAfter(/databases/$(database)/documents/messages/$(request.resource.data.messageId)) &&
           ! exists(/databases/$(database)/documents/messages/$(request.resource.data.messageId))
      ) || (
        request.resource.data.count == resource.data.count - 1 &&
        ! existsAfter(/databases/$(database)/documents/messages/$(request.resource.data.messageId)) &&
               exists(/databases/$(database)/documents/messages/$(request.resource.data.messageId))
      );
    }

  }
}
裘光启
2023-03-14

假设您有一个“消息”集合,其中包含客户端可以添加和删除的消息。还可以想象另一个集合中的文档,路径为“messages stats/data”,其中有一个名为“count”的字段,用于维护消息中文档的准确计数。如果客户端应用程序执行如下事务以添加文档:

async function addDocumentTransaction() {
    try {
        const ref = firestore.collection("messages").doc()
        const statsRef = firestore.collection("messages-stats").doc("data")
        await firestore.runTransaction(transaction => {
            transaction.set(ref, {
                foo: "bar"
            })
            transaction.update(statsRef, {
                count: firebase.firestore.FieldValue.increment(1),
                messageId: ref.id
            })
            return Promise.resolve()
        })
        console.log(`Added message ${ref.id}`)
    }
    catch (error) {
        console.error(error)
    }
}

或者像这样的一批:

async function addDocumentBatch() {
    try {
        const batch = firestore.batch()
        const ref = firestore.collection("messages").doc()
        const statsRef = firestore.collection("messages-stats").doc("data")
        batch.set(ref, {
            foo: "bar"
        })
        batch.update(statsRef, {
            count: firebase.firestore.FieldValue.increment(1),
            messageId: ref.id
        })
        await batch.commit()
        console.log(`Added message ${ref.id}`)
    }
    catch (error) {
        console.error(error)
    }
}

像这样使用事务删除文档:

async function deleteDocumentTransaction(id) {
    try {
        const ref = firestore.collection("messages").doc(id)
        const statsRef = firestore.collection("messages-stats").doc("data")
        await firestore.runTransaction(transaction => {
            transaction.delete(ref)
            transaction.update(statsRef, {
                count: firebase.firestore.FieldValue.increment(-1),
                messageId: ref.id
            })
            return Promise.resolve()
        })
        console.log(`Deleted message ${ref.id}`)
    }
    catch (error) {
        console.error(error)
    }
}

或类似于批次:

async function deleteDocumentBatch(id) {
    try {
        const batch = firestore.batch()
        const ref = firestore.collection("messages").doc(id)
        const statsRef = firestore.collection("messages-stats").doc("data")
        batch.delete(ref)
        batch.update(statsRef, {
            count: firebase.firestore.FieldValue.increment(-1),
            messageId: ref.id
        })
        await batch.commit()
        console.log(`Deleted message ${ref.id}`)
    }
    catch (error) {
        console.error(error)
    }
}

然后,您可以使用安全规则要求要添加或删除的文档只能与具有 count 字段的文档同时更改。微:

rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    match /messages/{id} {
      allow read;
      allow create: if
        getAfter(/databases/$(database)/documents/messages-stats/data).data.count ==
             get(/databases/$(database)/documents/messages-stats/data).data.count + 1;
      allow delete: if
        getAfter(/databases/$(database)/documents/messages-stats/data).data.count ==
             get(/databases/$(database)/documents/messages-stats/data).data.count - 1;
    }

    match /messages-stats/data {
      allow read;
      allow update: if (
        request.resource.data.count == resource.data.count + 1 &&
        existsAfter(/databases/$(database)/documents/messages/$(request.resource.data.messageId)) &&
           ! exists(/databases/$(database)/documents/messages/$(request.resource.data.messageId))
      ) || (
        request.resource.data.count == resource.data.count - 1 &&
        ! existsAfter(/databases/$(database)/documents/messages/$(request.resource.data.messageId)) &&
               exists(/databases/$(database)/documents/messages/$(request.resource.data.messageId))
      );
    }

  }
}

请注意,客户必须:

    < li >增加或减少< code >/messages-stats/data 中的计数。 < li >必须在名为< code>messageId的字段中提供要在“数据”文档中添加或删除的文档的id。 < li >递增计数要求< code>messageId中标识的新文档在批处理/事务提交之前不存在,在事务之后存在。 < li >减少计数要求< code>messageId中标识的旧文档必须在批处理/事务提交之前存在,并且在事务之后不能存在。

请注意,existsAfter()在事务完成后检查命名文档的状态,而exists()在事务结束前检查该状态。这两个函数之间的差异对于这些规则的工作方式很重要。

另请注意,这在重载下不会很好地扩展。如果文档的添加和删除速度超过每秒10,则数据文档的每个文档写入速率将被超过,并且事务将失败。

一旦你有了这个,现在你实际上可以编写安全规则来限制集合的大小,如下所示:

match /messages/{id} {
  allow create: if
    get(/databases/$(database)/documents/messages-stats/data).data.count < 5;
}
 类似资料:
  • 我被android firstore文档和子集合弄糊涂了。 此示例将生成以下数据结构: 代码如下: 子集合和文档节点交替显示:a、c与b、d。 但是如何在没有d节点的情况下实现类似的数据结构(如下所示): 自。add方法仅适用于document而不适用于collection,我们不能只删除d(document),因为c(collection)没有add方法。 我应该如何修改我的代码?

  • 我需要创建一个fiRecovery文档,它也有一个集合,理想情况下是在单个写入操作中。 我在文档中没有看到类似的内容,因此,如果失败了,那么关于获取创建的文档id然后将多个文档添加到集合中有什么提示吗? 编辑:我正在用typescript/js开发

  • 我们正在使用谷歌的Firestore获取嵌入式机器配置数据。因为这些数据控制着一个可配置的页面流和许多其他东西,所以它被分割成许多子集合。在这个系统中,每台机器都有自己的顶级文档。然而,当我们向机队中添加机器时,需要花费很长时间,因为我们必须手动复制多个文档中的所有数据。有人知道如何在Python中递归复制Firestore文档、所有的子集合、它们的文档、子集合等吗。您将有一个顶级文档的引用,以及

  • 问题内容: 我们目前正处于非公开Beta测试阶段,因此仍在进行相当快速的更改,尽管显然随着使用量的增加,我们将放慢这个过程。话虽这么说,我们遇到的一个问题是,在我们推出新JavaScript文件的更新后,客户端浏览器仍然使用文件的缓存版本,而他们看不到更新。显然,在技术支持电话上,我们可以简单地通知他们进行刷新以确保他们从服务器获取最新文件,但是最好在此之前进行处理。 我们当前的想法是将版本号简单

  • 在Firestore中,如何获取集合中的文档总数? 例如,如果我有 我想查询我有多少人,得到2。 我可以对 /people进行查询,然后获取返回结果的长度,但这似乎是一种浪费,特别是因为我将在更大的数据集上进行此操作。

  • 我是Android编程新手。 我想了解如何为特定的Android应用程序维护活动堆栈,以及它如何根据用户导航进行更改。 例如,如果有多个活动,那么当用户单击“后退”按钮或“主页”按钮或启动新活动时,活动堆栈的行为如何? 我试图找到一个合适的职位,我可以得到所有的信息,但我没有得到任何。有人能建议我一些链接/帖子,我可以学习这个吗? 编辑: 到目前为止我遇到的链接/帖子: > onSaveInsta