Cloud Firestore prend en charge les opérations atomiques pour lire et écrire des données. Dans un ensemble d'opérations atomiques, soit toutes les opérations réussissent, soit aucune d'entre elles n'est appliquée. Il existe deux types d'opérations atomiques dans Cloud Firestore :
- Transactions : une transaction est un ensemble d'opérations de lecture et d'écriture sur un ou plusieurs documents.
- Ecritures par lots : une écriture par lots est un ensemble d'opérations d'écriture sur un ou plusieurs documents.
Chaque transaction ou lot d'écritures peut écrire dans un maximum de 500 documents. Pour des limites supplémentaires liées aux écritures, consultez Quotas et limites .
Mise à jour des données avec les transactions
À l'aide des bibliothèques clientes Cloud Firestore, vous pouvez regrouper plusieurs opérations en une seule transaction. Les transactions sont utiles lorsque vous souhaitez mettre à jour la valeur d'un champ en fonction de sa valeur actuelle ou de la valeur d'un autre champ.
Une transaction consiste en un nombre quelconque d'opérations get()
suivies d'un nombre quelconque d'opérations d'écriture telles que set()
, update()
ou delete()
. Dans le cas d'une modification simultanée, Cloud Firestore exécute à nouveau l'intégralité de la transaction. Par exemple, si une transaction lit des documents et qu'un autre client modifie l'un de ces documents, Cloud Firestore relance la transaction. Cette fonctionnalité garantit que la transaction s'exécute sur des données à jour et cohérentes.
Les transactions n'appliquent jamais partiellement les écritures. Toutes les écritures s'exécutent à la fin d'une transaction réussie.
Lorsque vous utilisez des transactions, notez que :
- Les opérations de lecture doivent précéder les opérations d'écriture.
- Une fonction appelant une transaction (fonction de transaction) peut s'exécuter plusieurs fois si une modification simultanée affecte un document lu par la transaction.
- Les fonctions de transaction ne doivent pas modifier directement l'état de l'application.
- Les transactions échoueront lorsque le client est hors ligne.
L'exemple suivant montre comment créer et exécuter une transaction :
Web modular API
import { runTransaction } from "firebase/firestore"; try { await runTransaction(db, async (transaction) => { const sfDoc = await transaction.get(sfDocRef); if (!sfDoc.exists()) { throw "Document does not exist!"; } const newPopulation = sfDoc.data().population + 1; transaction.update(sfDocRef, { population: newPopulation }); }); console.log("Transaction successfully committed!"); } catch (e) { console.log("Transaction failed: ", e); }
Web namespaced API
// Create a reference to the SF doc. var sfDocRef = db.collection("cities").doc("SF"); // Uncomment to initialize the doc. // sfDocRef.set({ population: 0 }); return db.runTransaction((transaction) => { // This code may get re-run multiple times if there are conflicts. return transaction.get(sfDocRef).then((sfDoc) => { if (!sfDoc.exists) { throw "Document does not exist!"; } // Add one person to the city population. // Note: this could be done without a transaction // by updating the population using FieldValue.increment() var newPopulation = sfDoc.data().population + 1; transaction.update(sfDocRef, { population: newPopulation }); }); }).then(() => { console.log("Transaction successfully committed!"); }).catch((error) => { console.log("Transaction failed: ", error); });
Rapide
let sfReference = db.collection("cities").document("SF") db.runTransaction({ (transaction, errorPointer) -> Any? in let sfDocument: DocumentSnapshot do { try sfDocument = transaction.getDocument(sfReference) } catch let fetchError as NSError { errorPointer?.pointee = fetchError return nil } guard let oldPopulation = sfDocument.data()?["population"] as? Int else { let error = NSError( domain: "AppErrorDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" ] ) errorPointer?.pointee = error return nil } // Note: this could be done without a transaction // by updating the population using FieldValue.increment() transaction.updateData(["population": oldPopulation + 1], forDocument: sfReference) return nil }) { (object, error) in if let error = error { print("Transaction failed: \(error)") } else { print("Transaction successfully committed!") } }
Objectif c
FIRDocumentReference *sfReference = [[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]; [self.db runTransactionWithBlock:^id (FIRTransaction *transaction, NSError **errorPointer) { FIRDocumentSnapshot *sfDocument = [transaction getDocument:sfReference error:errorPointer]; if (*errorPointer != nil) { return nil; } if (![sfDocument.data[@"population"] isKindOfClass:[NSNumber class]]) { *errorPointer = [NSError errorWithDomain:@"AppErrorDomain" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"Unable to retreive population from snapshot" }]; return nil; } NSInteger oldPopulation = [sfDocument.data[@"population"] integerValue]; // Note: this could be done without a transaction // by updating the population using FieldValue.increment() [transaction updateData:@{ @"population": @(oldPopulation + 1) } forDocument:sfReference]; return nil; } completion:^(id result, NSError *error) { if (error != nil) { NSLog(@"Transaction failed: %@", error); } else { NSLog(@"Transaction successfully committed!"); } }];
Kotlin+KTX
val sfDocRef = db.collection("cities").document("SF") db.runTransaction { transaction -> val snapshot = transaction.get(sfDocRef) // Note: this could be done without a transaction // by updating the population using FieldValue.increment() val newPopulation = snapshot.getDouble("population")!! + 1 transaction.update(sfDocRef, "population", newPopulation) // Success null }.addOnSuccessListener { Log.d(TAG, "Transaction success!") } .addOnFailureListener { e -> Log.w(TAG, "Transaction failure.", e) }
Java
final DocumentReference sfDocRef = db.collection("cities").document("SF"); db.runTransaction(new Transaction.Function<Void>() { @Override public Void apply(@NonNull Transaction transaction) throws FirebaseFirestoreException { DocumentSnapshot snapshot = transaction.get(sfDocRef); // Note: this could be done without a transaction // by updating the population using FieldValue.increment() double newPopulation = snapshot.getDouble("population") + 1; transaction.update(sfDocRef, "population", newPopulation); // Success return null; } }).addOnSuccessListener(new OnSuccessListener<Void>() { @Override public void onSuccess(Void aVoid) { Log.d(TAG, "Transaction success!"); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.w(TAG, "Transaction failure.", e); } });
Dart
final sfDocRef = db.collection("cities").doc("SF"); db.runTransaction((transaction) async { final snapshot = await transaction.get(sfDocRef); // Note: this could be done without a transaction // by updating the population using FieldValue.increment() final newPopulation = snapshot.get("population") + 1; transaction.update(sfDocRef, {"population": newPopulation}); }).then( (value) => print("DocumentSnapshot successfully updated!"), onError: (e) => print("Error updating document $e"), );
Java
Python
Python
C++
DocumentReference sf_doc_ref = db->Collection("cities").Document("SF"); db->RunTransaction([sf_doc_ref](Transaction& transaction, std::string& out_error_message) -> Error { Error error = Error::kErrorOk; DocumentSnapshot snapshot = transaction.Get(sf_doc_ref, &error, &out_error_message); // Note: this could be done without a transaction by updating the // population using FieldValue::Increment(). std::int64_t new_population = snapshot.Get("population").integer_value() + 1; transaction.Update( sf_doc_ref, {{"population", FieldValue::Integer(new_population)}}); return Error::kErrorOk; }).OnCompletion([](const Future<void>& future) { if (future.error() == Error::kErrorOk) { std::cout << "Transaction success!" << std::endl; } else { std::cout << "Transaction failure: " << future.error_message() << std::endl; } });
Node.js
Aller
PHP
Unité
DocumentReference cityRef = db.Collection("cities").Document("SF"); db.RunTransactionAsync(transaction => { return transaction.GetSnapshotAsync(cityRef).ContinueWith((snapshotTask) => { DocumentSnapshot snapshot = snapshotTask.Result; long newPopulation = snapshot.GetValue<long>("Population") + 1; Dictionary<string, object> updates = new Dictionary<string, object> { { "Population", newPopulation} }; transaction.Update(cityRef, updates); }); });
C#
Rubis
Transmettre des informations sur les transactions
Ne modifiez pas l'état de l'application dans vos fonctions de transaction. Cela introduirait des problèmes de simultanéité, car les fonctions de transaction peuvent s'exécuter plusieurs fois et ne sont pas garanties de s'exécuter sur le thread d'interface utilisateur. Au lieu de cela, transmettez les informations dont vous avez besoin de vos fonctions de transaction. L'exemple suivant s'appuie sur l'exemple précédent pour montrer comment transmettre des informations d'une transaction :
Web modular API
import { doc, runTransaction } from "firebase/firestore"; // Create a reference to the SF doc. const sfDocRef = doc(db, "cities", "SF"); try { const newPopulation = await runTransaction(db, async (transaction) => { const sfDoc = await transaction.get(sfDocRef); if (!sfDoc.exists()) { throw "Document does not exist!"; } const newPop = sfDoc.data().population + 1; if (newPop <= 1000000) { transaction.update(sfDocRef, { population: newPop }); return newPop; } else { return Promise.reject("Sorry! Population is too big"); } }); console.log("Population increased to ", newPopulation); } catch (e) { // This will be a "population is too big" error. console.error(e); }
Web namespaced API
// Create a reference to the SF doc. var sfDocRef = db.collection("cities").doc("SF"); db.runTransaction((transaction) => { return transaction.get(sfDocRef).then((sfDoc) => { if (!sfDoc.exists) { throw "Document does not exist!"; } var newPopulation = sfDoc.data().population + 1; if (newPopulation <= 1000000) { transaction.update(sfDocRef, { population: newPopulation }); return newPopulation; } else { return Promise.reject("Sorry! Population is too big."); } }); }).then((newPopulation) => { console.log("Population increased to ", newPopulation); }).catch((err) => { // This will be an "population is too big" error. console.error(err); });
Rapide
let sfReference = db.collection("cities").document("SF") db.runTransaction({ (transaction, errorPointer) -> Any? in let sfDocument: DocumentSnapshot do { try sfDocument = transaction.getDocument(sfReference) } catch let fetchError as NSError { errorPointer?.pointee = fetchError return nil } guard let oldPopulation = sfDocument.data()?["population"] as? Int else { let error = NSError( domain: "AppErrorDomain", code: -1, userInfo: [ NSLocalizedDescriptionKey: "Unable to retrieve population from snapshot \(sfDocument)" ] ) errorPointer?.pointee = error return nil } // Note: this could be done without a transaction // by updating the population using FieldValue.increment() let newPopulation = oldPopulation + 1 guard newPopulation <= 1000000 else { let error = NSError( domain: "AppErrorDomain", code: -2, userInfo: [NSLocalizedDescriptionKey: "Population \(newPopulation) too big"] ) errorPointer?.pointee = error return nil } transaction.updateData(["population": newPopulation], forDocument: sfReference) return newPopulation }) { (object, error) in if let error = error { print("Error updating population: \(error)") } else { print("Population increased to \(object!)") } }
Objectif c
FIRDocumentReference *sfReference = [[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]; [self.db runTransactionWithBlock:^id (FIRTransaction *transaction, NSError **errorPointer) { FIRDocumentSnapshot *sfDocument = [transaction getDocument:sfReference error:errorPointer]; if (*errorPointer != nil) { return nil; } if (![sfDocument.data[@"population"] isKindOfClass:[NSNumber class]]) { *errorPointer = [NSError errorWithDomain:@"AppErrorDomain" code:-1 userInfo:@{ NSLocalizedDescriptionKey: @"Unable to retreive population from snapshot" }]; return nil; } NSInteger population = [sfDocument.data[@"population"] integerValue]; population++; if (population >= 1000000) { *errorPointer = [NSError errorWithDomain:@"AppErrorDomain" code:-2 userInfo:@{ NSLocalizedDescriptionKey: @"Population too big" }]; return @(population); } [transaction updateData:@{ @"population": @(population) } forDocument:sfReference]; return nil; } completion:^(id result, NSError *error) { if (error != nil) { NSLog(@"Transaction failed: %@", error); } else { NSLog(@"Population increased to %@", result); } }];
Kotlin+KTX
val sfDocRef = db.collection("cities").document("SF") db.runTransaction { transaction -> val snapshot = transaction.get(sfDocRef) val newPopulation = snapshot.getDouble("population")!! + 1 if (newPopulation <= 1000000) { transaction.update(sfDocRef, "population", newPopulation) newPopulation } else { throw FirebaseFirestoreException( "Population too high", FirebaseFirestoreException.Code.ABORTED, ) } }.addOnSuccessListener { result -> Log.d(TAG, "Transaction success: $result") }.addOnFailureListener { e -> Log.w(TAG, "Transaction failure.", e) }
Java
final DocumentReference sfDocRef = db.collection("cities").document("SF"); db.runTransaction(new Transaction.Function<Double>() { @Override public Double apply(@NonNull Transaction transaction) throws FirebaseFirestoreException { DocumentSnapshot snapshot = transaction.get(sfDocRef); double newPopulation = snapshot.getDouble("population") + 1; if (newPopulation <= 1000000) { transaction.update(sfDocRef, "population", newPopulation); return newPopulation; } else { throw new FirebaseFirestoreException("Population too high", FirebaseFirestoreException.Code.ABORTED); } } }).addOnSuccessListener(new OnSuccessListener<Double>() { @Override public void onSuccess(Double result) { Log.d(TAG, "Transaction success: " + result); } }) .addOnFailureListener(new OnFailureListener() { @Override public void onFailure(@NonNull Exception e) { Log.w(TAG, "Transaction failure.", e); } });
Dart
final sfDocRef = db.collection("cities").doc("SF"); db.runTransaction((transaction) { return transaction.get(sfDocRef).then((sfDoc) { final newPopulation = sfDoc.get("population") + 1; transaction.update(sfDocRef, {"population": newPopulation}); return newPopulation; }); }).then( (newPopulation) => print("Population increased to $newPopulation"), onError: (e) => print("Error updating document $e"), );
Java
Python
Python
C++
// This is not yet supported.
Node.js
Aller
PHP
Unité
DocumentReference cityRef = db.Collection("cities").Document("SF"); db.RunTransactionAsync(transaction => { return transaction.GetSnapshotAsync(cityRef).ContinueWith((task) => { long newPopulation = task.Result.GetValue<long>("Population") + 1; if (newPopulation <= 1000000) { Dictionary<string, object> updates = new Dictionary<string, object> { { "Population", newPopulation} }; transaction.Update(cityRef, updates); return true; } else { return false; } }); }).ContinueWith((transactionResultTask) => { if (transactionResultTask.Result) { Console.WriteLine("Population updated successfully."); } else { Console.WriteLine("Sorry! Population is too big."); } });
C#
Rubis
Échec de la transaction
Une transaction peut échouer pour les raisons suivantes :
- La transaction contient des opérations de lecture après les opérations d'écriture. Les opérations de lecture doivent toujours précéder les opérations d'écriture.
- La transaction a lu un document qui a été modifié en dehors de la transaction. Dans ce cas, la transaction s'exécute automatiquement à nouveau. La transaction est réessayée un nombre fini de fois.
La transaction a dépassé la taille de demande maximale de 10 Mio.
La taille de la transaction dépend de la taille des documents et des entrées d'index modifiées par la transaction. Pour une opération de suppression, cela inclut la taille du document cible et les tailles des entrées d'index supprimées en réponse à l'opération.
Une transaction ayant échoué renvoie une erreur et n'écrit rien dans la base de données. Vous n'avez pas besoin d'annuler la transaction ; Cloud Firestore le fait automatiquement.
Écritures par lots
Si vous n'avez pas besoin de lire de documents dans votre ensemble d'opérations, vous pouvez exécuter plusieurs opérations d'écriture en un seul lot contenant n'importe quelle combinaison d'opérations set()
, update()
ou delete()
. Un lot d'écritures se termine de manière atomique et peut écrire dans plusieurs documents. L'exemple suivant montre comment créer et valider un batch d'écriture :
Web modular API
import { writeBatch, doc } from "firebase/firestore"; // Get a new write batch const batch = writeBatch(db); // Set the value of 'NYC' const nycRef = doc(db, "cities", "NYC"); batch.set(nycRef, {name: "New York City"}); // Update the population of 'SF' const sfRef = doc(db, "cities", "SF"); batch.update(sfRef, {"population": 1000000}); // Delete the city 'LA' const laRef = doc(db, "cities", "LA"); batch.delete(laRef); // Commit the batch await batch.commit();
Web namespaced API
// Get a new write batch var batch = db.batch(); // Set the value of 'NYC' var nycRef = db.collection("cities").doc("NYC"); batch.set(nycRef, {name: "New York City"}); // Update the population of 'SF' var sfRef = db.collection("cities").doc("SF"); batch.update(sfRef, {"population": 1000000}); // Delete the city 'LA' var laRef = db.collection("cities").doc("LA"); batch.delete(laRef); // Commit the batch batch.commit().then(() => { // ... });
Rapide
// Get new write batch let batch = db.batch() // Set the value of 'NYC' let nycRef = db.collection("cities").document("NYC") batch.setData([:], forDocument: nycRef) // Update the population of 'SF' let sfRef = db.collection("cities").document("SF") batch.updateData(["population": 1000000 ], forDocument: sfRef) // Delete the city 'LA' let laRef = db.collection("cities").document("LA") batch.deleteDocument(laRef) // Commit the batch batch.commit() { err in if let err = err { print("Error writing batch \(err)") } else { print("Batch write succeeded.") } }
Objectif c
// Get new write batch FIRWriteBatch *batch = [self.db batch]; // Set the value of 'NYC' FIRDocumentReference *nycRef = [[self.db collectionWithPath:@"cities"] documentWithPath:@"NYC"]; [batch setData:@{} forDocument:nycRef]; // Update the population of 'SF' FIRDocumentReference *sfRef = [[self.db collectionWithPath:@"cities"] documentWithPath:@"SF"]; [batch updateData:@{ @"population": @1000000 } forDocument:sfRef]; // Delete the city 'LA' FIRDocumentReference *laRef = [[self.db collectionWithPath:@"cities"] documentWithPath:@"LA"]; [batch deleteDocument:laRef]; // Commit the batch [batch commitWithCompletion:^(NSError * _Nullable error) { if (error != nil) { NSLog(@"Error writing batch %@", error); } else { NSLog(@"Batch write succeeded."); } }];
Kotlin+KTX
val nycRef = db.collection("cities").document("NYC") val sfRef = db.collection("cities").document("SF") val laRef = db.collection("cities").document("LA") // Get a new write batch and commit all write operations db.runBatch { batch -> // Set the value of 'NYC' batch.set(nycRef, City()) // Update the population of 'SF' batch.update(sfRef, "population", 1000000L) // Delete the city 'LA' batch.delete(laRef) }.addOnCompleteListener { // ... }
Java
// Get a new write batch WriteBatch batch = db.batch(); // Set the value of 'NYC' DocumentReference nycRef = db.collection("cities").document("NYC"); batch.set(nycRef, new City()); // Update the population of 'SF' DocumentReference sfRef = db.collection("cities").document("SF"); batch.update(sfRef, "population", 1000000L); // Delete the city 'LA' DocumentReference laRef = db.collection("cities").document("LA"); batch.delete(laRef); // Commit the batch batch.commit().addOnCompleteListener(new OnCompleteListener<Void>() { @Override public void onComplete(@NonNull Task<Void> task) { // ... } });
Dart
// Get a new write batch final batch = db.batch(); // Set the value of 'NYC' var nycRef = db.collection("cities").doc("NYC"); batch.set(nycRef, {"name": "New York City"}); // Update the population of 'SF' var sfRef = db.collection("cities").doc("SF"); batch.update(sfRef, {"population": 1000000}); // Delete the city 'LA' var laRef = db.collection("cities").doc("LA"); batch.delete(laRef); // Commit the batch batch.commit().then((_) { // ... });
Java
Python
Python
C++
// Get a new write batch WriteBatch batch = db->batch(); // Set the value of 'NYC' DocumentReference nyc_ref = db->Collection("cities").Document("NYC"); batch.Set(nyc_ref, {}); // Update the population of 'SF' DocumentReference sf_ref = db->Collection("cities").Document("SF"); batch.Update(sf_ref, {{"population", FieldValue::Integer(1000000)}}); // Delete the city 'LA' DocumentReference la_ref = db->Collection("cities").Document("LA"); batch.Delete(la_ref); // Commit the batch batch.Commit().OnCompletion([](const Future<void>& future) { if (future.error() == Error::kErrorOk) { std::cout << "Write batch success!" << std::endl; } else { std::cout << "Write batch failure: " << future.error_message() << std::endl; } });
Node.js
Aller
PHP
Unité
WriteBatch batch = db.StartBatch(); // Set the data for NYC DocumentReference nycRef = db.Collection("cities").Document("NYC"); Dictionary<string, object> nycData = new Dictionary<string, object> { { "name", "New York City" } }; batch.Set(nycRef, nycData); // Update the population for SF DocumentReference sfRef = db.Collection("cities").Document("SF"); Dictionary<string, object> updates = new Dictionary<string, object> { { "Population", 1000000} }; batch.Update(sfRef, updates); // Delete LA DocumentReference laRef = db.Collection("cities").Document("LA"); batch.Delete(laRef); // Commit the batch batch.CommitAsync();
C#
Rubis
Une écriture par lot peut contenir jusqu'à 500 opérations. Chaque opération du lot compte séparément dans votre utilisation de Cloud Firestore.
Comme les transactions, les écritures par lots sont atomiques. Contrairement aux transactions, les écritures par lots n'ont pas besoin de s'assurer que les documents lus restent inchangés, ce qui réduit le nombre de cas d'échec. Ils ne sont pas sujets à de nouvelles tentatives ou à des échecs dus à un trop grand nombre de tentatives. Les écritures par lots s'exécutent même lorsque l'appareil de l'utilisateur est hors ligne.
Validation des données pour les opérations atomiques
Pour les bibliothèques clientes mobiles/web, vous pouvez valider les données à l'aide des règles de sécurité Cloud Firestore . Vous pouvez vous assurer que les documents associés sont toujours mis à jour de manière atomique et toujours dans le cadre d'une transaction ou d'une écriture par lots. Utilisez la fonction de règle de sécurité getAfter()
pour accéder et valider l'état d'un document après la fin d'un ensemble d'opérations, mais avant que Cloud Firestore ne valide les opérations.
Par exemple, imaginez que la base de données de l'exemple cities
contienne également une collection countries
. Chaque document country
utilise un champ last_updated
pour garder une trace de la dernière fois qu'une ville liée à ce pays a été mise à jour. Les règles de sécurité suivantes exigent qu'une mise à jour d'un document city
mette également à jour de manière atomique le champ last_updated
du pays associé :
service cloud.firestore { match /databases/{database}/documents { // If you update a city doc, you must also // update the related country's last_updated field. match /cities/{city} { allow write: if request.auth != null && getAfter( /databases/$(database)/documents/countries/$(request.resource.data.country) ).data.last_updated == request.time; } match /countries/{country} { allow write: if request.auth != null; } } }
Limites des règles de sécurité
Dans les règles de sécurité pour les transactions ou les écritures par lots, il existe une limite de 20 appels d'accès aux documents pour l'ensemble de l'opération atomique en plus de la limite normale de 10 appels pour chaque opération de document unique dans le lot.
Par exemple, considérez les règles suivantes pour une application de chat :
service cloud.firestore { match /databases/{db}/documents { function prefix() { return /databases/{db}/documents; } match /chatroom/{roomId} { allow read, write: if request.auth != null && roomId in get(/$(prefix())/users/$(request.auth.uid)).data.chats || exists(/$(prefix())/admins/$(request.auth.uid)); } match /users/{userId} { allow read, write: if request.auth != null && request.auth.uid == userId || exists(/$(prefix())/admins/$(request.auth.uid)); } match /admins/{userId} { allow read, write: if request.auth != null && exists(/$(prefix())/admins/$(request.auth.uid)); } } }
Les extraits ci-dessous illustrent le nombre d'appels d'accès aux documents utilisés pour quelques modèles d'accès aux données :
// 0 document access calls used, because the rules evaluation short-circuits // before the exists() call is invoked. db.collection('user').doc('myuid').get(...); // 1 document access call used. The maximum total allowed for this call // is 10, because it is a single document request. db.collection('chatroom').doc('mygroup').get(...); // Initializing a write batch... var batch = db.batch(); // 2 document access calls used, 10 allowed. var group1Ref = db.collection("chatroom").doc("group1"); batch.set(group1Ref, {msg: "Hello, from Admin!"}); // 1 document access call used, 10 allowed. var newUserRef = db.collection("users").doc("newuser"); batch.update(newUserRef, {"lastSignedIn": new Date()}); // 1 document access call used, 10 allowed. var removedAdminRef = db.collection("admin").doc("otheruser"); batch.delete(removedAdminRef); // The batch used a total of 2 + 1 + 1 = 4 document access calls, out of a total // 20 allowed. batch.commit();
Pour plus d'informations sur la résolution des problèmes de latence causés par des écritures volumineuses et par lots, des erreurs dues à des conflits de transactions qui se chevauchent et d'autres problèmes, consultez la page de dépannage .