Check the v0.2 graph update: broader Cypher path coverage, multi-segment `shortestPath` and `allShortestPaths`, named paths across multiple variable-length segments, stronger undirected fast paths, runtime-observed graph `EXPLAIN`, Neo4j-oriented compatibility evidence, and a current benchmark snapshot vs Neo4j, SurrealDB, and pgstack. Read the full v0.2 breakdown.

Explain JSON

AionDB exposes a structured EXPLAIN payload for graph and hybrid queries.

New in v0.2: EXPLAIN (FORMAT JSON) and EXPLAIN (ANALYZE, FORMAT JSON) are now explicit supported surfaces with a versioned contract for graph tooling. See What's New in v0.2.

Supported forms:

EXPLAIN (FORMAT JSON)
MATCH (a)-[:KNOWS]->(b)
RETURN b.id;

EXPLAIN (ANALYZE, FORMAT JSON)
MATCH (a)-[:KNOWS]->(b)
RETURN b.id;

FORMAT JSON returns a single-row JSON document instead of the usual multi-line text plan. ANALYZE keeps the same JSON shape and adds runtime data such as actual rows, clause input/output rows, selectivity, and lightweight timings.

Versioned contract

The payload is versioned:

Clients should tolerate additive fields and reject only on incompatible schema_version or format_kind.

Top-level shape

FieldMeaning
query_plan_linesFull text EXPLAIN output preserved as an array of lines.
plan_linesNon-graph EXPLAIN lines.
structural_plan_linesplan_lines without runtime summary lines such as Execution: or Rows Returned:.
graph_linesHuman-readable graph observability lines.
plan_overviewStable summary of the non-graph plan root and primary operator.
graph_summaryStable machine-readable summary of graph risk, pivots, joins, and drift.
graph_detailClause-level and pattern-level graph details.
execution_summaryRuntime summary when ANALYZE is used.

plan_overview

plan_overview is meant to give a small stable SQL-side summary for tooling and UI.

Fields:

Current plan_category values include:

Current plan_subcategory values include:

graph_summary

graph_summary is the compact machine-readable graph health block.

Important fields include:

Current severity values are:

graph_detail

graph_detail contains:

Each clause can expose:

join_risk can expose:

Each pattern detail can expose:

Provenance fields

The graph payload carries explicit provenance for the most important runtime-facing values.

Common summary-level provenance fields:

Common clause and pattern provenance fields:

Current values are:

Practical reading:

observed means the engine saw the behavior at runtime. inferred means the value was derived from static plan shape or planner metadata. mixed means the final summary combines both static and runtime signals. unavailable means the metric is only meaningful under ANALYZE and no runtime evidence exists.

execution_summary

execution_summary is present in both modes, but runtime values are only populated under ANALYZE.

Fields:

Under plain EXPLAIN (FORMAT JSON), these runtime fields can be null.

Consuming the payload

From a SQL client such as psql, FORMAT JSON returns a single text cell that contains the JSON document. That path is useful for ad hoc inspection, shell tooling, and compatibility with existing SQL clients.

Inside the engine, prefer the structured helpers instead of reparsing text output:

Those helpers:

Minimal Rust sketch:

use aiondb_engine::engine::api::QueryEngine;

fn load_graph_summary(
    engine: &dyn QueryEngine,
    session: &aiondb_engine::session::SessionHandle,
) -> aiondb_core::error::DbResult<serde_json::Value> {
    engine.execute_explain_graph_summary_json(
        session,
        "MATCH (a)-[:KNOWS]->(b) RETURN b.id",
        true,
    )
}

For UI or telemetry work:

Prefer the provenance companions when deciding how strongly to present a signal:

Text EXPLAIN provenance

The plain multi-line text EXPLAIN surface now also exposes provenance on the most important human-readable lines.

Typical examples:

Use the text form for ad hoc debugging and operator review. Use the JSON form for product logic, telemetry ingestion, or UI state.

Example

Abbreviated payload:

{
  "schema_version": 1,
  "format_kind": "aiondb.explain_json",
  "plan_overview": {
    "root_kind": "Cypher Query",
    "primary_operator_kind": "Nested Loop",
    "plan_category": "join",
    "plan_subcategory": "nested_loop"
  },
  "graph_summary": {
    "severity": "watch",
    "fragile_pivots": 1,
    "pivot_driver_metrics_source": "inferred",
    "drift_metrics_source": "unavailable",
    "risky_join_clauses": 0,
    "join_risk_metrics_source": "unavailable",
    "max_fanout": null
  },
  "graph_detail": {
    "summary": {
      "severity": "watch"
    },
    "clauses": [
      {
        "kind": "PipelineMatch",
        "pattern_details": [
          {
            "pattern_runtime_strategy": "left_to_right_node_seed",
            "pattern_runtime_strategy_source": "observed",
            "seed_mode": "label_scan",
            "pivot_decision": "retained_leftmost",
            "pivot_decision_source": "inferred"
          }
        ]
      }
    ]
  },
  "execution_summary": {
    "kind": "Query",
    "rows_returned": 1,
    "memory_used_bytes": 5283
  }
}

Reading common graph cases

Two patterns matter in practice:

Correlated shared-anchor fanout

Example query:

EXPLAIN (ANALYZE, FORMAT JSON)
MATCH (a)-[:KNOWS]->(b), (a)-[:KNOWS]->(c)
RETURN a.id, b.id, c.id;

Typical signals to expect:

How to read it:

When this shape becomes expensive, look first at:

Independent multi-scan

