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

ข้อมูลเบื้องต้น

การดำเนินการไปป์ไลน์มีอินเทอร์เฟซการค้นหาใหม่สำหรับ Cloud Firestore ซึ่งรองรับฟังก์ชันการค้นหาขั้นสูงและนิพจน์ที่ซับซ้อน โดยจะเปิดตัวฟังก์ชันใหม่ๆ มากมาย เช่น min(...), max(...), substring(...), regex_match(...) และ array_contains_all(...) รวมถึงขั้นตอนต่างๆ เพื่อให้สามารถทำการเปลี่ยนรูปแบบที่ซับซ้อนได้

เริ่มต้นใช้งาน

หากต้องการติดตั้งและเริ่มต้นใช้งาน SDK ของไคลเอ็นต์ โปรดดูวิธีการในคู่มือต่อไปนี้

ไวยากรณ์

ส่วนต่อไปนี้จะแสดงภาพรวมของไวยากรณ์สำหรับการดำเนินการไปป์ไลน์

แนวคิด

ความแตกต่างที่เห็นได้ชัดอย่างหนึ่งของการดำเนินการไปป์ไลน์คือการเปิดตัวการจัดเรียง "ระยะ" ที่ชัดเจน ซึ่งช่วยให้แสดงการค้นหาที่ซับซ้อนมากขึ้นได้ อย่างไรก็ตาม การดำเนินการนี้แตกต่างอย่างเห็นได้ชัดจากอินเทอร์เฟซการค้นหาที่มีอยู่ซึ่งใช้การดำเนินการหลักที่ระบุลำดับของระยะต่างๆ ไว้โดยนัย ลองพิจารณาตัวอย่างการดำเนินการของไปป์ไลน์ต่อไปนี้

Web

const pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort(field("name").ascending())
  // Step 4: Return the top 10. Note applying the limit earlier in the
  // pipeline would have unintentional results.
  .limit(10);
Swift
let pipeline = db.pipeline()
  // Step 1: Start a query with collection scope
  .collection("cities")
  // Step 2: Filter the collection
  .where(Field("population").greaterThan(100000))
  // Step 3: Sort the remaining documents
  .sort([Field("name").ascending()])
  // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
  // unintentional results.
  .limit(10)

Kotlin

val pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10)

Java

Pipeline pipeline = db.pipeline()
    // Step 1: Start a query with collection scope
    .collection("cities")
    // Step 2: Filter the collection
    .where(field("population").greaterThan(100000))
    // Step 3: Sort the remaining documents
    .sort(field("name").ascending())
    // Step 4: Return the top 10. Note applying the limit earlier in the pipeline would have
    // unintentional results.
    .limit(10);
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than(100_000))
    .sort(Field.of("name").ascending())
    .limit(10)
)
Go
pipeline := client.Pipeline().
	Collection("cities").
	Where(firestore.FieldOf("population").GreaterThan(100000)).
	Sort(firestore.Orders(firestore.Ascending(firestore.FieldOf("name")))).
	Limit(10)

การเริ่มต้น

การดำเนินการไปป์ไลน์มีไวยากรณ์ที่คุ้นเคยอย่างมากซึ่งมาจากคำค้นหา Cloud Firestore ที่มีอยู่ ในการเริ่มต้นใช้งาน คุณจะเริ่มต้นการค้นหาโดยเขียนโค้ดต่อไปนี้

Web

const { getFirestore } = require("firebase/firestore");
const { execute } = require("firebase/firestore/pipelines");
const database = getFirestore(app, "enterprise");
const pipeline = database.pipeline();
Swift
let firestore = Firestore.firestore(database: "enterprise")
let pipeline = firestore.pipeline()

Kotlin

val firestore = Firebase.firestore("enterprise")
val pipeline = firestore.pipeline()

Java

