【问题标题】:Querying multiple collections of firestore using array-contains-any使用 array-contains-any 查询多个 firestore 集合
【发布时间】:2021-04-29 03:10:19
【问题描述】:

我正在尝试使用array-contains-any 查询不同集合中的多个文档,方法是使用Promise.all() 一次获取所有文档。

我检查了路径是否正确,文档是否存在,key是否存在于文档中,一切正常。

querySnapshotssnapshots 没有检索到数据。在日志的某处说:"_size":0,"_materializedDocs":null

  let promises = []
  depIds.forEach(id => {
    const prodIds = groupedProducts[id].reduce((acc, val) => [...acc, val.prodId], []);
    console.log("All prodIds: ", prodIds, "; also id is: ", id);
    promise = admin.firestore()
      .collection('Products')
      .doc('Departments')
      .collection(id)
      .where('key', 'array-contains-any', prodIds)
      .get();
    promises.push(promise)

  })

  const querySnapshots = await Promise.all(promises);
  const snapshots = querySnapshots.map(doc => {
    console.log("docs: ", JSON.stringify(doc))
    return doc;
  });

所以我的问题是:

  1. 可以如上查询吗?

  2. Promise.all()命令后如何获取实际数据?

感谢您的帮助!

【问题讨论】:

    标签: javascript firebase google-cloud-firestore google-cloud-functions


    【解决方案1】:

    如果文档中的 key 字段是字符串,则应使用 in operator

    array-contains-any operator 检查您给定的任何值是否在命名字段的 数组 中。由于key 是一个字符串,所以这个操作符永远不会返回任何结果。

    要获取 key 与给定 ID 匹配的所有文档,同时还要确保一次可以获取超过 10 个文档,您可以使用:

    /** splits array `arr` into chunks of max size `n` */
    function chunkArr(arr, n) {
      if (n <= 0) throw new Error("n must be greater than 0");
      return Array
        .from({length: Math.ceil(arr.length/n)})
        .map((_, i) => arr.slice(n*i, n*(i+1)))
    }
    
    /** Fetch all given product IDs (if they exist) for the given department */
    fetchDepartmentProducts(depId, prodIdList) {
      const prodIdListInBatches = chunkArr(prodIdList, 10);
      const departmentCollectionRef = admin.firestore()
        .collection('Products')
        .doc('Departments')
        .collection(depId);
      
      const promises = prodIdListInBatches.map((prodIdListBatch) => {
        return departmentCollectionRef
          .where('key', 'in', prodIdListBatch)
          .get();
      });
      
      return Promise.all(promises) // waits for all get requests
        .then((allQuerySnapshots) => {
          // flatten the found documents of the query snapshots into one array
          const allDocSnapshots = [];
          allQuerySnapshots.forEach((querySnapshot) =>
            allFoundDocSnapshots.push(...querySnapshot.docs)
          );
          return allDocSnapshots;
        });
    }
    

    将其应用到您的代码中,得到:

    const promises = depIds.map((id) => {
      const prodIds = groupedProducts[id].map((product) => product.prodId);
      return fetchDepartmentProducts(id, prodIds);
    }
    
    const productsByDepartment = await Promise.all(promises);
    
    productsByDepartment.forEach((docsInDeparment, i) => {
      console.log(`Found ${docsInDeparment.length} products in department #${depId[i]}.`);
    });
    

    【讨论】:

    • 非常感谢!有效!将数组拆分为 10 个块,效果完全不同。
    【解决方案2】:

    是否可以如上查询

    是的,这是完全有可能的,因为 get() 方法返回一个 Promise,该 Promise 将通过 Query 的结果来解决(即 QuerySnapshot

    Promise.all() 命令后如何获取实际数据?

    await Promise.all(promises); 返回单个 Promise,它“解析为输入 Promise 的结果数组”(MDN source),即在您的情况下为 QuerySnapshots 数组。

    因此,要读取结果,您需要在数组上循环并在QuerySnapshot 上循环每个元素。例如如下:

      const promises = [];
      depIds.forEach((id) => {
        const prodIds = groupedProducts[id].reduce(
          (acc, val) => [...acc, val.prodId],
          []
        );
        console.log('All prodIds: ', prodIds, '; also id is: ', id);
        promise = admin
          .firestore()
          .collection('Products')
          .doc('Departments')
          .collection(id)
          .where('key', 'array-contains-any', prodIds)
          .get();
        promises.push(promise);
      });
    
      const querySnapshots = await Promise.all(promises);
    
      const docs = [];
      
      querySnapshots.forEach(querySnapshot => {
          querySnapshot.forEach(doc => {
              docs.push(doc.data());
          });
      });
    

    我已经使用 forEach 循环遍历 QuerySnapshots 的数组,但您可以使用任何其他循环遍历/映射数组的方式来调整它

    【讨论】:

    • 正如@samthecodingman 指出的那样,我的错误是搜索一个数组,而实际上我应该在文档中的一个字段中搜索。
    【解决方案3】:

    要查询,索引必须首先存在。检查您的 Firebase 控制台或应用程序控制台,确保没有 index not created 警告。

    文档:https://firebase.google.com/docs/firestore/query-data/indexing

    Firestore 在阻止请求之前也有一个固有的垃圾邮件限制,请确保将您的方法批处理为不超过 50 个并发待处理。

    【讨论】:

    • 一切都事先检查过了。感谢您的帮助!
    猜你喜欢
    • 2020-04-04
    • 2020-11-29
    • 1970-01-01
    • 1970-01-01
    • 2019-04-11
    • 1970-01-01
    • 2021-11-01
    • 2019-04-20
    • 1970-01-01
    相关资源
    最近更新 更多