เริ่มต้นใช้งานการดำเนินการไปป์ไลน์ Firestore

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

การค้นหาแบบไปป์ไลน์เป็นอินเทอร์เฟซการค้นหาใหม่สำหรับ Firestore ซึ่งมีฟังก์ชันการทำงานของคำค้นหาขั้นสูง รวมถึงนิพจน์ที่ซับซ้อน นอกจากนี้ยังรองรับฟังก์ชันใหม่ๆ อีกมากมาย เช่น min, max, substring, regex_match และ array_contains_all การค้นหาไปป์ไลน์ยังช่วยให้การสร้างดัชนีเป็นแบบไม่บังคับโดยสมบูรณ์ ซึ่งช่วยเพิ่มประสิทธิภาพ กระบวนการพัฒนาการค้นหาใหม่ๆ นอกจากนี้ การค้นหาแบบไปป์ไลน์ยังช่วยลดข้อจำกัดหลายอย่างเกี่ยวกับรูปร่างของการค้นหา ทำให้คุณระบุการค้นหา in หรือ or ขนาดใหญ่ได้

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

หากต้องการติดตั้งและเริ่มต้น 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)
)

การเริ่มต้น

การค้นหาแบบไปป์ไลน์มีไวยากรณ์ที่คุ้นเคยเป็นอย่างดีซึ่งมาจาก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()

โครงสร้าง

เมื่อสร้างการค้นหา Pipeline คุณควรทำความเข้าใจคำศัพท์ 2-3 คำ ได้แก่ สเตจ นิพจน์ และฟังก์ชัน

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

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

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

การอ้างอิงฟิลด์เทียบกับการอ้างอิงค่าคงที่

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

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")))
)

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

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

ขั้นตอนการป้อนข้อมูลแสดงถึงขั้นตอนแรกของคำค้นหา กำหนดชุดเอกสารเริ่มต้นที่คุณกำลังค้นหา สําหรับการค้นหาไปป์ไลน์ ส่วนใหญ่จะคล้ายกับการค้นหาที่มีอยู่ ซึ่งการค้นหาส่วนใหญ่จะเริ่มต้นด้วยขั้นตอน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()
)

เช่นเดียวกับขั้นตอนอื่นๆ ทั้งหมด ลำดับของผลลัพธ์จากขั้นตอนอินพุตเหล่านี้จะไม่ เสถียร ควรเพิ่ม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()
)

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

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

select(...) และ add_fields(...) ช่วยให้คุณระบุผลลัพธ์ของ นิพจน์เป็นชื่อฟิลด์ที่ผู้ใช้ระบุได้ นิพจน์ที่ทำให้เกิดข้อผิดพลาด จะทำให้เกิดค่า null 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()
)

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

distinct(...)สเตจgroupingsเป็นตัวดำเนินการรวบรวมที่เรียบง่ายซึ่งช่วยให้สร้างเฉพาะ 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()
)

ฟังก์ชัน

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

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

หลายขั้นตอนยอมรับนิพจน์ที่มีฟังก์ชันอย่างน้อย 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()
)

จำกัดสูงสุด

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

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

ข้อผิดพลาด

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

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

ประสิทธิภาพ

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

สร้างดัชนี

ดัชนีที่ใช้

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

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

คุณสามารถทำตามเอกสารประกอบการจัดการดัชนีที่มีอยู่เพื่อสร้างดัชนี ก่อนสร้างดัชนี โปรดทำความคุ้นเคยกับแนวทางปฏิบัติแนะนำทั่วไปสำหรับดัชนีใน 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()
)

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

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

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

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

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

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

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

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

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

อธิบายการค้นหา

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

เมตริก

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

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

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

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

การใส่เลขหน้า

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

Web

// Existing pagination via `startAt()`
const q =
  query(collection(db, "cities"), orderBy("population"), startAt(1000000));

// Private preview workaround using pipelines
const pageSize = 2;
const pipeline = db.pipeline()
  .collection("cities")
  .select("name", "population", "__name__")
  .sort(field("population").descending(), field("__name__").ascending());

// Page 1 results
let snapshot = await execute(pipeline.limit(pageSize));

// End of page marker
const lastDoc = snapshot.results[snapshot.results.length - 1];

// Page 2 results
snapshot = await execute(
  pipeline
    .where(
      or(
        and(
          field("population").equal(lastDoc.get("population")),
          field("__name__").greaterThan(lastDoc.ref)
        ),
        field("population").lessThan(lastDoc.get("population"))
      )
    )
    .limit(pageSize)
);
Swift
// Existing pagination via `start(at:)`
let query = db.collection("cities").order(by: "population").start(at: [1000000])

// Private preview workaround using pipelines
let pipeline = db.pipeline()
  .collection("cities")
  .where(Field("population").greaterThanOrEqual(1000000))
  .sort([Field("population").descending()])

Kotlin

// Existing pagination via `startAt()`
val query = db.collection("cities").orderBy("population").startAt(1000000)

// Private preview workaround using pipelines
val pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending())

Java

// Existing pagination via `startAt()`
Query query = db.collection("cities").orderBy("population").startAt(1000000);

// Private preview workaround using pipelines
Pipeline pipeline = db.pipeline()
    .collection("cities")
    .where(field("population").greaterThanOrEqual(1000000))
    .sort(field("population").descending());
Python
from google.cloud.firestore_v1.pipeline_expressions import Field

# Existing pagination via `start_at()`
query = (
    client.collection("cities")
    .order_by("population")
    .start_at({"population": 1_000_000})
)

# Private preview workaround using pipelines
pipeline = (
    client.pipeline()
    .collection("cities")
    .where(Field.of("population").greater_than_or_equal(1_000_000))
    .sort(Field.of("population").descending())
)

การสนับสนุนโปรแกรมจำลอง

โปรแกรมจำลองยังไม่รองรับการค้นหาแบบไปป์ไลน์

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

การค้นหาไปป์ไลน์ยังไม่มีความสามารถแบบเรียลไทม์และออฟไลน์

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