【问题标题】:Can Firestore update multiple documents matching a condition, using one query?Firestore 可以使用一个查询更新多个匹配条件的文档吗?
【发布时间】:2018-08-03 11:44:13
【问题描述】:

换句话说,我试图找出在 SQL 中与此等效的 Firestore 是什么:

UPDATE table SET field = 'foo' WHERE <condition>`

是的,我问的是如何update multiple documentsat once,但与链接的问题不同,我特意问的是如何一次性做到这一点,无需将任何内容读入内存,因为当您只想在所有符合条件的文档上设置标志时,无需这样做。

db.collection('table')
  .where(...condition...)
  .update({
    field: 'foo',
  });

这是我期望的工作,CollectionReference 没有 .update 方法。

Transactions and Batched Writes 文档提到了事务和批量写入。事务已结束,因为“事务由任意数量的 get() 操作 和任意数量的写入操作组成”Batched writes 也不是解决方案,因为它们逐个文档地工作。

使用MongoDB,这将是

db.table.update(
  { /* where clause */ },
  { $set: { field: 'foo' } }
)

那么,Firestore 是否可以像 SQL 数据库或 MongoDB 的工作方式一样,通过一个查询更新多个文档,即不需要为每个文档往返客户端?如果没有,如何有效地做到这一点?

【问题讨论】:

    标签: firebase google-cloud-firestore


    【解决方案1】:

    在 Cloud Firestore 中更新文档需要知道其 ID。 Cloud Firestore 不支持等效的 SQL 更新查询。

    您将始终需要分两步执行此操作:

    1. 使用您的条件运行查询以确定文档 ID
    2. 使用单个更新或一个或多个batched writes 更新文档。

    请注意,您只需要步骤 1 中的文档 ID。因此您可以运行仅返回 ID 的查询。这在客户端 SDK 中是不可能的,但可以通过 REST API 和 Admin SDK 来完成,如下所示:How to get a list of document IDs in a collection Cloud Firestore?

    【讨论】:

    • 今天仍然如此吗?如果是这样,路线图是否包含模拟UPDATE table SET field = 'foo' WHERE &lt;condition&gt; 的 SQL 功能的功能?该文档暗示它仍然是正确的,但对此并不明确。感谢您的帮助!
    • 是的,今天仍然如此。
    【解决方案2】:

    弗兰克的回答实际上是一个很好的回答,并且确实解决了这个问题。

    但对于那些赶时间的人,也许这个 sn-p 可能会帮助你:

    const updateAllFromCollection = async (collectionName) => {
        const firebase = require('firebase-admin')
    
        const collection = firebase.firestore().collection(collectionName)
    
        const newDocumentBody = {
            message: 'hello world'
        }
    
        collection.where('message', '==', 'goodbye world').get().then(response => {
            let batch = firebase.firestore().batch()
            response.docs.forEach((doc) => {
                const docRef = firebase.firestore().collection(collectionName).doc(doc.id)
                batch.update(docRef, newDocumentBody)
            })
            batch.commit().then(() => {
                console.log(`updated all documents inside ${collectionName}`)
            })
        })
    }
    

    只需更改查询数据的 where 函数和每个文档上正在更改的 newDocumentBody 函数中的内容。

    也不要忘记使用集合名称调用函数。

    【讨论】:

    • 您可以在 docRef 的初始化中重用您的 collection 变量,对吧? const docRef = collection.doc(doc.id)
    • firebase.firestore().collection(collectionName).doc(doc.id) 应替换为 doc.ref
    • 当心!如果您有超过 500 个文档,则不能这样做。事务和批次限制:firebase.google.com/docs/firestore/manage-data/transactions
    【解决方案3】:

    最简单的方法是这样的

    const ORDER_ITEMS = firebase.firestore().collection('OrderItems')
    
    ORDER_ITEMS.where('order', '==', 2)
      .get()
      .then(snapshots => {
        if (snapshots.size > 0) {
          snapshots.forEach(orderItem => {
            ORDER_ITEMS.doc(orderItem.id).update({ status: 1 })
          })
        }
      })
    

    【讨论】:

    • 非常感谢,它对我的​​项目帮助很大!
    • 这是最好的答案,简单易懂
    【解决方案4】:

    对于 Dart / Flutter 用户(由 Renato Trombini Neto 编辑)

    // CollectionReference collection = FirebaseFirestore.instance.collection('something');
    // This collection can be a subcollection.
    
    _updateAllFromCollection(CollectionReference collection) async {
      var newDocumentBody = {"username": ''};
      User firebaseUser = FirebaseAuth.instance.currentUser;
      DocumentReference docRef;
    
      var response = await collection.where('uid', isEqualTo: firebaseUser.uid).get();
      var batch = FirebaseFirestore.instance.batch();
      response.docs.forEach((doc) {
        docRef = collection.doc(doc.id);
        batch.update(docRef, newDocumentBody);
      });
      batch.commit().then((a) {
        print('updated all documents inside Collection');
      });
    }
    

    【讨论】:

      【解决方案5】:

      如果有人在寻找 Java 解决方案:

      public boolean bulkUpdate() {
        try {
          // see https://firebase.google.com/docs/firestore/quotas#writes_and_transactions
          int writeBatchLimit = 500;
          int totalUpdates = 0;
      
          while (totalUpdates % writeBatchLimit == 0) {
            WriteBatch writeBatch = this.firestoreDB.batch();
            // the query goes here
            List<QueryDocumentSnapshot> documentsInBatch =
                this.firestoreDB.collection("student")
                    .whereEqualTo("graduated", false)
                    .limit(writeBatchLimit)
                    .get()
                    .get()
                    .getDocuments();
      
            if (documentsInBatch.isEmpty()) {
              break;
            }
            // what I want to change goes here
            documentsInBatch.forEach(
                document -> writeBatch.update(document.getReference(), "graduated", true));
      
            writeBatch.commit().get();
      
            totalUpdates += documentsInBatch.size();
          }
      
          System.out.println("Number of updates: " + totalUpdates);
      
        } catch (Exception e) {
          return false;
        }
        return true;
      }
      

      【讨论】:

      • 这将创建一个无限循环,因为您将一遍又一遍地从 Firestore 中读取相同的文档。如果文档数超过批处理限制,它不会从您离开读取上一次迭代的位置开始读取。但是break; 也不会在文档计数小于批处理限制时被调用。
      • 感谢您的关注!这实际上是我实际查询的修改版本。我编辑了我的答案以明确搜索 A = x,然后批量更新 A = y,因此它不会永远循环。
      【解决方案6】:

      结合 Renato 和 David 的答案,以及批处理部分的 async/await 语法。如果任何承诺失败,也可以将它们包含在 try/catch 中:

          const updateAllFromCollection = async (collectionName) => {
      
              const firebase = require('firebase-admin');
              const collection = firebase.firestore().collection(collectionName);
              const newDocumentBody = { message: 'hello world' };
      
              try {
                 const response = await collection.where('message', '==', 'goodbye world').get();
                 const batch = firebase.firestore().batch();
                 response.docs.forEach((doc) => {
                    batch.update(doc.ref, newDocumentBody);
                 });
                 await batch.commit();  //Done
                 console.log(`updated all documents inside ${collectionName}`);
      
             } catch (err) {
                 console.error(err);
             }
             return;
          }
      

      【讨论】:

      • 漂亮的代码。但我认为这可能仍然会在超过 500 个的批次下遇到问题。
      【解决方案7】:

      我喜欢一些答案,但我觉得这更干净:

      import * as admin from "firebase-admin";
      const db = admin.firestore();
      const updates = { status: "pending" }
      await db
        .collection("COLLECTION_NAME")
        .where("status", "==", "open")
        .get()
        .then((snap) => {
          let batch = db.batch();
          snap.docs.forEach((doc) => {
            const ref = doc.ref;
            batch.update(ref, updates);
          });
          return batch.commit();
        });
      

      它使用批量更新和文档中的“参考”。

      【讨论】:

        猜你喜欢
        • 2020-11-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-05
        相关资源
        最近更新 更多