Catch up on everthing we announced at this year's Firebase Summit. Learn more

עסקאות וכתובות באצווה

Cloud Firestore תומך בפעולות אטומיות לקריאה וכתיבת נתונים. בקבוצה של פעולות אטומיות, או שכל הפעולות מצליחות, או שאף אחת מהן לא מיושמת. ישנם שני סוגים של פעולות אטומיות ב-Cloud Firestore:

  • עסקות: א עסקה היא קבוצה של פעולות קריאה וכתיבה על מסמך אחד או יותר.
  • לכלכתי כותב: א כתיבת באצוות היא סט של פעולות כתיבה על מסמך אחד או יותר.

כל עסקה או אצווה של כתיבה יכולה לכתוב עד 500 מסמכים לכל היותר. עבור מגבלות נוספות הקשורות כותב, לראות מכסות ומגבלות .

עדכון נתונים עם עסקאות

באמצעות ספריות הלקוחות של Cloud Firestore, אתה יכול לקבץ מספר פעולות לעסקה אחת. עסקאות שימושיות כאשר ברצונך לעדכן ערך של שדה על סמך הערך הנוכחי שלו, או הערך של שדה אחר.

עסקה מורכבת כל מספר של get() פעולות ואחריו מספר כלשהו של פעולות כתיבה כגון set() , update() , או delete() . במקרה של עריכה במקביל, Cloud Firestore מפעיל שוב את כל העסקה. לדוגמה, אם עסקה קוראת מסמכים ולקוח אחר משנה כל אחד מהמסמכים הללו, Cloud Firestore מנסה שוב את העסקה. תכונה זו מבטיחה שהעסקה תרוץ על נתונים עדכניים ועקביים.

עסקאות לעולם לא חלות באופן חלקי על כתיבה. כל הכתיבה מתבצעת בתום עסקה מוצלחת.

בעת שימוש בעסקאות, שים לב כי:

  • פעולות קריאה חייבות לבוא לפני פעולות כתיבה.
  • פונקציה הקוראת לעסקה (פונקציית טרנזקציה) עשויה לפעול יותר מפעם אחת אם עריכה במקביל משפיעה על מסמך שהטרנזקציה קוראת.
  • פונקציות עסקה לא אמורות לשנות ישירות את מצב היישום.
  • עסקאות ייכשלו כאשר הלקוח לא מקוון.

הדוגמה הבאה מראה כיצד ליצור ולהפעיל עסקה:

גרסת אינטרנט 9

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

גרסת אינטרנט 8

// 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);
});
מָהִיר
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
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!")
    }
}
Objective-C
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
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!");
  }
}];

Java

final DocumentReference sfDocRef = db.collection("cities").document("SF");

db.runTransaction(new Transaction.Function<Void>() {
    @Override
    public Void apply(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);
    }
});

קוטלין+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
// Initialize doc
final DocumentReference docRef = db.collection("cities").document("SF");
City city = new City("SF");
city.setCountry("USA");
city.setPopulation(860000L);
docRef.set(city).get();

// run an asynchronous transaction
ApiFuture<Void> futureTransaction = db.runTransaction(transaction -> {
  // retrieve document and increment population field
  DocumentSnapshot snapshot = transaction.get(docRef).get();
  long oldPopulation = snapshot.getLong("population");
  transaction.update(docRef, "population", oldPopulation + 1);
  return null;
});
// block on transaction operation using transaction.get()
פִּיתוֹן
transaction = db.transaction()
city_ref = db.collection(u'cities').document(u'SF')

@firestore.transactional
def update_in_transaction(transaction, city_ref):
    snapshot = city_ref.get(transaction=transaction)
    transaction.update(city_ref, {
        u'population': snapshot.get(u'population') + 1
    })

update_in_transaction(transaction, city_ref)

פִּיתוֹן

transaction = db.transaction()
city_ref = db.collection("cities").document("SF")

@firestore.async_transactional
async def update_in_transaction(transaction, city_ref):
    snapshot = await city_ref.get(transaction=transaction)
    transaction.update(city_ref, {"population": snapshot.get("population") + 1})

await update_in_transaction(transaction, city_ref)
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
// Initialize document
const cityRef = db.collection('cities').doc('SF');
await cityRef.set({
  name: 'San Francisco',
  state: 'CA',
  country: 'USA',
  capital: false,
  population: 860000
});

