Optimize queries with range and inequality filters on multiple fields
Stay organized with collections
Save and categorize content based on your preferences.
This page provides examples of indexing strategy that you can use for queries
with range and inequality filters on multiple fields to create an efficient
query experience.
The query scans 95000 index entries only to return five documents. Since the query
predicate isn't satisfied, a large number of index entries are read but are
filtered out.
You can infer from domain expertise that most employees will have at least some
experience but few will have a salary that is more than 100000. Given this
insight, you can see that the salary constraint is more selective than the
experience constraint. To influence the index that Cloud Firestore uses to
execute the query, specify an orderBy clause that orders the salary
constraint before the experience constraint.
When you explicitly use the orderBy() clause to add the predicates,
Cloud Firestore uses the (salary ASC, experience ASC) index to run the query.
Since the selectivity of the first range filter is higher in this query
compared to the earlier query, the query runs faster and is more cost efficient.
[[["Easy to understand","easyToUnderstand","thumb-up"],["Solved my problem","solvedMyProblem","thumb-up"],["Other","otherUp","thumb-up"]],[["Missing the information I need","missingTheInformationINeed","thumb-down"],["Too complicated / too many steps","tooComplicatedTooManySteps","thumb-down"],["Out of date","outOfDate","thumb-down"],["Samples / code issue","samplesCodeIssue","thumb-down"],["Other","otherDown","thumb-down"]],["Last updated 2025-09-04 UTC."],[],[],null,["\u003cbr /\u003e\n\nThis page provides examples of indexing strategy that you can use for queries\nwith range and inequality filters on multiple fields to create an efficient\nquery experience.\n\nBefore you optimize your queries, read about [the related concepts](/docs/firestore/query-data/multiple-range-fields).\n\nOptimize queries with Query Explain\n\nTo determine if your query and indexes are optimal, you can use [Query\nExplain](/docs/firestore/query-explain) to get the query plan summary and execution statistics\nof the query: \n\nJava \n\n Query q = db.collection(\"employees\").whereGreaterThan(\"salary\",\n 100000).whereGreaterThan(\"experience\", 0);\n\n ExplainResults\u003cQuerySnapshot\u003e explainResults = q.explain(ExplainOptions.builder().analyze(true).build()).get();\n ExplainMetrics metrics = explainResults.getMetrics();\n\n PlanSummary planSummary = metrics.getPlanSummary();\n ExecutionStats executionStats = metrics.getExecutionStats();\n\n System.out.println(planSummary.getIndexesUsed());\n System.out.println(stats.getResultsReturned());\n System.out.println(stats.getExecutionDuration());\n System.out.println(stats.getReadOperations());\n System.out.println(stats.getDebugStats());\n\nNode.js \n\n let q = db.collection(\"employees\")\n .where(\"salary\", \"\u003e\", 100000)\n .where(\"experience\", \"\u003e\",0);\n\n let options = { analyze : 'true' };\n let explainResults = await q.explain(options);\n\n let planSummary = explainResults.metrics.planSummary;\n let stats = explainResults.metrics.executionStats;\n\n console.log(planSummary);\n console.log(stats);\n\nThe following example shows how the use of correct index ordering reduces the\nnumber of index entries that Cloud Firestore scans.\n\nSimple queries\n\nWith the [earlier example](/docs/firestore/query-data/multiple-range-fields#indexing_considerations) of a collection of employees, the simple query\nthat runs with the `(experience ASC, salary ASC)` index is as follows: \n\nJava \n\n db.collection(\"employees\")\n .whereGreaterThan(\"salary\", 100000)\n .whereGreaterThan(\"experience\", 0)\n .orderBy(\"experience\")\n .orderBy(\"salary\");\n\nThe query scans 95000 index entries only to return five documents. Since the query\npredicate isn't satisfied, a large number of index entries are read but are\nfiltered out. \n\n```scilab\n// Output query planning info\n{\n \"indexesUsed\": [\n {\n \"properties\": \"(experience ASC, salary ASC, __name__ ASC)\",\n \"query_scope\": \"Collection\"\n }\n ],\n\n // Output Query Execution Stats\n \"resultsReturned\": \"5\",\n \"executionDuration\": \"2.5s\",\n \"readOperations\": \"100\",\n \"debugStats\": {\n \"index_entries_scanned\": \"95000\",\n \"documents_scanned\": \"5\",\n \"billing_details\": {\n \"documents_billable\": \"5\",\n \"index_entries_billable\": \"95000\",\n \"small_ops\": \"0\",\n \"min_query_cost\": \"0\"\n }\n }\n}\n```\n\nYou can infer from domain expertise that most employees will have at least some\nexperience but few will have a salary that is more than 100000. Given this\ninsight, you can see that the `salary` constraint is more selective than the\n`experience` constraint. To influence the index that Cloud Firestore uses to\nexecute the query, specify an `orderBy` clause that orders the `salary`\nconstraint before the `experience` constraint. \n\nJava \n\n db.collection(\"employees\")\n .whereGreaterThan(\"salary\", 100000)\n .whereGreaterThan(\"experience\", 0)\n .orderBy(\"salary\")\n .orderBy(\"experience\");\n\nWhen you explicitly use the `orderBy()` clause to add the predicates,\nCloud Firestore uses the `(salary ASC, experience ASC)` index to run the query.\nSince the selectivity of the first range filter is higher in this query\ncompared to the earlier query, the query runs faster and is more cost efficient. \n\n```scilab\n// Output query planning info\n{\n \"indexesUsed\": [\n {\n \"properties\": \"(salary ASC, experience ASC, __name__ ASC)\",\n \"query_scope\": \"Collection\"\n }\n ],\n\n // Output Query Execution Stats\n \"resultsReturned\": \"5\",\n \"executionDuration\": \"0.2s\",\n \"readOperations\": \"6\",\n \"debugStats\": {\n \"index_entries_scanned\": \"1000\",\n \"documents_scanned\": \"5\",\n \"billing_details\": {\n \"documents_billable\": \"5\",\n \"index_entries_billable\": \"1000\",\n \"small_ops\": \"0\",\n \"min_query_cost\": \"0\"\n }\n }\n}\n```\n\nWhat's next\n\n- Learn about [Query Explain](/docs/firestore/query-explain).\n- Learn about [indexing best practices](/docs/firestore/query-data/index-overview#indexing_best_practices)."]]