code

Firestore가 하나의 쿼리를 사용하여 조건에 맞는 여러 문서를 업데이트할 수 있습니까?

starcafe 2023. 6. 17. 09:32
반응형

Firestore가 하나의 쿼리를 사용하여 조건에 맞는 여러 문서를 업데이트할 수 있습니까?

즉, SQL에서 이와 동등한 Firestore가 무엇인지 파악하려고 합니다.

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

예, 여러 문서를 한 업데이트하는 방법을 묻는 것입니다. 하지만 연결된 질문과 달리, 특정 조건에 맞는 모든 문서에 플래그를 설정하기만 하면 되기 때문에 메모리에 아무것도 읽지 않고 한 번에 업데이트하는 방법을 묻는 것입니다.

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

제가 기대했던 대로입니다. CollectionReference에는 다음이 없습니다..update방법.

트랜잭션일괄 처리된 쓰기 설명서에는 트랜잭션 및 일괄 처리된 쓰기가 나와 있습니다.트랜잭션은 "트랜잭션은 임의의 수의 get() 작업에 이어 임의의 수의 쓰기 작업으로 구성됩니다." 일괄 처리된 쓰기는 문서별로 작동하므로 해결책이 아닙니다.

MongoDB를 사용하면, 이것은

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

그렇다면 Firestore는 SQL 데이터베이스 또는 MongoDB가 작동하는 방식, 즉 각 문서에 대해 클라이언트를 왕복할 필요 없이 하나의 쿼리로 여러 문서를 업데이트할 수 있습니까?그렇지 않다면 어떻게 효율적으로 이 작업을 수행할 수 있습니까?

Cloud Firestore에서 문서를 업데이트하려면 ID를 알아야 합니다.Cloud Firestore는 SQL의 업데이트 쿼리와 동일한 쿼리를 지원하지 않습니다.

이 작업은 항상 두 단계로 수행해야 합니다.

  1. 문서 ID를 결정하기 위해 사용자 조건으로 쿼리 실행
  2. 개별 업데이트 또는 하나 이상의 일괄 작성으로 문서를 업데이트합니다.

1단계의 문서 ID만 필요합니다.따라서 ID만 반환하는 쿼리를 실행할 수 있습니다.클라이언트 측 SDK에서는 이 작업을 수행할 수 없지만 다음과 같이 REST API 및 Admin SDK를 통해 수행할 수 있습니다.Cloud Firestore 컬렉션에서 문서 ID 목록을 가져오는 방법은 무엇입니까?

프랭크의 대답은 사실 훌륭하고 문제를 해결합니다.

하지만 서두르는 사람들에게 이 토막글은 다음과 같은 도움이 될 수 있습니다.

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그것이 모든 문서에서 변경되는 것입니다.

또한 컬렉션의 이름으로 함수를 호출하는 것도 잊지 마십시오.

가장 간단한 방법은 다음과 같습니다.

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

Dart / Float 사용자용(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');
  });
}

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

Renato와 David의 답변과 배치 부분에 대한 비동기/대기 구문을 결합합니다.또한 약속이 실패할 경우를 대비하여 시도/어획물을 동봉합니다.

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

저는 몇 가지 답변이 마음에 들지만, 이것이 더 깨끗하다고 생각합니다.

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

일괄 업데이트와 문서의 "ref"를 사용합니다.

컬렉션 업데이트를 위해 이미 UID를 수집한 경우 다음 단계를 수행합니다.

if(uids.length) {
    for(let i = 0; i < uids.length; i++) {    
    await (db.collection("collectionName")
             .doc(uids[i]))
             .update({"fieldName": false});
             };
        };

언급URL : https://stackoverflow.com/questions/48947499/can-firestore-update-multiple-documents-matching-a-condition-using-one-query

반응형