try {
  await db.runTransaction(async (t) => {
    const doc = await t.get(cityRef);

    // Add one person to the city population.
    // Note: this could be done without a transaction
    //       by updating the population using FieldValue.increment()
    const newPopulation = doc.data().population + 1;
    t.update(cityRef, {population: newPopulation});
  });

  console.log('Transaction success!');
} catch (e) {
  console.log('Transaction failure:', e);
}
ללכת
ref := client.Collection("cities").Doc("SF")
err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
	doc, err := tx.Get(ref) // tx.Get, NOT ref.Get!
	if err != nil {
		return err
	}
	pop, err := doc.DataAt("population")
	if err != nil {
		return err
	}
	return tx.Set(ref, map[string]interface{}{
		"population": pop.(int64) + 1,
	}, firestore.MergeAll)
})
if err != nil {
	// Handle any errors appropriately in this section.
	log.Printf("An error has occurred: %s", err)
}
PHP
$cityRef = $db->collection('samples/php/cities')->document('SF');
$db->runTransaction(function (Transaction $transaction) use ($cityRef) {
    $snapshot = $transaction->snapshot($cityRef);
    $newPopulation = $snapshot['population'] + 1;
    $transaction->update($cityRef, [
        ['path' => 'population', 'value' => $newPopulation]
    ]);
});
אַחְדוּת
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#
DocumentReference cityRef = db.Collection("cities").Document("SF");
await db.RunTransactionAsync(async transaction =>
{
    DocumentSnapshot snapshot = await transaction.GetSnapshotAsync(cityRef);
    long newPopulation = snapshot.GetValue<long>("Population") + 1;
    Dictionary<string, object> updates = new Dictionary<string, object>
    {
        { "Population", newPopulation}
    };
    transaction.Update(cityRef, updates);
});
אוֹדֶם
city_ref = firestore.doc "#{collection_path}/SF"

firestore.transaction do |tx|
  new_population = tx.get(city_ref).data[:population] + 1
  puts "New population is #{new_population}."
  tx.update city_ref, { population: new_population }
end

העברת מידע מתוך עסקאות

אל תשנה את מצב היישום בתוך פונקציות העסקה שלך. פעולה זו תציג בעיות במקביל, מכיוון שפונקציות טרנזקציות יכולות לפעול מספר פעמים ולא מובטחת לפעול על שרשור ממשק המשתמש. במקום זאת, העבר מידע שאתה צריך מתוך פונקציות העסקאות שלך. הדוגמה הבאה מתבססת על הדוגמה הקודמת כדי להראות כיצד להעביר מידע מעסקה:

גרסת אינטרנט 9

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

גרסת אינטרנט 8

// 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);
});
מָהִיר
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
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!)")
    }
}
Objective-C
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
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);
  }
}];

Java

final DocumentReference sfDocRef = db.collection("cities").document("SF");

db.runTransaction(new Transaction.Function<Double>() {
    @Override
    public Double apply(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);
    }
});

קוטלין+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 docRef = db.collection("cities").document("SF");
ApiFuture<String> futureTransaction = db.runTransaction(transaction -> {
  DocumentSnapshot snapshot = transaction.get(docRef).get();
  Long newPopulation = snapshot.getLong("population") + 1;
  // conditionally update based on current population
  if (newPopulation <= 1000000L) {
    transaction.update(docRef, "population", newPopulation);
    return "Population increased to " + newPopulation;
  } else {
    throw new Exception("Sorry! Population is too big.");
  }
});
// Print information retrieved from transaction
System.out.println(futureTransaction.get());
פִּיתוֹן
transaction = db.transaction()
city_ref = db.collection(u'cities').document(u'SF')

@firestore.transactional
def update_in_transaction(transaction, city_ref):
    snapshot = city_ref.get(transaction=transaction)
    new_population = snapshot.get(u'population') + 1

    if new_population < 1000000:
        transaction.update(city_ref, {
            u'population': new_population
        })
        return True
    else:
        return False

result = update_in_transaction(transaction, city_ref)
if result:
    print(u'Population updated')
else:
    print(u'Sorry! Population is too big.')

פִּיתוֹן

transaction = db.transaction()
city_ref = db.collection("cities").document("SF")

@firestore.async_transactional
async def update_in_transaction(transaction, city_ref):
    snapshot = await city_ref.get(transaction=transaction)
    new_population = snapshot.get("population") + 1

    if new_population < 1000000:
        transaction.update(city_ref, {"population": new_population})
        return True
    else:
        return False

result = await update_in_transaction(transaction, city_ref)
if result:
    print("Population updated")
else:
    print("Sorry! Population is too big.")
