Mulai menggunakan operasi Firestore Pipeline

Latar belakang

Kueri Pipeline adalah antarmuka kueri baru untuk Firestore. Fitur ini menyediakan fungsi kueri tingkat lanjut, termasuk ekspresi kompleks. Fitur ini juga menambahkan dukungan untuk berbagai fungsi baru seperti min, max, substring, regex_match dan array_contains_all. Dengan Kueri Pipeline, pembuatan indeks juga sepenuhnya bersifat opsional, sehingga menyederhanakan proses pengembangan kueri baru. Kueri Pipeline juga menghilangkan berbagai batasan pada bentuk kueri, sehingga Anda dapat menentukan kueri in atau or yang besar.

Memulai

Untuk menginstal dan menginisialisasi SDK klien, lihat petunjuk di Panduan memulai.

Sintaksis

Bagian berikut memberikan ringkasan sintaksis untuk Kueri Pipeline.

Konsep

Salah satu perbedaan penting di Kueri Pipeline adalah penerapan pengurutan "tahap" secara eksplisit. Dengan ini, Anda dapat mengekspresikan kueri yang lebih kompleks. Namun, ini merupakan penyimpangan yang signifikan dari antarmuka kueri yang sudah ada, yang urutan tahapnya bersifat implisit. Perhatikan contoh Kueri Pipeline berikut:

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

Inisialisasi

Kueri Pipeline memiliki sintaksis yang sangat familier yang berasal dari kueri Cloud Firestore yang ada. Untuk memulai, Anda harus menginisialisasi kueri dengan menulis perintah berikut:

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

Struktur

Ada beberapa istilah yang penting untuk dipahami saat membuat Kueri Pipeline: tahap, ekspresi, dan fungsi.

Contoh yang menunjukkan tahap dan ekspresi dalam kueri

Tahap: Pipeline dapat terdiri dari satu atau beberapa tahap. Secara logis, ini mewakili serangkaian langkah (atau tahap) yang dilakukan untuk menjalankan kueri. Catatan: Dalam praktiknya, tahap dapat dijalankan di luar urutan untuk meningkatkan performa. Namun, hal ini tidak mengubah maksud atau kebenaran kueri.

Ekspresi: Tahap sering kali menerima ekspresi yang memungkinkan Anda mengekspresikan kueri yang lebih kompleks. Ekspresi bisa jadi sederhana dan terdiri dari satu fungsi seperti eq("a", 1). Anda juga dapat membuat ekspresi yang lebih kompleks dengan menyusun ekspresi seperti and(eq("a", 1), eq("b", 2)).

Referensi Kolom vs. Konstan

Kueri Pipeline mendukung ekspresi yang kompleks. Oleh karena itu, mungkin perlu dibedakan apakah suatu nilai merepresentasikan kolom atau konstan. Perhatikan contoh berikut:

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

Tahap

Tahap Input

Tahap input merupakan tahap pertama kueri. Tahap ini menentukan kumpulan dokumen awal yang Anda kueri. Untuk Kueri Pipeline, tahap ini sangat mirip dengan kueri yang sudah ada, yang sebagian besar kuerinya dimulai dengan tahap collection(...) atau collection_group(...). Dua tahap input barunya adalah database() dan documents(...). Dengan database(), semua dokumen dapat ditampilkan dalam database. Sementara itu, documents(...) berfungsi mirip dengan operasi baca secara massal.

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

Seperti semua tahap lainnya, urutan hasil dari tahap input ini tidak stabil. Operator sort(...) harus selalu ditambahkan jika urutan tertentu diinginkan.

Di mana

Tahap where(...) berfungsi sebagai operasi filter tradisional pada dokumen yang dihasilkan dari tahap sebelumnya dan sebagian besar mencerminkan sintaksis "where" yang ada untuk kueri yang sudah ada. Dokumen apa pun yang ekspresinya menghasilkan nilai yang bukan true akan difilter dari dokumen yang ditampilkan.

Beberapa pernyataan where(...) dapat dirangkai bersama, dan bertindak sebagai ekspresi and(...). Misalnya, dua kueri berikut secara logis setara dan dapat digunakan secara bergantian.

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 & Remove Fields