FirebaseFirestore firestore = FirebaseFirestore.getInstance("enterprise");
PipelineSource pipeline = firestore.pipeline();
Python
firestore_client = firestore.client(default_app, "your-new-enterprise-database")
pipeline = firestore_client.pipeline()
Go
client, err := firestore.NewClientWithDatabase(ctx, projectID, databaseID)
if err != nil {
	fmt.Fprintf(w, "firestore.NewClientWithDatabase failed: %v", err)
	return err
}
defer client.Close()
pipeline := client.Pipeline().Collection("books")

โครงสร้าง

มีคำศัพท์บางคำที่สำคัญต่อการทำความเข้าใจเมื่อสร้างการดำเนินการไปป์ไลน์ ได้แก่ ระยะ นิพจน์ และฟังก์ชัน และ Wrapper การสืบค้นย่อย

ตัวอย่างที่แสดงระยะและนิพจน์ในการค้นหา

ระยะ: ไปป์ไลน์อาจประกอบด้วยระยะอย่างน้อย 1 ระยะ ในทางตรรกะแล้ว ขั้นตอนเหล่านี้แสดงถึงชุดขั้นตอน (หรือระยะ) ที่ใช้ในการดำเนินการค้นหา

นิพจน์: ขั้นตอนมักจะยอมรับนิพจน์ที่ช่วยให้คุณแสดงคำค้นหาที่ซับซ้อนมากขึ้นได้ นิพจน์อาจเป็นนิพจน์แบบง่ายและประกอบด้วยฟังก์ชันเดียว เช่น eq("a", 1) นอกจากนี้ คุณยังแสดงนิพจน์ที่ซับซ้อนมากขึ้นได้โดยการฝังนิพจน์ เช่น and(eq("a", 1), eq("b", 2)).

Wrapper ของการค้นหาย่อย: ฟังก์ชันต่างๆ เช่น array() และ scalar() ช่วยให้คุณฝังไปป์ไลน์ที่ฝังเป็นนิพจน์ภายในระยะได้

ฟิลด์ / ค่าคงที่ / ตัวแปร

การดำเนินการไปป์ไลน์รองรับนิพจน์ที่ซับซ้อน ดังนั้นจึงควรแยกแยะว่าค่าหนึ่งๆ แสดงถึงฟิลด์ ค่าคงที่ หรือตัวแปร

แม้ว่าฟิลด์จะหมายถึงข้อมูลภายในเอกสาร และค่าคงที่จะช่วยให้ระบุค่าใดก็ได้เป็นอาร์กิวเมนต์ของนิพจน์ แต่ตัวแปรจะช่วยให้กำหนดและใช้ค่าชั่วคราวที่กำหนดขอบเขตไว้สำหรับการดำเนินการค้นหาแทนเอกสารที่กำลังประมวลผล ต่อไปนี้เป็นภาพรวมของแนวคิดเหล่านี้ โปรดดูข้อมูลเพิ่มเติมเกี่ยวกับวิธีอ่านและเขียนตัวแปรระหว่างการดำเนินการค้นหาในระยะ let(...)

ช่อง ค่าคงที่ ตัวแปร
Purpose เข้าถึงหรือจัดเก็บฟิลด์ในเอกสาร ระบุมูลค่าคงที่ ใช้ค่าชั่วคราวระหว่างการดำเนินการไปป์ไลน์
การใช้งาน SDK field("name") constant("val") variable("name")
ขอบเขต ในเครื่องไปยังเอกสารปัจจุบัน ทั่วโลก ส่วนกลางไปยังไปป์ไลน์และไปป์ไลน์ย่อย
ข้อมูลอ้างอิงที่ไม่ได้กำหนด ประเมินเป็น absent ไม่มี สร้างข้อผิดพลาดขณะรันไทม์

ตัวอย่างเช่น

Web

const pipeline = db.pipeline()
  .collection("cities")
  .where(field("name").equal(constant("Toronto")));
Swift
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("name").equal(Constant("Toronto")))

Kotlin

val pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")))

Java

Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("name").equal(constant("Toronto")));
Python
from google.cloud.firestore_v1.pipeline_expressions import Field, Constant

pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("name").equal(Constant.of("Toronto")))
)
Go
pipeline := client.Pipeline().Collection("cities").
	Where(firestore.FieldOf("name").Equal(firestore.ConstantOf("Toronto")))

บริการจัดเตรียมเวที

ขั้นตอนการป้อนข้อมูล

ระยะอินพุตแสดงถึงระยะแรกของการค้นหา โดยจะกำหนดชุดเอกสารเริ่มต้นที่คุณค้นหา สำหรับการดำเนินการไปป์ไลน์ การดำเนินการนี้จะคล้ายกับการค้นหาที่มีอยู่เป็นส่วนใหญ่ โดยการค้นหาส่วนใหญ่จะเริ่มต้นด้วยระยะ collection(...) หรือ collection_group(...) ขั้นตอนการป้อนข้อมูลใหม่ 2 ขั้นตอนคือ database() และ documents(...) โดยที่ database() อนุญาตให้แสดงผลเอกสารทั้งหมดในฐานข้อมูล ขณะที่ documents(...) ทำงานเหมือนกับการอ่านแบบกลุ่ม

Web

let results;

// Return all restaurants in San Francisco
results = await execute(db.pipeline().collection("cities/sf/restaurants"));

// Return all restaurants
results = await execute(db.pipeline().collectionGroup("restaurants"));

// Return all documents across all collections in the database (the entire database)
results = await execute(db.pipeline().database());

// Batch read of 3 documents
results = await execute(db.pipeline().documents([
  doc(db, "cities", "SF"),
  doc(db, "cities", "DC"),
  doc(db, "cities", "NY")
]));
Swift
var results: Pipeline.Snapshot

// Return all restaurants in San Francisco
results = try await db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = try await db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = try await db.pipeline().database().execute()

// Batch read of 3 documents
results = try await db.pipeline().documents([
  db.collection("cities").document("SF"),
  db.collection("cities").document("DC"),
  db.collection("cities").document("NY")
]).execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute()

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute()

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute()

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute()

Java

Task<Pipeline.Snapshot> results;

// Return all restaurants in San Francisco
results = db.pipeline().collection("cities/sf/restaurants").execute();

// Return all restaurants
results = db.pipeline().collectionGroup("restaurants").execute();

// Return all documents across all collections in the database (the entire database)
results = db.pipeline().database().execute();

// Batch read of 3 documents
results = db.pipeline().documents(
    db.collection("cities").document("SF"),
    db.collection("cities").document("DC"),
    db.collection("cities").document("NY")
).execute();
Python
# Return all restaurants in San Francisco
results = client.pipeline().collection("cities/sf/restaurants").execute()

# Return all restaurants
results = client.pipeline().collection_group("restaurants").execute()

# Return all documents across all collections in the database (the entire database)
results = client.pipeline().database().execute()

# Batch read of 3 documents
results = (
    client.pipeline()
    .documents(
        client.collection("cities").document("SF"),
        client.collection("cities").document("DC"),
        client.collection("cities").document("NY"),
    )
    .execute()
)
Go
// Return all restaurants in San Francisco
results1, err := client.Pipeline().Collection("cities/sf/restaurants").Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

// Return all restaurants
results2, err := client.Pipeline().CollectionGroup("restaurants").Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

// Return all documents across all collections in the database (the entire database)
results3, err := client.Pipeline().Database().Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

// Batch read of 3 documents
results4, err := client.Pipeline().
	Documents([]*firestore.DocumentRef{
		client.Collection("cities").Doc("SF"),
		client.Collection("cities").Doc("DC"),
		client.Collection("cities").Doc("NY"),
	}).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

เช่นเดียวกับระยะอื่นๆ ทั้งหมด ลำดับของผลลัพธ์จากระยะอินพุตเหล่านี้ไม่เสถียร ควรเพิ่มโอเปอเรเตอร์ sort(...) เสมอหากต้องมีการจัดลำดับที่เฉพาะเจาะจง

สถานที่

