แก้ไขข้อมูลด้วยการดำเนินการไปป์ไลน์ Firestore

ใช้ขั้นตอนภาษาในการจัดการข้อมูล (DML) update(...) และ delete(...) เพื่อสร้าง Data Pipeline ที่สามารถค้นหาเอกสาร แล้วลบหรือแก้ไขข้อมูล

ข้อกำหนดของรุ่น

การดำเนินการที่อธิบายไว้ในหน้านี้ต้องใช้ Firestore Enterprise Edition

ก่อนเริ่มต้น

คุณควรคุ้นเคยกับวิธีค้นหาฐานข้อมูลด้วยการดำเนินการไปป์ไลน์

อัปเดตเอกสาร

ใช้ระยะ DML update(...) เพื่อสร้าง Data Pipeline ที่สามารถค้นหาเอกสาร แล้วเพิ่มหรือแก้ไขข้อมูล

ระยะ 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();
Python
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()
)
Java
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();
Go
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)

ลบเอกสาร

ใช้ระยะ DML delete(...) เพื่อสร้าง Data Pipeline ที่สามารถค้นหาเอกสารแล้วลบข้อมูล เพื่อป้องกันการลบข้อมูลจำนวนมากโดยไม่ตั้งใจ ไปป์ไลน์ที่ลงท้ายด้วย delete(...) ควรมีระยะ where(...) อย่างน้อย 1 ระยะ ระยะ DML ทั้งหมดต้องอยู่ที่ส่วนท้ายของไปป์ไลน์

ตัวอย่างเช่น ไปป์ไลน์ต่อไปนี้จะลบเอกสาร users ทั้งหมดที่มี address.users ตั้งค่าเป็น USA และมี __create_time__ น้อยกว่า 10 วัน

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();
Python
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()
)
Java
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();
Go
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 ทำงานนอกธุรกรรมที่ชัดเจน ให้ทำดังนี้

  • การอัปเดตแบบอิสระและความสำเร็จบางส่วน: เอกสารแต่ละฉบับจะได้รับการอัปเดตแยกกัน และการดำเนินการจะไม่เป็นแบบอะตอมมิกในเอกสารต่างๆ ไปป์ไลน์จะหยุดทำงานเมื่อพบข้อผิดพลาดแรก และเอกสารที่แก้ไขก่อนหน้านี้จะยังคงเขียนลงในฐานข้อมูล ซึ่งช่วยให้ดำเนินการสำเร็จบางส่วนได้
  • การรองรับสเตจ
    • ระบบไม่รองรับระยะขั้นสูงและระยะแบบหลายการค้นหา (เช่น aggregate(...), distinct(...), unnest(...), find_nearest(...), union(...), การรวม และการค้นหาย่อย) ก่อน DML นอกธุรกรรม ใช้ธุรกรรมหากไปป์ไลน์ของคุณต้องมีขั้นตอนเหล่านี้
  • ขีดจำกัดการค้นหา 20 ครั้งของกฎความปลอดภัย: เอกสารแต่ละชุดจะดำเนินการในธุรกรรมของตนเองและประเมินกฎความปลอดภัยอีกครั้ง หากกฎความปลอดภัยใช้การค้นหา get() หรือ exists() แต่ละกลุ่มจะทำการค้นหา โดยจะถึงขีดจำกัดการค้นหา 20 ครั้งต่อคำขอหลังจากเขียนเอกสารประมาณ 200 รายการและไม่สำเร็จด้วย PERMISSION_DENIED ใช้การอ้างสิทธิ์ที่กำหนดเองแทนการค้นหา get() หรือ exists() เพื่อหลีกเลี่ยงขีดจำกัดนี้

ข้อจำกัด

โปรดทราบข้อจำกัดต่อไปนี้สำหรับระยะ DML

  • ระยะ DML ต้องเป็นระยะสุดท้ายในคำจำกัดความไปป์ไลน์ก่อนเรียกใช้ .execute()
  • เมื่อเรียกใช้นอกธุรกรรม เอกสารแต่ละรายการจะอัปเดตแยกกันและอาจทำให้เกิดความสำเร็จบางส่วน
  • การดำเนินการ DML ที่ไม่ใช่ธุรกรรมซึ่งอยู่ภายใต้กฎความปลอดภัยที่มีการค้นหา get() หรือ exists() จะถูกจำกัดไว้ที่เอกสารประมาณ 200 รายการเนื่องจากขีดจำกัดการค้นหา 20 รายการ
  • การดำเนินการ DML แบบธุรกรรมจะถูกจำกัดโดยขีดจำกัดธุรกรรมของ Firestore Enterprise Edition เนื่องจากธุรกรรม Firestore ไม่อนุญาตให้อ่านหลังจากเขียนครั้งแรก คุณจึงไม่สามารถเรียกใช้ไปป์ไลน์ DML หลายครั้งภายในธุรกรรมเดียว และไม่สามารถอ่านเอกสารหลังจากเรียกใช้ไปป์ไลน์ DML ได้ การพยายามดำเนินการอ่านหลังจากเขียน DML ไม่สำเร็จพร้อมข้อผิดพลาด FAILED_PRECONDITION: Cannot have reads after the first write in a Firestore transaction.
  • หากขั้นตอนก่อนหน้าขั้นตอน DML สร้างเอกสารหลายรายการที่มี __name__ เดียวกันหรือประเมินเป็นรหัสเอกสารปลายทางเดียวกัน ระบบจะประมวลผลการเปลี่ยนแปลงแต่ละรายการตามลำดับ ดังนี้
    • สำหรับ upsert(...) และ update(...): การเปลี่ยนแปลงหลายรายการที่กำหนดเป้าหมายไปยังเอกสารเดียวกันจะเป็นไปตามความหมาย "รายการสุดท้ายชนะ" โดยการเปลี่ยนแปลงที่ประมวลผลล่าสุดจะเป็นตัวกำหนดสถานะสุดท้ายของเอกสาร
    • สำหรับ delete(...): การพยายามครั้งแรกจะลบเอกสาร และการพยายามครั้งต่อๆ ไปสำหรับเอกสารเดียวกันจะไม่ดำเนินการใดๆ
    • สำหรับ insert(...): การพยายามครั้งแรกจะสร้างเอกสาร และการพยายามครั้งต่อๆ ไปที่กำหนดเป้าหมายรหัสเอกสารเดียวกันจะล้มเหลวพร้อมข้อผิดพลาด ALREADY_EXISTS