select(...), add_fields(...) & remove_fields(...) memungkinkan Anda mengubah kolom yang ditampilkan dari tahap sebelumnya. Ketiga tahap ini umumnya disebut sebagai tahap gaya proyeksi.

select(...) dan add_fields(...) memungkinkan Anda menentukan hasil ekspresi ke nama kolom yang disediakan pengguna. Ekspresi yang menghasilkan error akan menghasilkan nilai null. select(...) hanya akan menampilkan dokumen dengan nama kolom yang ditentukan, sedangkan add_fields(...) akan memperluas skema tahap sebelumnya (kemungkinan akan menimpa nilai dengan nama kolom yang mirip).

remove_fields(...) memungkinkan penentuan sekumpulan kolom yang akan dihapus dari tahap sebelumnya. Tidak ada operasi yang dilakukan jika nama kolom yang ditentukan tidak ada.

Lihat bagian Membatasi Kolom yang Ditampilkan di bawah. Namun secara umum, penggunaan tahap tersebut untuk membatasi hasil hanya pada kolom yang diperlukan di klien akan membantu mengurangi biaya dan latensi untuk sebagian besar kueri.

Aggregate/Distinct

Tahap aggregate(...) memungkinkan Anda melakukan serangkaian penggabungan pada dokumen input. Secara default, semua dokumen digabungkan, tetapi argumen grouping opsional dapat diberikan, sehingga dokumen input dapat digabungkan ke dalam bucket yang berbeda.

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

Jika groupings tidak ditentukan, tahap ini hanya akan menghasilkan satu dokumen, atau dokumen akan dibuat untuk setiap kombinasi unik nilai groupings.

Tahap distinct(...) adalah operator penggabungan yang disederhanakan yang memungkinkan pembuatan groupings unik secara khusus tanpa akumulator. Perilakunya mirip dengan aggregate(...) dalam segala aspek lainnya. Berikut contohnya:

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

Fungsi

Fungsi adalah elemen penyusun untuk membuat ekspresi dan kueri yang kompleks. Untuk mengetahui daftar lengkap fungsi beserta contohnya, lihat Referensi fungsi. Sebagai pengingat, pertimbangkan struktur kueri umum:

Contoh yang menunjukkan tahap dan fungsi dalam kueri

Banyak tahap menerima ekspresi yang berisi satu atau beberapa fungsi. Penggunaan fungsi yang paling umum akan ditemukan di tahap where(...) dan select(...). Ada dua jenis fungsi utama yang harus Anda ketahui:

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

Batas

Pada umumnya, Edisi Enterprise tidak membatasi bentuk kueri. Dengan kata lain, Anda tidak dibatasi untuk hanya menggunakan sejumlah kecil nilai dalam kueri IN atau OR. Namun, ada dua batasan utama yang harus Anda ketahui:

  • Batas waktu: 60 detik (sama dengan Edisi Standard).
  • Penggunaan Memori: Batas 128 MiB untuk jumlah data yang diwujudkan selama eksekusi kueri.

Error

Anda bisa saja mengalami kegagalan kueri karena sejumlah alasan. Berikut link ke error umum dan tindakan terkait yang dapat Anda lakukan:

Kode Error Tindakan
DEADLINE_EXCEEDED Kueri yang Anda jalankan melebihi batas waktu 60 detik dan memerlukan pengoptimalan tambahan. Lihat bagian performa untuk mendapatkan tips. Jika Anda tidak dapat menemukan akar masalahnya, silakan hubungi tim.
RESOURCE_EXHAUSTED Kueri yang Anda jalankan melebihi batas memori dan memerlukan pengoptimalan tambahan. Lihat bagian performa untuk mendapatkan tips. Jika Anda tidak dapat menemukan akar masalahnya, silakan hubungi tim.
INTERNAL Hubungi tim untuk mendapatkan dukungan.

Performa