ระยะ where(...) ทำหน้าที่เป็นการดำเนินการกรองมาตรฐานเหนือเอกสารที่สร้างจากระยะก่อนหน้า และส่วนใหญ่จะสะท้อนไวยากรณ์ "where" ที่มีอยู่สำหรับการค้นหาที่มีอยู่ ระบบจะกรองเอกสารที่นิพจน์ที่ระบุประเมินเป็นค่าที่ไม่ใช่ true ออกจากเอกสารที่แสดงผล

คุณสามารถเชื่อมโยงคำสั่ง where(...) หลายรายการเข้าด้วยกัน และทำหน้าที่เป็นนิพจน์ and(...) ตัวอย่างเช่น คำค้นหา 2 รายการต่อไปนี้เทียบเท่ากันในเชิงตรรกะและใช้แทนกันได้

Web

let results;

results = await execute(db.pipeline().collection("books")
  .where(field("rating").equal(5))
  .where(field("published").lessThan(1900))
);

results = await execute(db.pipeline().collection("books")
  .where(and(field("rating").equal(5), field("published").lessThan(1900)))
);
Swift
var results: Pipeline.Snapshot

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5))
  .where(Field("published").lessThan(1900))
  .execute()

results = try await db.pipeline().collection("books")
  .where(Field("rating").equal(5) && Field("published").lessThan(1900))
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute()

results = db.pipeline().collection("books")
    .where(Expression.and(field("rating").equal(5),
      field("published").lessThan(1900)))
    .execute()

Java

Task<Pipeline.Snapshot> results;

results = db.pipeline().collection("books")
    .where(field("rating").equal(5))
    .where(field("published").lessThan(1900))
    .execute();

results = db.pipeline().collection("books")
    .where(Expression.and(
        field("rating").equal(5),
        field("published").lessThan(1900)
    ))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import And, Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("rating").equal(5))
    .where(Field.of("published").less_than(1900))
    .execute()
)

results = (
    client.pipeline()
    .collection("books")
    .where(And(Field.of("rating").equal(5), Field.of("published").less_than(1900)))
    .execute()
)
Go
results1, err := client.Pipeline().
	Collection("books").
	Where(firestore.FieldOf("rating").Equal(5)).
	Where(firestore.FieldOf("published").LessThan(1900)).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

results2, err := client.Pipeline().
	Collection("books").
	Where(firestore.And(
		firestore.FieldOf("rating").Equal(5),
		firestore.FieldOf("published").LessThan(1900),
	)).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

เลือก / เพิ่มและนำช่องออก

select(...), add_fields(...) และ remove_fields(...) ทั้งหมดนี้ช่วยให้คุณแก้ไขช่องที่แสดงผลจากขั้นตอนก่อนหน้าได้ โดยทั่วไปแล้วทั้ง 3 แบบนี้ จะเรียกว่าเวทีแบบฉายภาพ

select(...) และ add_fields(...) ช่วยให้คุณระบุผลลัพธ์ของนิพจน์เป็นชื่อฟิลด์ที่ผู้ใช้ระบุได้ select(...) จะแสดงผลเฉพาะเอกสารที่มีชื่อฟิลด์ที่ระบุ ส่วน add_fields(...) จะขยายสคีมาของระยะก่อนหน้า (อาจเขียนทับค่าที่มีชื่อฟิลด์เหมือนกัน)

remove_fields(...) ช่วยให้ระบุชุดฟิลด์ที่จะนำออกจากขั้นตอนก่อนหน้าได้ การระบุชื่อฟิลด์ที่ไม่มีอยู่จะไม่มีผลใดๆ

ดูส่วนจำกัดฟิลด์ที่จะแสดงผลด้านล่าง แต่โดยทั่วไปแล้ว การใช้ขั้นตอนดังกล่าวเพื่อจำกัดผลลัพธ์ให้เหลือเฉพาะฟิลด์ที่จำเป็นในไคลเอ็นต์จะช่วยลดค่าใช้จ่ายและเวลาในการตอบสนองสำหรับการค้นหาส่วนใหญ่ได้

รวม / แตกต่าง

