Analyze query execution with Query Explain

Query Explain allows you to submitCloud Firestore queries to thebackend and receive detailed performance statistics on backend query executionin return. It functions like theEXPLAIN [ANALYZE] operation in manyrelational database systems.

Query Explain requests can be sent using theFirestore server client libraries.

Query Explain results help you understand how your queries areexecuted, showing you inefficiencies and the location of likely server-sidebottlenecks.

Query Explain:

  • Provides insights on the query planning phase so you can adjust your queryindexes and boost efficiency.
  • Using the analyze option, helps you understand your cost and performance ona per-query basis and lets you quickly iterate through different querypatterns in order to optimize their usage.
Note: Query Explain supports polled queries. Streaming queries are not yet supported.

Understand Query Explain options: default and analyze

Query Explain operations can be performed using thedefault option oranalyze option.

With the default option, Query Explain plans the query, but skipsover the execution stage. This will return planner stage information. You canuse this to check that a query has the necessary indexes and understand whichindexes are used. This will help you to verify, for example, that a particularquery is using a composite index over having to intersect over many differentindexes.

With the analyze option, Query Explain both plans and executes thequery. This returns all the previously mentioned planner information along withstatistics from the query execution runtime. This will include the billinginformation of the query along with system-level insights into the queryexecution. You can use this tooling to test various query and indexconfigurations to optimize their cost and latency.

What does Query Explain cost?

When you use Query Explain with the default option, no index or read operationsare performed. Regardless of query complexity, one read operation is charged.

When you use Query Explain with the analyze option, index and read operationsare performed, so you are charged for the query as usual. There is no additionalcharge for the analysis activity, only the usual charge for the query beingexecuted.

Use Query Explain with the default option

You can use the client libraries to submit a default option request.

Note that requests are authenticated with IAM, using the samepermissions for regular query operations. Other authentication techniques, likeFirebase Authentication, are ignored. For more information, see the guide onIAM for server client libraries.

Java (Admin)
Queryq=db.collection("col").whereGreaterThan("a",1);ExplainOptionsoptions=ExplainOptions.builder().build();ExplainResults<QuerySnapshot>explainResults=q.explain(options).get();ExplainMetricsmetrics=explainResults.getMetrics();PlanSummaryplanSummary=metrics.getPlanSummary();
Node (Admin)
constq=db.collection('col').where('country','=','USA');constoptions={analyze:'false'};constexplainResults=awaitq.explain(options);constmetrics=explainResults.metrics;constplan=metrics.planSummary;

The exact format of the response depends on the execution environment.Returned results can be converted to JSON. For example:

{    "indexes_used": [        {"query_scope": "Collection", "properties": "(category ASC, __name__ ASC)"},        {"query_scope": "Collection", "properties": "(country ASC, __name__ ASC)"},    ]}

For more information, see theQuery Explain report reference.

Use Query Explain with the analyze option

You can use the client libraries to submit an analyze option request.

Note that requests are authenticated with IAM, using the samepermissions for regular query operations. Other authentication techniques, likeFirebase Authentication, are ignored. For more information, see the guide onIAM for server client libraries.

Note: One query execution is analyzed at a time; therefore, query timingresults may vary from request to request. The tool can help you spot significantstructural inefficiencies, but is not intended to provide aggregate statisticson query performance.
Java (Admin)
Queryq=db.collection("col").whereGreaterThan("a",1);ExplainOptionsoptions=ExplainOptions.builder().setAnalyze(true).build();ExplainResults<QuerySnapshot>explainResults=q.explain(options).get();ExplainMetricsmetrics=explainResults.getMetrics();PlanSummaryplanSummary=metrics.getPlanSummary();List<Map<String,Object>>indexesUsed=planSummary.getIndexesUsed();ExecutionStatsstats=metrics.getExecutionStats();
Node (Admin)
constq=db.collection('col').where('country','=','USA');constoptions={analyze:'true'};constexplainResults=awaitq.explain(options);constmetrics=explainResults.metrics;constplan=metrics.planSummary;constindexesUsed=plan.indexesUsed;conststats=metrics.executionStats;

The following example shows thestats object returned in addition toplanInfo.The exact format of the response depends on the execution environment. Theexample response is in JSON format.

{    "resultsReturned": "5",    "executionDuration": "0.100718s",    "readOperations": "5",    "debugStats": {               "index_entries_scanned": "95000",               "documents_scanned": "5"               "billing_details": {                     "documents_billable": "5",                     "index_entries_billable": "0",                     "small_ops": "0",                     "min_query_cost": "0",               }    }}

For more information, see theQuery Explain report reference.

Interpret results and make adjustments

Let's look at an example scenario in which we query movies by genre andcountry of production.

Note: Query Explain is designed for useful ad hoc analysis; its reportformat will evolve to maximize ease of reading and understanding, notsuitability for machine processing. Some metrics are expected to changeasCloud Firestore evolves (i.e., metrics may be added, removed, or updated) andare not covered by the same deprecation policy as otherCloud Firestore APIs. Formore information, see theQuery Explain report reference.

For illustration, assume the equivalent of this SQL query.

SELECT *FROM /moviesWHERE category = 'Romantic' AND country = 'USA';

If we use the analyze option, the returned metrics show the queryruns on two single-field indexes,(category ASC, __name__ ASC) and(country ASC, __name__ ASC). It scans 16500 index entries, but returnsonly 1200 documents.

// Output query planning info{"indexes_used":[{"query_scope":"Collection","properties":"(category ASC, __name__ ASC)"},{"query_scope":"Collection","properties":"(country ASC, __name__ ASC)"},]}// Output query status{"resultsReturned":"1200","executionDuration":"0.118882s","readOperations":"1200","debugStats":{"index_entries_scanned":"16500","documents_scanned":"1200""billing_details":{"documents_billable":"1200","index_entries_billable":"0","small_ops":"0","min_query_cost":"0",}}}

To optimize the performance of executing the query, you can create afully-covered composite index(category ASC, country ASC, __name__ ASC).

Running the query with the analyze option again we can see that thenewly-created index is selected for this query, and the query runs much fasterand more efficiently.

// Output query planning info{"indexes_used":[{"query_scope":"Collection","properties":"(category ASC, country ASC,  __name__ ASC)"}]}// Output query stats{"resultsReturned":"1200","executionDuration":"0.026139s","readOperations":"1200","debugStats":{"index_entries_scanned":"1200","documents_scanned":"1200""billing_details":{"documents_billable":"1200","index_entries_billable":"0","small_ops":"0","min_query_cost":"0",}}}

Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.

Last updated 2026-02-18 UTC.