C++
// This is not yet supported.
Node.js
const cityRef = db.collection('cities').doc('SF');
try {
  const res = await db.runTransaction(async t => {
    const doc = await t.get(cityRef);
    const newPopulation = doc.data().population + 1;
    if (newPopulation <= 1000000) {
      await t.update(cityRef, { population: newPopulation });
      return `Population increased to ${newPopulation}`;
    } else {
      throw 'Sorry! Population is too big.';
    }
  });
  console.log('Transaction success', res);
} catch (e) {
  console.log('Transaction failure:', e);
}
ללכת
ref := client.Collection("cities").Doc("SF")
err := client.RunTransaction(ctx, func(ctx context.Context, tx *firestore.Transaction) error {
	doc, err := tx.Get(ref)
	if err != nil {
		return err
	}
	pop, err := doc.DataAt("population")
	if err != nil {
		return err
	}
	newpop := pop.(int64) + 1
	if newpop <= 1000000 {
		return tx.Set(ref, map[string]interface{}{
			"population": pop.(int64) + 1,
		}, firestore.MergeAll)
	}
	return errors.New("population is too big")
})
if err != nil {
	// Handle any errors in an appropriate way, such as returning them.
	log.Printf("An error has occurred: %s", err)
}
PHP
$cityRef = $db->collection('samples/php/cities')->document('SF');
$transactionResult = $db->runTransaction(function (Transaction $transaction) use ($cityRef) {
    $snapshot = $transaction->snapshot($cityRef);
    $newPopulation = $snapshot['population'] + 1;
    if ($newPopulation <= 1000000) {
        $transaction->update($cityRef, [
            ['path' => 'population', 'value' => $newPopulation]
        ]);
        return true;
    } else {
        return false;
    }
});

if ($transactionResult) {
    printf('Population updated successfully.' . PHP_EOL);
} else {
    printf('Sorry! Population is too big.' . PHP_EOL);
}
אַחְדוּת
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#
DocumentReference cityRef = db.Collection("cities").Document("SF");
bool transactionResult = await db.RunTransactionAsync(async transaction =>
{
    DocumentSnapshot snapshot = await transaction.GetSnapshotAsync(cityRef);
    long newPopulation = snapshot.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;
    }
});

if (transactionResult)
{
    Console.WriteLine("Population updated successfully.");
}
else
{
    Console.WriteLine("Sorry! Population is too big.");
}
אוֹדֶם
city_ref = firestore.doc "#{collection_path}/SF"

updated = firestore.transaction do |tx|
  new_population = tx.get(city_ref).data[:population] + 1
  if new_population < 1_000_000
    tx.update city_ref, { population: new_population }
    true
  end
end

if updated
  puts "Population updated!"
else
  puts "Sorry! Population is too big."
end

כשל בעסקה

עסקה עלולה להיכשל מהסיבות הבאות:

  • העסקה מכילה פעולות קריאה לאחר פעולות כתיבה. פעולות קריאה חייבות לבוא תמיד לפני כל פעולות כתיבה.
  • העסקה קראה מסמך ששונה מחוץ לעסקה. במקרה זה, העסקה פועלת שוב אוטומטית. העסקה מנוסה שוב מספר סופי של פעמים.
  • העסקה חרגה מגודל הבקשה המקסימלי של 10 MiB.

    גודל העסקה תלוי בגדלים של מסמכים וערכים באינדקס ששונו על ידי העסקה. עבור פעולת מחיקה, זה כולל את גודל מסמך היעד ואת הגדלים של ערכי האינדקס שנמחקו בתגובה לפעולה.

עסקה שנכשלה מחזירה שגיאה ואינה כותבת דבר למסד הנתונים. אינך צריך להחזיר את העסקה לאחור; Cloud Firestore עושה זאת באופן אוטומטי.

כותב אצווה

אם אתה לא צריך לקרוא את כל המסמכים סט הפעולה שלך, אתה יכול לבצע פעולות כתיבה מרובות כקבוצה אחת המכילה שילוב כלשהו של set() , update() , או delete() פעולות. אצווה של כתיבה מסתיימת מבחינה אטומית ויכולה לכתוב למספר מסמכים. הדוגמה הבאה מראה כיצד לבנות ולבצע אצווה כתיבה:

גרסת אינטרנט 9

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

גרסת אינטרנט 8

// 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(() => {
    // ...
});
מָהִיר
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
// 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.")
    }
}
Objective-C
הערה: מוצר זה אינו זמין על מטרות קליפ watchOS ו App.
// 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.");
  }
}];

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) {
        // ...
    }
});