ระยะ aggregate(...) ช่วยให้คุณดำเนินการรวมข้อมูลหลายรายการในเอกสารอินพุตได้ โดยค่าเริ่มต้น ระบบจะรวมเอกสารทั้งหมดเข้าด้วยกัน แต่คุณสามารถระบุอาร์กิวเมนต์ grouping ที่ไม่บังคับได้ ซึ่งจะช่วยให้เอกสารอินพุตที่รวมเข้าด้วยกันอยู่ในที่เก็บข้อมูลที่แตกต่างกัน

Web

const results = await execute(db.pipeline()
  .collection("books")
  .aggregate(
    field("rating").average().as("avg_rating")
  )
  .distinct(field("genre"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .aggregate([
    Field("rating").average().as("avg_rating")
  ], groups: [
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .aggregate(
        AggregateStage
            .withAccumulators(AggregateFunction.average("rating").alias("avg_rating"))
            .withGroups(field("genre"))
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .aggregate(AggregateStage
        .withAccumulators(
            AggregateFunction.average("rating").alias("avg_rating"))
        .withGroups(field("genre")))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .aggregate(
        Field.of("rating").average().as_("avg_rating"), groups=[Field.of("genre")]
    )
    .execute()
)
Go
snapshot := client.Pipeline().
	Collection("books").
	Aggregate(
		firestore.Accumulators(firestore.Average("rating").As("avg_rating")),
		firestore.WithAggregateGroups("genre"),
	).
	Execute(ctx)

เมื่อไม่ได้ระบุ groupings ขั้นตอนนี้จะสร้างเอกสารเพียงรายการเดียว ไม่เช่นนั้นระบบจะสร้างเอกสารสำหรับชุดค่าผสมที่ไม่ซ้ำกันแต่ละชุดของค่า groupings

ระยะ distinct(...) เป็นโอเปอเรเตอร์การรวมที่เรียบง่ายซึ่งช่วยให้สร้าง groupings ที่ไม่ซ้ำกันได้โดยไม่ต้องมีตัวสะสม โดยจะทำงานเหมือนกับ aggregate(...) ในด้านอื่นๆ ทั้งหมด ตัวอย่างต่อไปนี้แสดงข้อมูลต่อไปนี้

Web

const results = await execute(db.pipeline()
  .collection("books")
  .distinct(
    field("author").toUpper().as("author"),
    field("genre")
  )
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .distinct([
    Field("author").toUpper().as("author"),
    Field("genre")
  ])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .distinct(
        field("author").toUpper().alias("author"),
        field("genre")
    )
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .distinct(Field.of("author").to_upper().as_("author"), "genre")
    .execute()
)
Go
snapshot := client.Pipeline().
	Collection("books").
	Distinct(firestore.Fields(
		firestore.ToUpper(firestore.FieldOf("author")).As("author"),
		firestore.FieldOf("genre"),
	)).
	Execute(ctx)

ฟังก์ชัน

ฟังก์ชันเป็นองค์ประกอบพื้นฐานสำหรับการสร้างนิพจน์และการค้นหาที่ซับซ้อน ดูรายการฟังก์ชันทั้งหมดพร้อมตัวอย่างได้ที่ข้อมูลอ้างอิงเกี่ยวกับฟังก์ชัน โปรดพิจารณาโครงสร้างของคำค้นหาทั่วไปดังนี้

ตัวอย่างที่แสดงระยะและฟังก์ชันในการค้นหา

หลายขั้นตอนยอมรับนิพจน์ที่มีฟังก์ชันอย่างน้อย 1 ฟังก์ชัน การใช้งานฟังก์ชันที่พบบ่อยที่สุดจะอยู่ในระยะwhere(...) และ select(...) ฟังก์ชันมี 2 ประเภทหลักๆ ที่คุณควรทำความคุ้นเคย ได้แก่

Web

let results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = await execute(db.pipeline().collection("books")
  .select(field("current").logicalMinimum(field("updated")).as("price_min"))
);

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = await execute(db.pipeline().collection("books")
  .aggregate(field("price").minimum().as("min_price"))
);
Swift
var results: Pipeline.Snapshot

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = try await db.pipeline().collection("books")
  .select([
    Field("current").logicalMinimum(["updated"]).as("price_min")
  ])
  .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = try await db.pipeline().collection("books")
  .aggregate([Field("price").minimum().as("min_price")])
  .execute()

Kotlin

var results: Task<Pipeline.Snapshot>

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute()

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute()

Java

Task<Pipeline.Snapshot> results;

// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results = db.pipeline().collection("books")
    .select(
        field("current").logicalMinimum("updated").alias("price_min")
    )
    .execute();

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results = db.pipeline().collection("books")
    .aggregate(AggregateFunction.minimum("price").alias("min_price"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Type 1: Scalar (for use in non-aggregation stages)
# Example: Return the min store price for each book.
results = (
    client.pipeline()
    .collection("books")
    .select(
        Field.of("current").logical_minimum(Field.of("updated")).as_("price_min")
    )
    .execute()
)

# Type 2: Aggregation (for use in aggregate stages)
# Example: Return the min price of all books.
results = (
    client.pipeline()
    .collection("books")
    .aggregate(Field.of("price").minimum().as_("min_price"))
    .execute()
)
Go
// Type 1: Scalar (for use in non-aggregation stages)
// Example: Return the min store price for each book.
results1, err := client.Pipeline().
	Collection("books").
	Select(firestore.Fields(
		firestore.LogicalMinimum(firestore.FieldOf("current"), firestore.FieldOf("updated")).As("price_min"),
	)).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

// Type 2: Aggregation (for use in aggregate stages)
// Example: Return the min price of all books.
results2, err := client.Pipeline().
	Collection("books").
	Aggregate(firestore.Accumulators(
		firestore.Minimum("price").As("min_price"),
	)).
	Execute(ctx).Results().GetAll()
if err != nil {
	fmt.Fprintf(w, "GetAll failed: %v", err)
	return err
}

จำกัดสูงสุด

โดยส่วนใหญ่แล้ว รุ่น Enterprise จะไม่จำกัดรูปแบบการค้นหา กล่าวคือ คุณไม่ได้ถูกจำกัดให้ใช้ค่าจำนวนน้อยในคำค้นหา IN หรือ OR แต่มีขีดจำกัดหลัก 2 ประการที่คุณควรทราบ ดังนี้

  • กำหนดเวลา: 60 วินาที (เหมือนกับรุ่น Standard)
  • การใช้งานหน่วยความจำ: ขีดจำกัด 128 MiB สำหรับปริมาณข้อมูลที่เกิดขึ้นจริงระหว่างการดำเนินการค้นหา

ข้อผิดพลาด

คุณอาจพบการค้นหาที่ล้มเหลวเนื่องจากสาเหตุหลายประการ ลิงก์ไปยังข้อผิดพลาดที่พบบ่อยและการดำเนินการที่เกี่ยวข้องซึ่งคุณทำได้มีดังนี้

รหัสข้อผิดพลาด การดำเนินการ
DEADLINE_EXCEEDED การค้นหาที่คุณกำลังดำเนินการเกินกำหนดเวลา 60 วินาทีและต้องมีการเพิ่มประสิทธิภาพเพิ่มเติม ดูเคล็ดลับในส่วนประสิทธิภาพ หากไม่สามารถหาสาเหตุของปัญหาได้ โปรดติดต่อทีม
RESOURCE_EXHAUSTED การค้นหาที่คุณกำลังดำเนินการเกินขีดจำกัดหน่วยความจำและต้องมีการเพิ่มประสิทธิภาพเพิ่มเติม ดูเคล็ดลับในส่วนประสิทธิภาพ หากไม่สามารถหาสาเหตุของปัญหาได้ โปรดติดต่อทีม
INTERNAL ติดต่อทีมเพื่อขอรับการสนับสนุน

ประสิทธิภาพ

ฐานข้อมูลรุ่น Enterprise ไม่จำเป็นต้องมีดัชนีอยู่เสมอ ซึ่งหมายความว่าการค้นหาอาจมีเวลาในการตอบสนองสูงกว่าเมื่อเทียบกับการค้นหาที่มีอยู่ซึ่งจะล้มเหลวทันทีพร้อมข้อผิดพลาดดัชนี FAILED_PRECONDITION หายไป คุณสามารถทำตามขั้นตอนต่อไปนี้เพื่อปรับปรุงประสิทธิภาพการทำงานของไปป์ไลน์

สร้างดัชนี

ดัชนีที่ใช้

Query Explain ช่วยให้คุณระบุได้ว่าคำค้นหาได้รับการแสดงโดยดัชนีหรือกลับไปใช้การดำเนินการที่มีประสิทธิภาพน้อยกว่า เช่น การสแกนตาราง หากการค้นหาของคุณไม่ได้รับการแสดงอย่างเต็มรูปแบบจากดัชนี คุณสามารถสร้างดัชนีได้โดยทำตามวิธีการ

การสร้างดัชนี

คุณสามารถทำตามเอกสารประกอบการจัดการดัชนีที่มีอยู่เพื่อสร้างดัชนีได้ ก่อนสร้างดัชนี โปรดทำความคุ้นเคยกับแนวทางปฏิบัติแนะนำทั่วไปเกี่ยวกับดัชนีใน Cloud Firestore เพื่อให้มั่นใจว่าการค้นหาของคุณสามารถใช้ประโยชน์จากดัชนีได้ ให้ทำตามแนวทางปฏิบัติแนะนำเพื่อสร้างดัชนีที่มีฟิลด์ตามลำดับต่อไปนี้

  1. ฟิลด์ทั้งหมดที่จะใช้ในตัวกรองความเท่ากัน (เรียงตามลำดับใดก็ได้)
  2. ฟิลด์ทั้งหมดที่จะจัดเรียง (ตามลำดับเดียวกัน)
  3. ช่องที่จะใช้ในตัวกรองช่วงหรือความไม่เท่ากันตามลำดับความสามารถในการเลือกข้อจำกัดการค้นหาจากมากไปน้อย

ตัวอย่างเช่น สำหรับคำค้นหาต่อไปนี้

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("published").lessThan(1900))
  .where(field("genre").equal("Science Fiction"))
  .where(field("rating").greaterThan(4.3))
  .sort(field("published").descending())
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("published").lessThan(1900))
  .where(Field("genre").equal("Science Fiction"))
  .where(Field("rating").greaterThan(4.3))
  .sort([Field("published").descending()])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("published").lessThan(1900))
    .where(field("genre").equal("Science Fiction"))
    .where(field("rating").greaterThan(4.3))
    .sort(field("published").descending())
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("published").less_than(1900))
    .where(Field.of("genre").equal("Science Fiction"))
    .where(Field.of("rating").greater_than(4.3))
    .sort(Field.of("published").descending())
    .execute()
)
Go
snapshot := client.Pipeline().
	Collection("books").
	Where(firestore.FieldOf("published").LessThan(1900)).
	Where(firestore.FieldOf("genre").Equal("Science Fiction")).
	Where(firestore.FieldOf("rating").GreaterThan(4.3)).
	Sort(firestore.Orders(firestore.Descending(firestore.FieldOf("published")))).
	Execute(ctx)

ดัชนีที่แนะนำคือดัชนีขอบเขตคอลเล็กชันใน books สำหรับ (genre [...], published DESC, avg_rating DESC).

ความหนาแน่นของดัชนี

Cloud Firestore รองรับดัชนีแบบ Sparse และแบบไม่ใช่ Sparse ดูข้อมูลเพิ่มเติมได้ที่ความหนาแน่นของดัชนี

การค้นหาที่ครอบคลุม + ดัชนีรอง

Cloud Firestore สามารถข้ามการดึงข้อมูลเอกสารแบบเต็มและแสดงผลลัพธ์จากดัชนีได้หากฟิลด์ทั้งหมดที่แสดงอยู่ในดัชนีรอง ซึ่งโดยปกติแล้วจะช่วยปรับปรุงเวลาในการตอบสนอง (และค่าใช้จ่าย) ได้อย่างมาก โดยใช้การค้นหาตัวอย่างด้านล่าง

Web

const results = await execute(db.pipeline()
  .collection("books")
  .where(field("category").like("%fantasy%"))
  .where(field("title").exists())
  .where(field("author").exists())
  .select(field("title"), field("author"))
);
Swift
let results = try await db.pipeline()
  .collection("books")
  .where(Field("category").like("%fantasy%"))
  .where(Field("title").exists())
  .where(Field("author").exists())
  .select([Field("title"), Field("author")])
  .execute()

Kotlin

val results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute()

Java

Task<Pipeline.Snapshot> results = db.pipeline()
    .collection("books")
    .where(field("category").like("%fantasy%"))
    .where(field("title").exists())
    .where(field("author").exists())
    .select(field("title"), field("author"))
    .execute();
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

results = (
    client.pipeline()
    .collection("books")
    .where(Field.of("category").like("%fantasy%"))
    .where(Field.of("title").exists())
    .where(Field.of("author").exists())
    .select("title", "author")
    .execute()
)
Go
snapshot := client.Pipeline().
	Collection("books").
	Where(firestore.FieldOf("category").Like("%fantasy%")).
	Where(firestore.FieldOf("title").FieldExists()).
	Where(firestore.FieldOf("author").FieldExists()).
	Select(firestore.Fields("title", "author")).
	Execute(ctx)

หากฐานข้อมูลมีดัชนีขอบเขตคอลเล็กชันใน books สำหรับ (category [...], title [...], author [...]) อยู่แล้ว ก็จะหลีกเลี่ยงการดึงข้อมูลจากเอกสารหลักได้ ในกรณีนี้ ลำดับในดัชนีไม่สำคัญ และจะใช้ [...] เพื่อระบุสิ่งนั้น

จำกัดช่องที่จะแสดงผล

โดยค่าเริ่มต้น การค้นหา Cloud Firestore จะแสดงผลช่องทั้งหมดในเอกสาร ซึ่งคล้ายกับ SELECT * ในระบบเชิงสัมพันธ์ อย่างไรก็ตาม หากแอปพลิเคชันของคุณต้องการเพียงชุดย่อยของช่อง คุณสามารถใช้ระยะ select(...) หรือ restrict(...) เพื่อพุชการกรองนี้ฝั่งเซิร์ฟเวอร์ได้ ซึ่งจะช่วยลดทั้งขนาดการตอบกลับ (ลดต้นทุนสำหรับข้อมูลขาออกของเครือข่าย) และปรับปรุงเวลาในการตอบสนอง

เครื่องมือแก้ปัญหา

Query Explain

Query Explain ช่วยให้คุณเห็นเมตริกการดำเนินการและรายละเอียดเกี่ยวกับดัชนีที่ใช้

เมตริก

การดำเนินการไปป์ไลน์หากผสานรวมกับเมตริก Cloud Firestore ที่มีอยู่อย่างสมบูรณ์

ปัญหา / ข้อจำกัดที่ทราบ

ดัชนีเฉพาะทาง

การดำเนินการไปป์ไลน์ยังไม่รองรับประเภทดัชนี array-contains และ vector ที่มีอยู่ แทนที่จะปฏิเสธคำค้นหาดังกล่าว Cloud Firestore จะพยายามใช้ดัชนี ascending และ descending อื่นๆ ที่มีอยู่ คาดว่าด้วยเหตุนี้ นิพจน์ array_contains หรือ find_nearest จะทำงานช้ากว่านิพจน์ที่เทียบเท่าที่มีอยู่

การสนับสนุนแบบเรียลไทม์และออฟไลน์

การดำเนินการไปป์ไลน์ไม่มีความสามารถแบบเรียลไทม์และออฟไลน์

ขั้นตอนถัดไป