Firestore可以使用一个查询更新符合条件的多个文档吗?

时间:2018-02-23 12:05:39

标签: firebase google-cloud-firestore

换句话说,我试图在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文档提到了事务和批量写入。交易结束是因为&#34;交易包含任意数量的 get()操作,后跟任意数量的写操作&#34; Batched writes也不是解决方案,因为它们逐个文档地工作。

使用MongoDB,这将是

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

那么,Firestore可以用一个查询更新多个文档,SQL数据库或MongoDB的工作方式,即不需要为每个文档往返客户端吗?如果没有,如何有效地完成这项工作?

6 个答案:

答案 0 :(得分:12)

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

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

  1. 根据您的条件运行查询以确定文档ID
  2. 使用单个更新或单个batched write更新文档。
  3. 请注意,您只需要步骤1中的文档ID。因此,您可以运行仅返回ID的查询。这在客户端SDK中是不可能的,但可以通过REST API和Admin SDK完成,如下所示:How to get a list of keys in Firestore?

答案 1 :(得分:2)

最简单的方法是这样

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 })
        })
      }
    })

答案 2 :(得分:1)

如果有人正在寻找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;
}

答案 3 :(得分:0)

弗兰克的答案实际上是一个很好的答案,确实可以解决问题。

但是对于那些着急的人来说,此片段可能会帮助您:

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

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

答案 4 :(得分:0)

对于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 :(得分:0)

结合 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;
    }