Natural language (Ask)
Send a question in plain English; OriginChain compiles it to a query plan and returns rows. Useful for ad-hoc reporting, AI agents that need to query the database, and as a fallback when you'd rather not write SQL.
The compiler runs a deterministic rule grammar first. If the grammar can't resolve a phrase against the catalog, it falls back to whatever LLM is configured on the instance. The compiled plan is cached - repeat questions are answered without recompiling.
1. Ask a question.
Send a sentence. Get rows. The schemas field tells the compiler which tables to consider - omit it to allow every registered schema (slower because the catalog is larger).
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/ask" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nl": "last 10 orders where status is pending",
"schemas": ["shop.orders"]
}'result = db.ask(
"last 10 orders where status is pending",
schemas=["shop.orders"],
)
for row in result["rows"]:
print(row)const result = await db.ask(
"last 10 orders where status is pending",
{ schemas: ["shop.orders"] },
);
for (const row of result.rows) {
console.log(row);
}result, err := db.Ask(ctx, "last 10 orders where status is pending")
if err != nil { /* handle */ }
for _, row := range result.Rows {
fmt.Println(row)
} | Field | Required | Notes |
|---|---|---|
| nl | yes | The question in plain English. Aim for one sentence - multi-step questions are best split. |
| schemas | no | Array of schema names to consider. Omitting it allows all schemas but is slower for large catalogs. |
| show_plan | no | When true, the response includes the executed plan tree. |
- Ambiguous column references. "Top customers by amount" doesn't say whether "amount" is per-order or aggregated. Be specific: "top customers by total order amount".
- Not scoping schemas. If you know the question is only about orders, pass
schemas=["shop.orders"]- the compiler is faster and less likely to pick the wrong table. - Sensitive data in questions. If you fall back to the LLM, the question gets sent to whatever LLM is configured. Don't put PII or secrets in the question.
2. Inspect the plan.
Set show_plan: true to see the query plan the compiler picked. Useful when you're going to call the same question in a hot path - you'll want to know whether it's hitting the right indexes.
curl -X POST "https://$OC_HOST/v1/tenants/$OC_TENANT/ask" \
-H "Authorization: Bearer $OC_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"nl": "top 5 customers by total spend",
"schemas": ["shop.orders", "shop.customers"],
"show_plan": true
}'# show_plan returns the executor plan alongside the rows.
# Useful for verifying that the compiler hit the right indexes.
result = db.ask(
"top 5 customers by total spend",
schemas=["shop.orders", "shop.customers"],
)
print(result.get("plan"))
print(result["rows"]) {
"rows": [
{ "customer_id": "c_3", "total": 124900 },
{ "customer_id": "c_7", "total": 98200 },
...
],
"cache": "miss",
"plan": { "kind": "Aggregate", "by": ["customer_id"], ... }
}
For the operator catalog (Scan, Filter, HashJoin, Aggregate, etc.), see core concepts → plan tree.
3. The plan cache.
Every response includes a cache field with one of three values:
| Value | What happened |
|---|---|
| "hit" | Cached plan was reused. Fast. |
| "miss" | First time seeing this question (or close to it). Compiled fresh, then cached. |
| "skip" | Cache was bypassed, usually because schemas were modified since the entry was cached. |
Schema migrations evict cached entries that referenced the affected tables. There's no manual eviction endpoint today - if you need to clear the cache, register the schema again (a no-op write counts as a migration).