Example query:

EXPLAIN (ANALYZE, FORMAT JSON)
MATCH (a:Person), (b:Company)
RETURN a.id, b.id;

Typical signals to expect:

How to read it:

When this shape is surprising, verify first that:

Pattern-level seed and pivot signals

Within graph_detail.clauses[*].pattern_details[*], the fields below are the quickest way to understand why one branch is expensive:

Practical reading:

watch versus risk

Use the top-level graph_summary.severity as the first triage signal:

Typical watch situations:

Typical risk situations:

Example risk case

Example query shape:

EXPLAIN (ANALYZE, FORMAT JSON)
MATCH (a:Person), (b:Company)
RETURN a.id, b.id;

If both branches are broad enough, the JSON can move from watch to risk:

{
  "graph_summary": {
    "severity": "risk",
    "independent_multi_scan": 1,
    "risky_join_clauses": 1,
    "high_risk_join_clauses": 1,
    "max_fanout": 9.0
  }
}

How to read it:

In practice, treat risk as a prompt to inspect:

UI and tooling guidance

The JSON payload is meant to support both:

Stable fields for summary cards

For a top-level UI summary, prefer:

Those fields are the best compact signals for:

Fields for drill-down views

For an expandable detail panel, prefer:

Those are the fields that usually explain why the summary is red or yellow.

Text lines versus structured fields

Treat the JSON objects as the stable contract.

Do not build product logic on:

Those lines are still useful for:

But the structured contract should be preferred for:

External compatibility posture

This JSON contract is intended for AionDB-native tooling. It is versioned, but it is not a cross-database interoperability format.

That means:

Suggested UI mappings

The payload does not prescribe UI colors or wording, but using a consistent mapping across tools makes the output easier to compare.

Severity

Suggested mapping:

Field valueSuggested labelSuggested tone
okHealthyneutral/green
watchWatchcaution/yellow
riskRiskstrong warning/red

Use graph_summary.severity for the top-level badge. If clause-level or pattern-level warnings are shown, keep them subordinate to the top-level severity instead of inventing a second competing global status.

Join shape

Suggested mapping for graph_detail.clauses[*].join_risk.join_shape:

Field valueSuggested labelPractical meaning
correlated_shared_anchorCorrelated starMultiple branches expanding from the same bound anchor
correlated_non_sharedCorrelated multi-branchCorrelated clause without a single shared star anchor
shared_anchor_uncorrelatedUncorrelated starShared local anchor, but no incoming correlation from an earlier binding
independent_multi_scanIndependent multi-scanClause behaves like a product across independent branches

Seed mode

Suggested mapping for graph_detail.clauses[*].pattern_details[*].seed_mode:

Field valueSuggested labelPractical meaning
id_constrainedID constrainedPattern starts from a highly selective id-based seed
indexedIndexed seedPattern starts from an indexed property path
range_constrainedRange constrainedPattern starts from a range-filtered seed
label_scanLabel scanPattern starts from a label-wide scan
anonymous_scanAnonymous scanPattern starts from an unconstrained anonymous scan

When a UI needs only one compact warning signal at pattern level, seed_mode = "label_scan" plus warning_severity = "high" is the most important combination to highlight first.

Seed binding state

Suggested mapping for seed_binding_state:

Field valueSuggested labelPractical meaning
preboundPreboundExpand from an already bound variable
freshFresh seedNew seed introduced at this pattern
anonymousAnonymous seedSeed is not carried as a named variable
unknownUnknownEngine could not classify binding state precisely

Suggested display order

For a compact pattern card, this order works well:

  1. shape
  2. warning_severity
  3. seed_mode
  4. seed_binding_state
  5. pivot_decision
  6. actual_rows
  7. estimate_error_ratio
  8. actual_time_ms

That order keeps the structural explanation ahead of the raw numbers.

Consumer checklist

If you are building a UI, telemetry adapter, or planner feedback client on top of this payload, keep the client logic conservative.

Validate first

On receipt:

  1. check that the payload is valid JSON;
  2. check format_kind == "aiondb.explain_json";
  3. check schema_version;
  4. tolerate unknown top-level and nested fields.

Reject the payload only when:

Prefer structured fields over text

For product logic:

Use text lines only for:

Log enough context

When storing or forwarding the payload, log at least:

That is usually enough to keep old snapshots interpretable after the format evolves.

Handle unknown enum-like values safely

Fields such as:

should be treated as open sets, not closed enums.

If a value is unknown:

Degrade by feature, not globally

If a client cannot interpret:

it should usually keep the rest of the payload usable.

Good fallback examples:

Bad fallback example:

Keep runtime expectations explicit

Do not assume runtime fields are always available.

Under plain EXPLAIN (FORMAT JSON):

If a feature requires runtime truth, gate it explicitly on:

Schema evolution policy

schema_version = 1 is the current contract version.

The intended compatibility rule is:

Changes that should remain compatible within version 1

Examples:

Clients are expected to tolerate those changes without failing.

Changes that should require a new schema version

Examples:

If such a change is necessary, the producer should:

When extending the payload:

  1. prefer adding fields over rewriting existing ones;
  2. keep enum-like fields open for future values;
  3. keep text lines secondary to structured fields;
  4. update examples and tests when the structured contract changes.

When reading the payload:

  1. branch first on format_kind;
  2. then branch on schema_version;
  3. treat unknown fields as ignorable by default;
  4. treat unknown enum-like values as display fallbacks, not fatal errors.

This keeps version 1 usable even as the graph observability surface grows.