Implementation:Arize ai Phoenix SpanQuery DSL
| Knowledge Sources | |
|---|---|
| Domains | AI_Observability, Client_SDK, Query_Language |
| Last Updated | 2026-02-14 05:30 GMT |
Overview
A span query DSL (Domain-Specific Language) with field selection, filtering, explosion, concatenation, renaming, and indexing operations for retrieving and reshaping span data from the Phoenix server.
Description
The SpanQuery DSL module provides a fluent, chainable interface for constructing queries against Phoenix span data. All query operations return new immutable SpanQuery instances, enabling a functional builder pattern.
The module includes five dataclass types:
- Projection -- Represents a field selection. Wraps a single
keystring and validates that it is not empty. - SpanFilter -- Represents a filter condition as a string expression. On initialization, it automatically replaces legacy field names (e.g.,
context.span_idtospan_id) for backward compatibility. - Explosion -- Represents an operation that expands a nested or repeated field into separate rows, similar to a SQL
UNNEST. Tracks aprimary_index_key(defaulting tocontext.span_id) and optional keyword arguments for mapping nested fields to columns. - Concatenation -- Represents an operation that joins values of a repeated field into a single string with a configurable separator (default
"\n\n"). - SpanQuery -- The main query builder. It holds optional references to
_select,_filter,_explode,_concat,_rename, and_indexcomponents, and provides chainable methods to compose them.
The module also maintains backward compatibility mappings that translate legacy Phoenix field names (e.g., cumulative_token_count.completion) to their current equivalents (e.g., cumulative_llm_token_count_completion). A _normalize_field() function handles both alias expansion (e.g., span_id to context.span_id) and legacy name replacement.
The to_dict() method on SpanQuery serializes the entire query into a dictionary suitable for sending as a JSON request body. If no explicit index has been set, it defaults to context.span_id.
Usage
Use SpanQuery when programmatically querying span data from a Phoenix server through the client SDK. The fluent API allows constructing queries with field selection, filtering, expansion of nested structures, and column renaming, all without string manipulation.
Code Reference
Source Location
- Repository: Arize_ai_Phoenix
- File: packages/phoenix-client/src/phoenix/client/types/spans.py
Signature
@dataclass
class Projection:
key: str
def to_dict(self) -> dict[str, Any]: ...
@dataclass
class SpanFilter:
condition: str
valid_eval_names: Optional[list[str]] = None
def to_dict(self) -> dict[str, Any]: ...
@dataclass
class Explosion:
key: str
kwargs: dict[str, str] = field(default_factory=dict)
primary_index_key: str = "context.span_id"
def to_dict(self) -> dict[str, Any]: ...
@dataclass
class Concatenation:
key: str
kwargs: dict[str, str] = field(default_factory=dict)
separator: str = "\n\n"
def to_dict(self) -> dict[str, Any]: ...
@dataclass
class SpanQuery:
def select(self, *fields: str) -> "SpanQuery": ...
def where(self, condition: str) -> "SpanQuery": ...
def explode(self, key: str, **kwargs: str) -> "SpanQuery": ...
def concat(self, key: str, **kwargs: str) -> "SpanQuery": ...
def rename(self, **kwargs: str) -> "SpanQuery": ...
def with_index(self, key: str) -> "SpanQuery": ...
def to_dict(self) -> dict[str, Any]: ...
Import
from phoenix.client.types.spans import SpanQuery, Projection, SpanFilter, Explosion, Concatenation
I/O Contract
SpanQuery Methods
| Method | Parameters | Returns | Description |
|---|---|---|---|
select() |
*fields: str |
SpanQuery |
Select specific span fields to include in results |
where() |
condition: str |
SpanQuery |
Apply a filter condition to restrict returned spans |
explode() |
key: str, **kwargs: str |
SpanQuery |
Expand a nested/repeated field into separate rows |
concat() |
key: str, **kwargs: str |
SpanQuery |
Concatenate a repeated field's values into a single string |
rename() |
**kwargs: str |
SpanQuery |
Rename fields in the output (old_name=new_name) |
with_index() |
key: str |
SpanQuery |
Set the index field for the result (default context.span_id) |
to_dict() |
(none) | dict[str, Any] |
Serialize the query to a dictionary for API requests |
Serialized Output Structure
| Key | Type | Description |
|---|---|---|
select |
dict[str, dict] |
Mapping of field names to Projection dicts |
filter |
dict |
Contains condition string
|
explode |
dict |
Contains key, primary_index_key, and optional kwargs
|
concat |
dict |
Contains key, separator, and optional kwargs
|
rename |
dict[str, str] |
Old-name to new-name mapping |
index |
dict |
Contains key (defaults to context.span_id)
|
Usage Examples
from phoenix.client.types.spans import SpanQuery
# Simple field selection with a filter
query = (
SpanQuery()
.select("name", "status_code", "latency_ms", "llm.token_count.total")
.where("status_code == 'ERROR'")
)
print(query.to_dict())
# {
# "select": {"name": {"key": "name"}, "status_code": {"key": "status_code"}, ...},
# "filter": {"condition": "status_code == 'ERROR'"},
# "index": {"key": "context.span_id"}
# }
# Explode retrieval documents into separate rows
query = (
SpanQuery()
.select("name")
.where("span_kind == 'RETRIEVER'")
.explode("retrieval.documents", text="document.content", score="document.score")
)
# Concatenate document content into a single string
query = (
SpanQuery()
.select("name")
.concat("retrieval.documents", text="document.content")
)
# Rename output columns and set custom index
query = (
SpanQuery()
.select("input.value", "output.value")
.rename(input_value="prompt", output_value="response")
.with_index("context.trace_id")
)
Related Pages
- Principle:Arize_ai_Phoenix_Annotation_Querying
- Arize_ai_Phoenix_Generated_V1_Types -- API types returned by span queries