اصلاح داده‌ها با عملیات Firestore Pipeline

از مراحل update(...) و delete(...) «زبان دستکاری داده» (DML) برای ساختن خطوط لوله داده استفاده کنید که می‌توانند اسناد را جستجو کنند و سپس داده‌ها را حذف یا اصلاح کنند.

الزامات نسخه

عملیات شرح‌داده‌شده در این صفحه به نسخه Firestore Enterprise نیاز دارد.

قبل از شروع

باید با نحوه پرس‌وجو از پایگاه داده با عملیات Pipeline آشنا باشید.

به‌روزرسانی اسناد

از مرحله update(...) DML برای ساختن خطوط لوله داده استفاده کنید که می‌تواند برای اسناد پُرسمان کند و سپس داده‌ها را اضافه یا اصلاح کند.

همه مراحل DML باید در انتهای کانال ارتباطی قرار بگیرند. اسنادی که به این مرحله می‌رسند باید فیلد __name__ را داشته باشند تا مشخص شود کدام اسناد باید به‌روز شوند. اگر هرکدام از اسنادی که می‌خواهید به‌روز کنید وجود نداشته باشد، عملیات ناموفق خواهد بود.

برای مثال، عملیات زیر تغییر مدل داده را در همه اسناد موجود در گروه مجموعه اعمال می‌کند. خط لوله فیلد preferences.color را به همه اسناد موجود در گروه مجموعه users که فاقد این فیلد هستند اضافه می‌کند.

Node.js
const snapshot = await db.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant(null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute();
پایتون
from google.cloud.firestore_v1.pipeline_expressions import Constant, Field, Not

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Not(Field.of("preferences.color").exists()))
    .add_fields(Constant.of(None).as_("preferences.color"))
    .remove_fields("color")
    .update()
    .execute()
)
جاوا
Pipeline.Snapshot snapshot = firestore.pipeline()
   .collectionGroup("users")
   .where(not(exists(field("preferences.color"))))
   .addFields(constant((String) null).as("preferences.color"))
   .removeFields("color")
   .update()
   .execute().get();
رفتن
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.Not(firestore.FieldExists(firestore.FieldOf("preferences.color")))).
	AddFields(firestore.Selectables(
		firestore.ConstantOfNull().As("preferences.color"),
	)).
	RemoveFields(firestore.Fields("color")).
	Update().
	Execute(ctx)

حذف کردن اسناد

از مرحله delete(...) DML برای ساختن خطوط لوله داده استفاده کنید که می‌تواند برای اسناد پُرسمان کند و سپس داده‌ها را حذف کند. برای جلوگیری از حذف انبوه تصادفی، خطوط لوله‌ای که به delete(...) ختم می‌شوند باید حداقل یک مرحله where(...) داشته باشند. همه مراحل DML باید در انتهای کانال ارتباطی قرار بگیرند.

برای مثال، خط لوله زیر همه اسناد users را که address.users روی USA تنظیم شده است و __create_time__ کمتر از ۱۰ روز دارد حذف می‌کند:

Node.js
const pipeline = db.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").timestampAdd("day", 10).lessThan(currentTimestamp()))
  .delete();
await pipeline.execute();
پایتون
from google.cloud.firestore_v1.pipeline_expressions import CurrentTimestamp, Field

snapshot = (
    client.pipeline()
    .collection_group("users")
    .where(Field.of("address.country").equal("USA"))
    .where(
        Field.of("__create_time__")
        .timestamp_add("day", 10)
        .less_than(CurrentTimestamp())
    )
    .delete()
    .execute()
)
جاوا
Pipeline.Snapshot deleteResults = firestore.pipeline()
  .collectionGroup("users")
  .where(field("address.country").equal("USA"))
  .where(field("__create_time__").add(constant(10)).lessThan(currentTimestamp()))
  .delete()
  .execute().get();
رفتن
snapshot := client.Pipeline().
	CollectionGroup("users").
	Where(firestore.FieldOf("address.country").Equal("USA")).
	Where(firestore.FieldOf("__create_time__").Add(firestore.ConstantOf(10)).LessThan(firestore.CurrentTimestamp())).
	Delete().
	Execute(ctx)

حالت‌های اجرا و هماهنگی

مراحل DML را می‌توان در داخل یک تراکنش صریح یا خارج از یک تراکنش اجرا کرد:

اجرای تراکنشی

DML تراکنشی سازگاری اتمی و همه‌یاهیچ را در سراسر اسناد فراهم می‌کند و از مجموعه کامل مراحل پُرسمان خط لوله پشتیبانی می‌کند. این امر آن را برای حجم‌های کاری که به تضمین‌های سازگاری دقیق نیاز دارند (جایی که به‌روزرسانی‌های جزئی غیرقابل‌قبول هستند) یا به مراحل پیچیده پُرسمان متکی هستند ایده‌آل می‌کند، مشروط بر اینکه تغییرات در محدوده اندازه تراکنش قرار گیرند.