Tidak seperti kueri yang sudah ada, Kueri Pipeline tidak selalu memerlukan indeks. Artinya, kueri dapat menunjukkan latensi yang lebih tinggi dibandingkan dengan kueri yang sudah ada, yang akan langsung gagal dengan error indeks yang hilang FAILED_PRECONDITION. Untuk meningkatkan performa Kueri Pipeline, ada beberapa langkah yang dapat Anda lakukan.

Membuat Indeks

Indeks yang Digunakan

Dengan Query Explain, Anda dapat mengetahui apakah kueri Anda ditayangkan oleh indeks atau dikembalikan ke operasi yang kurang efisien seperti pemindaian tabel. Jika kueri Anda tidak sepenuhnya ditayangkan dari indeks, Anda dapat membuat indeks dengan mengikuti petunjuk yang ada.

Membuat Indeks

Anda dapat mengikuti dokumentasi pengelolaan indeks yang ada untuk membuat indeks. Sebelum membuat indeks, pahami praktik terbaik umum terkait indeks di Firestore. Untuk memastikan kueri Anda dapat memanfaatkan indeks, ikuti praktik terbaik untuk membuat indeks dengan kolom dalam urutan berikut:

  1. Semua kolom yang akan digunakan dalam filter kesetaraan (dalam urutan apa pun)
  2. Semua kolom yang akan diurutkan (dalam urutan yang sama)
  3. Kolom yang akan digunakan dalam filter rentang atau ketidaksetaraan dalam urutan menurun dari selektivitas batasan kueri

Misalnya, untuk kueri berikut,

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

Indeks yang direkomendasikan adalah indeks cakupan koleksi pada books untuk (genre [...], published DESC, avg_rating DESC).

Kepadatan indeks

Cloud Firestore mendukung indeks sparse dan non-sparse. Untuk mengetahui informasi selengkapnya, lihat Kepadatan indeks.

Kueri yang Tercakup + Indeks Sekunder

Firestore dapat melewati pengambilan dokumen lengkap dan hanya menampilkan hasil dari indeks jika semua kolom yang ditampilkan ada dalam indeks sekunder. Hal ini biasanya akan menghasilkan penurunan latensi (dan biaya) yang signifikan. Dengan contoh kueri di bawah:

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

Jika database sudah memiliki indeks cakupan koleksi pada books untuk (category [...], title [...], author [...]), database dapat menghindari pengambilan apa pun dari dokumen utama itu sendiri. Dalam kasus ini, urutan dalam indeks tidaklah penting, hal ini ditandai dengan [...].

Membatasi Kolom yang akan Ditampilkan

Secara default, kueri Firestore menampilkan semua kolom dalam dokumen, yang serupa dengan SELECT * dalam sistem tradisional. Namun, jika aplikasi Anda hanya memerlukan sebagian kecil kolom, tahap select(...) atau restrict(...) dapat digunakan untuk mengirim pemfilteran ini ke sisi server. Hal ini akan mengurangi ukuran respons (mengurangi biaya egress jaringan) sekaligus mengurangi latensi.

Alat Pemecahan Masalah

Query Explain

Dengan Query Explain, Anda dapat memiliki visibilitas terkait metrik eksekusi dan detail tentang indeks yang digunakan.

Metrik

Kueri Pipeline terintegrasi sepenuhnya dengan metrik Firestore yang ada.

Masalah Umum/Batasan

Indeks Khusus

Kueri Pipeline belum mendukung jenis indeks array-contains & vector yang ada. Agar tidak terus-menerus menolak kueri tersebut, Firestore akan mencoba menggunakan indeks ascending & descending lainnya yang ada. Selama pratinjau pribadi, kueri Pipeline dengan ekspresi array_contains atau find_nearest tersebut diperkirakan akan lebih lambat daripada kueri yang setara karena hal ini.

Penomoran halaman

Dukungan untuk penomoran halaman yang mudah di set hasil tidak didukung selama pratinjau pribadi. Hal ini dapat diatasi dengan menggabungkan tahap where(...) & sort(...) yang setara seperti yang ditunjukkan di bawah.

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

Dukungan Emulator

Emulator belum mendukung kueri Pipeline.

Dukungan Real-time dan Offline

Kueri pipeline belum memiliki kemampuan real-time dan offline.

Langkah berikutnya