Inspect how Ask answers
The language tabs use earlier client interfaces. Check SDK versions and connection setup before running them.
Request show_plan to see the operations used to answer the question. Check the plan to confirm the intended table and filter.
Before you run it: Putting show_plan in the URL does not enable it. Plans may change with schemas and indexes.
A query plan is a list of database operations. In this example, IndexScan finds paid orders and Aggregate(count) counts them. Read those operations alongside the returned count before relying on the answer.
- Ask about paid ordersThe question names the rows you want to count.
- Inspect the query planCheck the orders table, paid filter and counting step.
- Verify the countCompare the answer with the paid sample rows.
Follow the example
- Load the sample and confirm Ask is enabled.
- Set show_plan: true in the JSON body.
- Send the question.
- Compare the plan and rows with the intended filter.
Set up the table
Save this definition as orders.toml and send it to POST /v1/tenants/:t/schemas with Content-Type: text/plain before loading rows. The schema walkthrough shows how to register a file; use this page's definition for its sample.
namespace = "shop"
table = "orders"
primary_key = ["id"]
[[columns]]
name = "id"
ty = "str"
required = true
[[columns]]
name = "customer_id"
ty = "str"
[[columns]]
name = "amount"
ty = "f64"
[[columns]]
name = "status"
ty = "str"
[[indexes]]
name = "by_status"
columns = ["status"]Load sample rows
# Seed a handful of paid and pending orders so the plan has work to do.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/rows/shop.orders/_batch" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '[
{ "id": "o_1", "customer_id": "c_1", "amount": 19.00, "status": "paid" },
{ "id": "o_2", "customer_id": "c_2", "amount": 42.00, "status": "paid" },
{ "id": "o_3", "customer_id": "c_1", "amount": 11.50, "status": "pending" }
]'Run the request
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/ask" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nl": "count of paid orders",
"schemas": ["shop.orders"],
"show_plan": true
}'Check the result
The response includes the plan alongside rows and cache information.
{
"rows": [
{ "count": 2 }
],
"cache": "miss",
"plan": {
"Aggregate": {
"group": [],
"aggs": [ { "Count": { "alias": "count" } } ],
"child": {
"Filter": {
"predicate": { "Eq": { "path": "status", "value": "paid" } },
"child": { "IndexScan": { "schema": "shop.orders", "index": "by_status" } }
}
}
}
}
}The plan field is the same JSON the engine's /v1/query endpoint accepts as input. Lift it, store it, replay it - the planner is deterministic for a fixed schema set.
Understand the behavior
- The compiler runs as usual. When
show_planis true, the resulting plan tree is serialised back into the response alongside the rows. - The plan here is
Aggregate(count) → Filter(status = paid) → IndexScan(by_status). The presence ofIndexScaninstead of plainScantells you the planner picked up theby_statusindex. - If you see
Scanwhere you expectedIndexScan, the index probably doesn't cover the predicate - add an index or rephrase.
Important limits and checks
- Putting
show_planin the URL. It's a body field. The query string is ignored. - Logging the plan tree on every request. It's a few KB and changes the response shape. Use it during development; turn it off in app code.
- Assuming the plan is stable across schema changes. Adding or dropping an index re-plans. The previous plan tree is no longer the one the engine will run - re-fetch it.