You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Firestore规则通配符能否支持列表查询与快照监听器?

Firestore安全规则:Get请求正常但List请求失败问题

数据库结构

/parent_collection
   /parent_doc
      ...data,
      [status]: Enum
      /sub_collection1
         /sub_doc1
            ...data
         /sub_doc2
            ...data
      /sub_collection2
         /sub_doc1
            ...data
         /sub_doc2
            ...data

当前安全规则

function isPublished(docId) {
   let data = get(/databases/$(database)/documents/parent_collection/$(docId)).data;
   return 'status' in data && data.status == "published";
}

match /parent_collection/{docId}/{document=**} {
   // Deny all requests to create, update, or delete the doc
   allow create, update, delete: if false

   // Allow the requestor to read the doc if published
   allow read: if isPublished(docId)
}

客户端查询代码

可正常通过规则的Get请求

const getDocById: GetDocById = (docId) => {
   return new Promise((resolve, reject) => {
      getDoc(doc(firestore, "parent_collection", docId))
         .then((doc) => {
            resolve(doc.data());
         })
         .catch((error) => reject(error));
   });
};

被规则拒绝的集合List请求

const getDocsWithCursor: GetDocsWithCursor = (cursor) => {
   const docs: Doc[] = [];
   return new Promise((resolve, reject) => {
      let q = query(
         collection(firestore, "parent_collection"),
         where("status", "==", "published"),
         orderBy("updated")
      );

   if (cursor) q = query(q, startAfter(cursor));

   getDocs(q)
      .then((snapshot) => {
         snapshot.forEach((doc) => {
            docs.push(doc.data());
         });

         return resolve([docs, snapshot.docs[snapshot.docs.length - 1]]);
      })
      .catch((error) => {
         return reject(error);
      });
   });
};

被规则拒绝的子集合快照监听器

const attachCollectionListener: AttachCollectionListener = (docId, callback) => {
   return onSnapshot(
      query(
         collection(
            firestore,
            "parent_collection",
            docId,
            "sub_collection1"
         ),
         orderBy("order")
      ),
      (snapshot) => {
         snapshot.docChanges().forEach((change) => {
            callback(
               change.type,
               change.doc.data(),
               change.newIndex
            );
         });
      }
   );
};

已排查的失败原因

  • 集合级List查询未指定具体文档ID,规则中的通配符{docId}无法获取有效值,导致isPublished(docId)函数无法执行;而Get请求因明确指定文档ID,通配符能正常取值。
  • Firestore安全规则对List查询采用批量校验,不会逐个检查文档权限,要求查询条件必须与规则限制完全匹配,否则整个查询会被拒绝。

问题解答

1. 依赖通配符的规则能否支持集合List查询?

不能直接用原规则实现。集合List查询针对整个parent_collection发起,规则中的{docId}通配符无对应值,isPublished(docId)无法通过get()获取具体文档状态。

可通过调整规则,让集合List查询的校验逻辑与客户端查询条件对齐:

// 单独定义父集合规则
match /parent_collection/{docId} {
  allow create, update, delete: if false;
  // 单个文档Get请求校验
  allow get: if resource.data.status == "published";
  // 集合List请求校验:要求查询必须包含status等于published的条件
  allow list: if request.query.where("status", "==", "published");
}

// 子集合规则保持原逻辑
match /parent_collection/{docId}/{document=**} {
  allow create, update, delete: if false;
  allow read: if get(/databases/$(database)/documents/parent_collection/$(docId)).data.status == "published";
}

客户端的getDocsWithCursor查询已包含where("status", "==", "published")条件,调整后即可通过规则校验。

2. 调整规则支持子集合快照监听器

可以实现。子集合监听器属于List类型请求,但路径中的{docId}由客户端传入、取值明确,规则可正常通过get()获取父文档状态。

使用以下优化后的规则即可:

match /parent_collection/{docId} {
  allow create, update, delete: if false;
  allow get: if resource.data.status == "published";
  allow list: if request.query.where("status", "==", "published");
}

// 子集合及以下的规则
match /parent_collection/{docId}/{subColl}/{subDoc=**} {
  allow create, update, delete: if false;
  // 直接校验父文档的published状态
  allow read: get(/databases/$(database)/documents/parent_collection/$(docId)).data.status == "published";
}

子集合的所有读请求(包括快照监听器)都会校验对应父文档的status是否为published,只要父文档符合条件,监听器就能正常工作,实现实时同步子集合内容。


内容的提问来源于stack exchange,提问作者Bernard

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 19:47:57