קוטלין+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);

// asynchronously commit the batch
ApiFuture<List<WriteResult>> future = batch.commit();
// ...
// future.get() blocks on batch commit operation
for (WriteResult result : future.get()) {
  System.out.println("Update time : " + result.getUpdateTime());
}
פִּיתוֹן
batch = db.batch()

# Set the data for NYC
nyc_ref = db.collection(u'cities').document(u'NYC')
batch.set(nyc_ref, {u'name': u'New York City'})

# Update the population for SF
sf_ref = db.collection(u'cities').document(u'SF')
batch.update(sf_ref, {u'population': 1000000})

# Delete DEN
den_ref = db.collection(u'cities').document(u'DEN')
batch.delete(den_ref)

# Commit the batch
batch.commit()

פִּיתוֹן

batch = db.batch()

# Set the data for NYC
nyc_ref = db.collection("cities").document("NYC")
batch.set(nyc_ref, {"name": "New York City"})

# Update the population for SF
sf_ref = db.collection("cities").document("SF")
batch.update(sf_ref, {"population": 1000000})

# Delete DEN
den_ref = db.collection("cities").document("DEN")
batch.delete(den_ref)

# Commit the batch
await batch.commit()
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
// Get a new write batch
const batch = db.batch();

// Set the value of 'NYC'
const nycRef = db.collection('cities').doc('NYC');
batch.set(nycRef, {name: 'New York City'});

// Update the population of 'SF'
const sfRef = db.collection('cities').doc('SF');
batch.update(sfRef, {population: 1000000});

// Delete the city 'LA'
const laRef = db.collection('cities').doc('LA');
batch.delete(laRef);

// Commit the batch
await batch.commit();
ללכת
// Get a new write batch.
batch := client.Batch()

// Set the value of "NYC".
nycRef := client.Collection("cities").Doc("NYC")
batch.Set(nycRef, map[string]interface{}{
	"name": "New York City",
})

// Update the population of "SF".
sfRef := client.Collection("cities").Doc("SF")
batch.Set(sfRef, map[string]interface{}{
	"population": 1000000,
}, firestore.MergeAll)

// Delete the city "LA".
laRef := client.Collection("cities").Doc("LA")
batch.Delete(laRef)

// Commit the batch.
_, err := batch.Commit(ctx)
if err != nil {
	// Handle any errors in an appropriate way, such as returning them.
	log.Printf("An error has occurred: %s", err)
}
PHP
$batch = $db->batch();

# Set the data for NYC
$nycRef = $db->collection('samples/php/cities')->document('NYC');
$batch->set($nycRef, [
    'name' => 'New York City'
]);

# Update the population for SF
$sfRef = $db->collection('samples/php/cities')->document('SF');
$batch->update($sfRef, [
    ['path' => 'population', 'value' => 1000000]
]);

# Delete LA
$laRef = $db->collection('samples/php/cities')->document('LA');
$batch->delete($laRef);

# Commit the batch
$batch->commit();
אַחְדוּת
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#
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
await batch.CommitAsync();
אוֹדֶם
firestore.batch do |b|
  # Set the data for NYC
  b.set "#{collection_path}/NYC", { name: "New York City" }

  # Update the population for SF
  b.update "#{collection_path}/SF", { population: 1_000_000 }

  # Delete LA
  b.delete "#{collection_path}/LA"
end

כתיבה אצווה יכולה להכיל עד 500 פעולות. כל פעולה באצווה נספרת בנפרד עבור השימוש שלך ב-Cloud Firestore.

כמו עסקאות, כתיבה אצווה היא אטומית. שלא כמו עסקאות, כתיבה אצווה אינה צריכה להבטיח שמסמכים שנקראו יישארו ללא שינוי, מה שמוביל לפחות מקרי כשל. הם אינם כפופים לניסיונות חוזרים או לכשלים מהרבה מדי ניסיונות חוזרים. כתיבה אצווה מתבצעת גם כשהמכשיר של המשתמש במצב לא מקוון.

אימות נתונים עבור פעולות אטומיות

על ספריות לקוח אינטרנט / ניידים, אתה יכול לאמת את הנתונים באמצעות ענן Firestore כללי אבטחת . אתה יכול להבטיח שמסמכים קשורים תמיד מעודכנים מבחינה אטומית ותמיד כחלק מעסקה או כתיבה אצווה. השתמש getAfter() פונקצית כלל ביטחון הגישה ולאמת את מצב מסמך לאחר סדרה של פעולות משלימות אך לפני קלאוד 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();