يدعم Cloud Firestore العمليات الذرية لقراءة البيانات وكتابتها. في مجموعة من العمليات الذرية، إما أن تنجح جميع العمليات، أو لا يتم تطبيق أي منها. هناك نوعان من العمليات الذرية في Cloud Firestore:
- المعاملات : المعاملة عبارة عن مجموعة من عمليات القراءة والكتابة على مستند واحد أو أكثر.
- عمليات الكتابة المجمعة : الكتابة المجمعة هي مجموعة من عمليات الكتابة على مستند واحد أو أكثر.
تحديث البيانات بالمعاملات
باستخدام مكتبات عملاء Cloud Firestore، يمكنك تجميع عمليات متعددة في معاملة واحدة. تكون المعاملات مفيدة عندما تريد تحديث قيمة الحقل استناداً إلى قيمته الحالية، أو قيمة بعض الحقول الأخرى.
تتكون المعاملة من أي عدد من عمليات get()
متبوعة بأي عدد من عمليات الكتابة مثل set()
أو update()
أو delete()
. في حالة التعديل المتزامن، يقوم Cloud Firestore بتشغيل المعاملة بأكملها مرة أخرى. على سبيل المثال، إذا كانت إحدى المعاملات تقرأ المستندات وقام عميل آخر بتعديل أي من تلك المستندات، فسيقوم Cloud Firestore بإعادة محاولة المعاملة. تضمن هذه الميزة تشغيل المعاملة على بيانات محدثة ومتسقة.
لا يتم تطبيق المعاملات جزئيًا أبدًا. يتم تنفيذ كافة عمليات الكتابة في نهاية معاملة ناجحة.
عند استخدام المعاملات، لاحظ ما يلي:
- يجب أن تأتي عمليات القراءة قبل عمليات الكتابة.
- قد يتم تشغيل وظيفة تستدعي معاملة (وظيفة المعاملة) أكثر من مرة إذا كان التحرير المتزامن يؤثر على المستند الذي تقرأه المعاملة.
- لا ينبغي لوظائف المعاملات تعديل حالة التطبيق مباشرة.
- ستفشل المعاملات عندما يكون العميل غير متصل بالإنترنت.
يوضح المثال التالي كيفية إنشاء معاملة وإدارتها:
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); });
سويفت
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!") } }
ج موضوعية
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"), );
جافا
بايثون
Python
سي ++
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
يذهب
بي أتش بي
وحدة
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); }); });
ج #
روبي
تمرير المعلومات من المعاملات
لا تقم بتعديل حالة التطبيق داخل وظائف المعاملة الخاصة بك. سيؤدي القيام بذلك إلى ظهور مشكلات في التزامن، نظرًا لأن وظائف المعاملة يمكن تشغيلها عدة مرات وليس من المضمون تشغيلها على مؤشر ترابط واجهة المستخدم. وبدلاً من ذلك، قم بتمرير المعلومات التي تحتاجها من وظائف المعاملات الخاصة بك. يعتمد المثال التالي على المثال السابق لإظهار كيفية تمرير المعلومات من المعاملة:
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); });
سويفت
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!)") } }
ج موضوعية
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"), );
جافا
بايثون
Python
سي ++
// This is not yet supported.
Node.js
يذهب
بي أتش بي
وحدة
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."); } });
ج #
روبي
فشل الصفقة
يمكن أن تفشل المعاملة للأسباب التالية:
- تحتوي المعاملة على عمليات القراءة بعد عمليات الكتابة. يجب أن تأتي عمليات القراءة دائمًا قبل أي عمليات كتابة.
- تقرأ المعاملة مستندًا تم تعديله خارج المعاملة. وفي هذه الحالة، يتم تشغيل المعاملة مرة أخرى تلقائيًا. تتم إعادة محاولة المعاملة لعدد محدود من المرات.
تجاوزت المعاملة الحد الأقصى لحجم الطلب وهو 10 ميجابايت.
يعتمد حجم المعاملة على أحجام المستندات وإدخالات الفهرس التي تم تعديلها بواسطة المعاملة. بالنسبة لعملية الحذف، يتضمن ذلك حجم المستند المستهدف وأحجام إدخالات الفهرس المحذوفة استجابة للعملية.
تُرجع المعاملة الفاشلة خطأً ولا تكتب أي شيء في قاعدة البيانات. لا تحتاج إلى التراجع عن المعاملة؛ يقوم Cloud Firestore بذلك تلقائيًا.
يكتب دفعة واحدة
إذا لم تكن بحاجة إلى قراءة أي مستندات في مجموعة العمليات الخاصة بك، فيمكنك تنفيذ عمليات كتابة متعددة كدفعة واحدة تحتوي على أي مجموعة من عمليات set()
أو update()
أو delete()
. يتم إكمال مجموعة من عمليات الكتابة بشكل ذري ويمكنها الكتابة إلى مستندات متعددة. يوضح المثال التالي كيفية إنشاء دفعة كتابة وتنفيذها:
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(() => { // ... });
سويفت
// 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.") } }
ج موضوعية
// 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((_) { // ... });
جافا
بايثون
Python
سي ++
// 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
يذهب
بي أتش بي
وحدة
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();
ج #
روبي
يمكن أن تحتوي الكتابة المجمعة على ما يصل إلى 500 عملية. يتم احتساب كل عملية في الدفعة بشكل منفصل ضمن استخدام Cloud Firestore الخاص بك.
مثل المعاملات، تكون عمليات الكتابة المجمعة ذرية. على عكس المعاملات، لا تحتاج عمليات الكتابة المجمعة إلى التأكد من بقاء مستندات القراءة غير معدلة مما يؤدي إلى تقليل حالات الفشل. وهي لا تخضع لإعادة المحاولة أو للفشل نتيجة لعدد كبير جدًا من المحاولات. يتم تنفيذ عمليات الكتابة المجمعة حتى عندما يكون جهاز المستخدم غير متصل بالإنترنت.
التحقق من صحة البيانات للعمليات الذرية
بالنسبة لمكتبات عملاء الهاتف المحمول/الويب، يمكنك التحقق من صحة البيانات باستخدام قواعد أمان Cloud Firestore . يمكنك التأكد من أن المستندات ذات الصلة يتم تحديثها دائمًا تلقائيًا ودائمًا كجزء من معاملة أو كتابة مجمعة. استخدم وظيفة قاعدة الأمان getAfter()
للوصول إلى حالة المستند والتحقق من صحتها بعد اكتمال مجموعة من العمليات ولكن قبل أن ينفذ Cloud Firestore العمليات.
على سبيل المثال، تخيل أن قاعدة البيانات الخاصة بمثال cities
تحتوي أيضًا على مجموعة countries
. يستخدم كل مستند country
حقل last_updated
لتتبع آخر مرة تم فيها تحديث أي مدينة مرتبطة بهذا البلد. تتطلب قواعد الأمان التالية أن تحديث مستند city
يجب أن يقوم أيضًا بتحديث الحقل last_updated
الخاص بالبلد ذي الصلة ذريًا:
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; } } }
حدود قواعد الأمان
في قواعد الأمان الخاصة بالمعاملات أو عمليات الكتابة المجمعة، يوجد حد يبلغ 20 استدعاء للوصول إلى المستند للعملية الذرية بأكملها بالإضافة إلى حد الاستدعاءات العادي البالغ 10 لكل عملية مستند واحدة في الدفعة.
على سبيل المثال، ضع في اعتبارك القواعد التالية لتطبيق الدردشة:
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)); } } }
توضح المقتطفات أدناه عدد استدعاءات الوصول إلى المستندات المستخدمة لبعض أنماط الوصول إلى البيانات:
// 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();
لمزيد من المعلومات حول كيفية حل مشكلات زمن الاستجابة الناتجة عن عمليات الكتابة الكبيرة وعمليات الكتابة المجمعة، والأخطاء الناتجة عن التنافس الناتج عن المعاملات المتداخلة، ومشكلات أخرى، خذ بعين الاعتبار مراجعة صفحة استكشاف الأخطاء وإصلاحها .