Mengoptimalkan kueri dengan filter rentang dan ketidaksetaraan di beberapa kolom

Halaman ini memberikan contoh strategi pengindeksan yang sebaiknya digunakan untuk kueri dengan filter rentang & ketidaksetaraan di beberapa kolom untuk menciptakan pengalaman kueri yang efisien.

Baca tentang konsep terkait sebelum mengoptimalkan kueri Anda.

Mengoptimalkan kueri dengan Query Explain

Untuk mengetahui apakah kueri dan indeks yang digunakan optimal, Anda dapat menggunakan Query Explain untuk mendapatkan ringkasan rencana kueri dan statistik eksekusi kueri:

Java

Query q = db.collection("employees").whereGreaterThan("salary",
100000).whereGreaterThan("experience", 0);

ExplainResults<QuerySnapshot> explainResults = q.explain(ExplainOptions.builder().analyze(true).build()).get();
ExplainMetrics metrics = explainResults.getMetrics();

PlanSummary planSummary = metrics.getPlanSummary();
ExecutionStats executionStats = metrics.getExecutionStats();

System.out.println(planSummary.getIndexesUsed());
System.out.println(stats.getResultsReturned());
System.out.println(stats.getExecutionDuration());
System.out.println(stats.getReadOperations());
System.out.println(stats.getDebugStats());

Node.js

let q = db.collection("employees")
      .where("salary", ">", 100000)
      .where("experience", ">",0);

let options = { analyze : 'true' };
let explainResults = await q.explain(options);

let planSummary = explainResults.metrics.planSummary;
let stats = explainResults.metrics.executionStats;

console.log(planSummary);
console.log(stats);

Contoh berikut menunjukkan bagaimana penggunaan pengurutan indeks yang benar mengurangi jumlah entri indeks yang dipindai Cloud Firestore.

Kueri sederhana

Dengan contoh sebelumnya dari kumpulan karyawan, kueri sederhana yang berjalan dengan indeks (experience ASC, salary ASC) adalah sebagai berikut:

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("experience")
  .orderBy("salary");

Kueri memindai 95.000 entri indeks hanya untuk mengembalikan 5 dokumen. Karena predikat kueri tidak terpenuhi, sejumlah besar entri indeks dibaca, tetapi difilter.

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(experience ASC, salary ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "2.5s",
    "readOperations": "100",
    "debugStats": {
        "index_entries_scanned": "95000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "95000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

Berdasarkan keahlian domain, sebagian besar karyawan akan memiliki setidaknya beberapa pengalaman, tetapi hanya sedikit yang akan memiliki gaji lebih dari 100.000. Dengan insight ini, kita dapat menyimpulkan bahwa batasan salary lebih selektif daripada batasan experience. Untuk memengaruhi indeks yang digunakan Cloud Firestore untuk menjalankan kueri, tentukan klausa orderBy yang mengurutkan batasan salary sebelum batasan experience.

Java

db.collection("employees")
  .whereGreaterThan("salary", 100000)
  .whereGreaterThan("experience", 0)
  .orderBy("salary")
  .orderBy("experience");

Jika Anda menggunakan klausa orderBy() secara eksplisit untuk menambahkan predikat, Cloud Firestore akan menggunakan indeks (salary ASC, experience ASC) untuk menjalankan kueri. Dengan demikian, karena selektivitas filter rentang pertama lebih tinggi dalam kueri ini dibandingkan dengan kueri sebelumnya, kueri akan berjalan lebih cepat dan lebih hemat biaya.

// Output query planning info
{
    "indexesUsed": [
        {
            "properties": "(salary ASC, experience ASC, __name__ ASC)",
            "query_scope": "Collection"
        }
    ],

    // Output Query Execution Stats
    "resultsReturned": "5",
    "executionDuration": "0.2s",
    "readOperations": "6",
    "debugStats": {
        "index_entries_scanned": "1000",
        "documents_scanned": "5",
        "billing_details": {
            "documents_billable": "5",
            "index_entries_billable": "1000",
            "small_ops": "0",
            "min_query_cost": "0"
        }
    }
}

Langkah Berikutnya