اجرای خط لوله DML در یک تراکنش صریح (بااستفاده از runTransaction / transaction.execute(pipeline) یا گزینه atomic: true) رفتار زیر را دارد:

  • همه جهش‌ها یا باهم ثبت می‌شوند یا درصورت ناموفق بودن کاملاً برگردانده می‌شوند.
  • همه مراحل پُرسمان خط لوله قبل‌از مرحله DML پشتیبانی می‌شوند (ازجمله زیرپُرسمان‌های where، add_fields، union، find_nearest، و aggregate).
  • در تراکنش، همه عملیات خواندن باید قبل‌از هرگونه عملیات نوشتن اجرا شود. چون خط لوله DML نوشتن را انجام می‌دهد، نمی‌توانید خواندن را پس‌از اجرای خط لوله DML در همان تراکنش اجرا کنید. برای مثال، نمی‌توانید یک خط لوله DML را چندین بار در یک تراکنش فراخوانی کنید، و همچنین نمی‌توانید اسناد را پس‌از اجرای خط لوله DML بخوانید. تلاش برای اجرای خواندن پس‌از اجرای خط لوله DML با خطای FAILED_PRECONDITION ناموفق می‌شود: Cannot have reads after the first write in a Firestore transaction.
  • این عملیات مشمول محدودیت‌های تراکنش نسخه Firestore Enterprise است.

اجرای غیرتراکنشی

DML غیرتراکنشی برای اصلاحات انبوه در تعداد زیادی سند بدون مواجه شدن با محدودیت‌های اندازه تراکنش طراحی شده است. با قفل کردن در سطح سند به‌جای کل عملیات، به سایر عملیات هم‌زمان اجازه می‌دهد تا درحین به‌روزرسانی اسناد در مقیاس بزرگ ادامه یابند.

وقتی مراحل DML خارج از تراکنش صریح اجرا می‌شود:

  • به‌روزرسانی‌های مستقل و موفقیت جزئی: هر سند به‌طور مستقل از سندهای دیگر به‌روزرسانی می‌شود و عملیات در سندها اتمی نیست. خط لوله در اولین خطا متوقف می‌شود و اسنادی که قبلاً اصلاح شده‌اند در پایگاه داده نوشته‌شده باقی می‌مانند که این امر امکان موفقیت جزئی را فراهم می‌کند.
  • پشتیبانی از Stage:
    • مراحل پیشرفته و چندپرسشی (مانند aggregate(...)، distinct(...)، unnest(...)، find_nearest(...)، union(...)، پیوندها، و زیرپرسش‌ها) قبل‌از DML خارج از تراکنش پشتیبانی نمی‌شوند. اگر خط لوله شما به این مراحل نیاز دارد، از تراکنش استفاده کنید.
  • محدودیت ۲۰ جستجوی «قوانین امنیتی»: هر دسته از اسناد در تراکنش خودش اجرا می‌شود و قوانین امنیتی را دوباره ارزیابی می‌کند. اگر قوانین امنیتی شما از جستجوهای get() یا exists() استفاده می‌کنند، هر دسته یک جستجو را اجرا می‌کند و پس‌از نوشتن تقریباً ۲۰۰ سند، به حد مجاز ۲۰ جستجو برای هر درخواست می‌رسد و با PERMISSION_DENIED ناموفق می‌شود. برای اجتناب از این محدودیت، به‌جای جستجوی get() یا exists()، از ادعاهای سفارشی استفاده کنید.

محدودیت‌ها

به محدودیت‌های زیر برای مراحل DML توجه کنید:

  • مراحل DML باید آخرین مرحله در تعریف خط لوله قبل‌از فراخوانی .execute() باشد.
  • وقتی خارج از تراکنش اجرا شود، هر سند مستقل از دیگری به‌روز می‌شود و امکان موفقیت جزئی وجود دارد.
  • عملیات DML غیرتراکنشی مشمول «قوانین امنیتی» با جستجوی get() یا exists() به‌دلیل محدودیت ۲۰ جستجو، به حدود ۲۰۰ سند محدود می‌شود.
  • عملیات DML تراکنشی با محدودیت‌های تراکنش نسخه Firestore Enterprise محدود شده است. Because Firestore transactions don't allow reads after the first write, you cannot call a DML pipeline multiple times within a single transaction, nor can you read documents after executing a DML pipeline. تلاش برای اجرای خواندن پس‌از ناموفق بودن نوشتن DML با خطای FAILED_PRECONDITION: Cannot have reads after the first write in a Firestore transaction.
  • اگر مرحله قبل‌از مرحله DML چندین سند با __name__ یکسان تولید کند یا به شناسه سند مقصد یکسانی ارزیابی شود، هر جهش به‌صورت متوالی پردازش می‌شود:
    • برای upsert(...) و update(...): جهش‌های متعدد که سند یکسانی را هدف قرار می‌دهند از معناشناسی «آخرین برنده است» پیروی می‌کنند، که در آن آخرین جهش پردازش‌شده وضعیت نهایی سند را تعیین می‌کند.
    • برای delete(...): تلاش اول سند را حذف می‌کند و تلاش‌های بعدی برای همان سند بی‌تأثیر است.
    • برای insert(...): تلاش اول سند را ایجاد می‌کند و تلاش‌های بعدی که همان شناسه سند را هدف قرار می‌دهند با خطای ALREADY_EXISTS ناموفق می‌شوند.