8.2 KiB
@startDocuBlock post_api_explain @brief explain an AQL query and return information about it
@RESTHEADER{POST /_api/explain, Explain an AQL query, explainQuery}
A JSON object describing the query and query parameters.
@RESTBODYPARAM{query,string,required,string} the query which you want explained; If the query references any bind variables, these must also be passed in the attribute bindVars. Additional options for the query can be passed in the options attribute.
@RESTBODYPARAM{bindVars,array,optional,object} key/value pairs representing the bind parameters.
@RESTBODYPARAM{options,object,optional,explain_options} Options for the query
@RESTSTRUCT{allPlans,explain_options,boolean,optional,} if set to true, all possible execution plans will be returned. The default is false, meaning only the optimal plan will be returned.
@RESTSTRUCT{maxNumberOfPlans,explain_options,integer,optional,int64} an optional maximum number of plans that the optimizer is allowed to generate. Setting this attribute to a low value allows to put a cap on the amount of work the optimizer does.
@RESTSTRUCT{optimizer.rules,explain_options,array,optional,string}
an array of to-be-included or to-be-excluded optimizer rules
can be put into this attribute, telling the optimizer to include or exclude
specific rules. To disable a rule, prefix its name with a -
, to enable a rule, prefix it
with a +
. There is also a pseudo-rule all
, which will match all optimizer rules.
@RESTDESCRIPTION
To explain how an AQL query would be executed on the server, the query string can be sent to the server via an HTTP POST request. The server will then validate the query and create an execution plan for it. The execution plan will be returned, but the query will not be executed.
The execution plan that is returned by the server can be used to estimate the probable performance of the query. Though the actual performance will depend on many different factors, the execution plan normally can provide some rough estimates on the amount of work the server needs to do in order to actually run the query.
By default, the explain operation will return the optimal plan as chosen by the query optimizer The optimal plan is the plan with the lowest total estimated cost. The plan will be returned in the attribute plan of the response object. If the option allPlans is specified in the request, the result will contain all plans created by the optimizer. The plans will then be returned in the attribute plans.
The result will also contain an attribute warnings, which is an array of warnings that occurred during optimization or execution plan creation. Additionally, a stats attribute is contained in the result with some optimizer statistics. If allPlans is set to false, the result will contain an attribute cacheable that states whether the query results can be cached on the server if the query result cache were used. The cacheable attribute is not present when allPlans is set to true.
Each plan in the result is a JSON object with the following attributes:
-
nodes: the array of execution nodes of the plan. The array of available node types can be found here
-
estimatedCost: the total estimated cost for the plan. If there are multiple plans, the optimizer will choose the plan with the lowest total cost.
-
collections: an array of collections used in the query
-
rules: an array of rules the optimizer applied. An overview of the available rules can be found here
-
variables: array of variables used in the query (note: this may contain internal variables created by the optimizer)
@RESTRETURNCODES
@RESTRETURNCODE{200} If the query is valid, the server will respond with HTTP 200 and return the optimal execution plan in the plan attribute of the response. If option allPlans was set in the request, an array of plans will be returned in the allPlans attribute instead.
@RESTRETURNCODE{400} The server will respond with HTTP 400 in case of a malformed request, or if the query contains a parse error. The body of the response will contain the error details embedded in a JSON object. Omitting bind variables if the query references any will also result in an HTTP 400 error.
@RESTRETURNCODE{404} The server will respond with HTTP 404 in case a non-existing collection is accessed in the query.
@EXAMPLES
Valid query
@EXAMPLE_ARANGOSH_RUN{RestExplainValid} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn); for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); } body = { query : "FOR p IN products RETURN p" };
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN
A plan with some optimizer rules applied
@EXAMPLE_ARANGOSH_RUN{RestExplainOptimizerRules} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn); db.products.ensureSkiplist("id"); for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); } body = { query : "FOR p IN products LET a = p.id FILTER a == 4 LET name = p.name SORT p.id LIMIT 1 RETURN name", };
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN
Using some options
@EXAMPLE_ARANGOSH_RUN{RestExplainOptions} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn); db.products.ensureSkiplist("id"); for (var i = 0; i < 10; ++i) { db.products.save({ id: i }); } body = { query : "FOR p IN products LET a = p.id FILTER a == 4 LET name = p.name SORT p.id LIMIT 1 RETURN name", options : { maxNumberOfPlans : 2, allPlans : true, optimizer : { rules: [ "-all", "+use-index-for-sort", "+use-index-range" ] } } };
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN
Returning all plans
@EXAMPLE_ARANGOSH_RUN{RestExplainAllPlans} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn); db.products.ensureHashIndex("id"); body = { query : "FOR p IN products FILTER p.id == 25 RETURN p", options: { allPlans: true } };
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN
A query that produces a warning
@EXAMPLE_ARANGOSH_RUN{RestExplainWarning} var url = "/_api/explain"; body = { query : "FOR i IN 1..10 RETURN 1 / 0" };
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
@END_EXAMPLE_ARANGOSH_RUN
Invalid query (missing bind parameter)
@EXAMPLE_ARANGOSH_RUN{RestExplainInvalid} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn); body = { query : "FOR p IN products FILTER p.id == @id LIMIT 2 RETURN p.n" };
var response = logCurlRequest('POST', url, body);
assert(response.code === 400);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN
The data returned in the plan attribute of the result contains one element per AQL top-level statement
(i.e. FOR
, RETURN
, FILTER
etc.). If the query optimizer removed some unnecessary statements,
the result might also contain less elements than there were top-level statements in the AQL query.
The following example shows a query with a non-sensible filter condition that the optimizer has removed so that there are less top-level statements.
@EXAMPLE_ARANGOSH_RUN{RestExplainEmpty} var url = "/_api/explain"; var cn = "products"; db._drop(cn); db._create(cn, { waitForSync: true }); body = '{ "query" : "FOR i IN [ 1, 2, 3 ] FILTER 1 == 2 RETURN i" }';
var response = logCurlRequest('POST', url, body);
assert(response.code === 200);
logJsonResponse(response);
~ db._drop(cn); @END_EXAMPLE_ARANGOSH_RUN @endDocuBlock