PPL Command Reference
import { Tabs, TabItem, Aside } from ‘@astrojs/starlight/components’;
This reference covers every PPL command available in OpenSearch. Each command includes syntax, parameters, and examples you can run against live OpenTelemetry data in the [playground](https://observability.playground.opensearch.org/w/19jD-R/app/explore/logs/#/?_g=(filters:!(),refreshInterval:(pause:!t,value:0),time:(from:now-6h,to:now))&_q=(dataset:(id:d1f424b0-2655-11f1-8baa-d5b726b04d73,timeFieldName:time,title:‘logs-otel-v1*‘,type:INDEX_PATTERN),language:PPL,query:”)&_a=(legacy:(columns:!(body,severityText,resource.attributes.service.name),interval:auto,isDirty:!f,sort:!()),tab:(logs:(),patterns:(usingRegexPatterns:!f)),ui:(activeTabId:logs,showHistogram:!t)).
Query structure
Section titled “Query structure”Every PPL query starts with a search command (or just source=), followed by a pipeline of commands separated by the pipe character (|):
source = <index-pattern>| command1| command2| command3In the Discover UI, the source is set automatically by the selected dataset, so queries typically begin with |:
| where severityText = 'ERROR'| stats count() as errors by `resource.attributes.service.name`Search and filter
Section titled “Search and filter”search
Section titled “search”Retrieve documents from an index. This is always the first command in a PPL query. The search keyword can be omitted.
Syntax:
search source=<index> [<boolean-expression>]source=<index> [<boolean-expression>]| Parameter | Required | Description |
|---|---|---|
<index> | Yes | Index name or pattern to query |
<boolean-expression> | No | Initial filter condition |
Example - Get all logs:
source = logs-otel-v1*Example - Search with inline filter:
source = logs-otel-v1* severityText = 'ERROR'Filter results using boolean expressions. Only rows where the expression evaluates to true are returned.
Syntax:
where <boolean-expression>| Parameter | Required | Description |
|---|---|---|
<boolean-expression> | Yes | Condition that evaluates to true/false |
Supports operators: =, !=, >, <, >=, <=, AND, OR, NOT, LIKE, IN, BETWEEN, IS NULL, IS NOT NULL.
Example - Filter error logs:
| where severityText = 'ERROR' or severityText = 'FATAL'| head 20Example - Compound conditions:
| where severityNumber >= 17 AND `resource.attributes.service.name` = 'checkout'| head 20(experimental, since 3.3)
Filter results by matching field values against a regular expression pattern.
Syntax:
regex <field> = <pattern>regex <field> != <pattern>| Parameter | Required | Description |
|---|---|---|
<field> | Yes | Field to match against |
<pattern> | Yes | Java regex pattern |
Example - Filter services matching a pattern:
| regex `resource.attributes.service.name` = ".*agent.*"| head 20subquery
Section titled “subquery”(experimental, since 3.0)
Embed one PPL query inside another for complex filtering.
Syntax:
where <field> [not] in [ source=<index> | ... ]where [not] exists [ source=<index> | ... ]Example - Find logs from services that have errors in traces:
source = logs-otel-v1*| where `resource.attributes.service.name` in [ source = otel-v1-apm-span-* | where status.code = 2 ]Field selection and transformation
Section titled “Field selection and transformation”fields
Section titled “fields”Keep or remove fields from search results.
Syntax:
fields [+|-] <field-list>| Parameter | Required | Description |
|---|---|---|
<field-list> | Yes | Comma-delimited list of fields |
+ or - | No | + includes (default), - excludes |
Example - Select specific fields:
| fields time, body, severityText, `resource.attributes.service.name`| head 20Example - Exclude fields:
| fields - traceId, spanId| head 20(experimental, since 3.3)
Alias for fields with enhanced syntax. Supports space-delimited field lists.
Syntax:
table [+|-] <field-list>Example:
| table time body severityText| head 20rename
Section titled “rename”Rename one or more fields. Supports wildcard patterns.
Syntax:
rename <source-field> AS <target-field> [, <source-field> AS <target-field>]...Example:
| rename `resource.attributes.service.name` as service| head 20Evaluate an expression and append (or overwrite) the result as a new field.
Syntax:
eval <field> = <expression> [, <field> = <expression>]...Example - Calculate duration in milliseconds:
source = otel-v1-apm-span-*| eval duration_ms = durationInNanos / 1000000| sort - duration_ms| head 10Example - Concatenate fields:
| eval service_operation = concat(`resource.attributes.service.name`, '/', body)| head 20convert
Section titled “convert”(experimental, since 3.5)
Transform field values to numeric values using specialized conversion functions.
Syntax:
convert (auto | ctime | dur2sec | memk | mktime | mstime | num | rmcomma | rmunit) (<field>) [as <alias>] [, ...]replace
Section titled “replace”(experimental, since 3.4)
Replace text in one or more fields.
Syntax:
replace (<regex>, <replacement>) in <field> [, <field>]...Example:
| replace ("error", "ERROR") in body| head 20fillnull
Section titled “fillnull”(experimental, since 3.0)
Fill null values with a specified value.
Syntax:
fillnull value=<value> <field-list>fillnull using <field> = <value> [, <field> = <value>]Example:
| fillnull value='unknown' `resource.attributes.service.name`| head 20expand
Section titled “expand”(experimental, since 3.1)
Expand a nested array field into multiple documents (one per array element).
Syntax:
expand <field> [as <alias>]flatten
Section titled “flatten”(experimental, since 3.1)
Flatten a struct/object field into separate top-level fields.
Syntax:
flatten <field> [as (<alias-list>)]Aggregation and statistics
Section titled “Aggregation and statistics”Calculate aggregations from search results. The workhorse of PPL analytics.
Syntax:
stats <aggregation>... [by <field-list>]stats <aggregation>... [by span(<field>, <interval>) [as <alias>], <field-list>]| Parameter | Required | Description |
|---|---|---|
<aggregation> | Yes | Aggregation function (count, sum, avg, max, min, etc.) |
by <field-list> | No | Group results by one or more fields |
span(<field>, <interval>) | No | Create time or numeric buckets |
Example - Count logs by service:
| stats count() as log_count by `resource.attributes.service.name`Example - Error rate by service:
| stats count() as total, sum(case(severityText = 'ERROR', 1 else 0)) as errors by `resource.attributes.service.name`| eval error_rate = errors * 100.0 / total| sort - error_rateExample - Time-bucketed log volume:
| stats count() as volume by span(time, 5m) as time_bucketeventstats
Section titled “eventstats”(experimental, since 3.1)
Like stats, but appends the aggregation result as a new field to every event instead of collapsing rows.
Syntax:
eventstats <function>... [by <field-list>]Example - Add service-level average alongside each log:
source = otel-v1-apm-span-*| eventstats avg(durationInNanos) as avg_duration by serviceName| eval deviation = durationInNanos - avg_duration| where deviation > avg_duration * 2streamstats
Section titled “streamstats”(experimental, since 3.4)
Calculate cumulative or rolling statistics as events are processed in order.
Syntax:
streamstats [current=<bool>] [window=<int>] <function>... [by <field-list>]| Parameter | Required | Description |
|---|---|---|
current | No | Include current event in calculation (default: true) |
window | No | Number of events for rolling window (default: 0 = all) |
Example - Rolling average latency over last 10 spans:
source = otel-v1-apm-span-*| sort startTime| streamstats window=10 avg(durationInNanos) as rolling_avg by serviceName(experimental, since 3.3)
Group numeric or time values into buckets of equal intervals.
Syntax:
bin <field> [span=<interval>] [bins=<count>]Example:
source = otel-v1-apm-span-*| eval duration_ms = durationInNanos / 1000000| bin duration_ms span=100| stats count() as spans by duration_mstimechart
Section titled “timechart”(experimental, since 3.3)
Create time-based aggregations - perfect for dashboards and trend analysis.
Syntax:
timechart [timefield=<field>] [span=<interval>] <aggregation> [by <field>]| Parameter | Required | Description |
|---|---|---|
timefield | No | Time field (default: @timestamp) |
span | No | Time interval (default: 1m) |
limit | No | Max distinct values for by field (default: 10) |
Example - Log volume over time by service:
| timechart timefield=time span=5m count() by `resource.attributes.service.name`(experimental, since 3.4)
Apply statistical aggregations with row and column splits for visualization.
Syntax:
chart <aggregation> [by <row-split> <column-split>]chart <aggregation> [over <row-split>] [by <column-split>]Example:
| chart count() by `resource.attributes.service.name`, severityTexttrendline
Section titled “trendline”(experimental, since 3.0)
Calculate moving averages of fields - simple moving average (SMA) or weighted moving average (WMA).
Syntax:
trendline [sort <field>] (sma|wma)(<window>, <field>) [as <alias>]Example:
source = otel-v1-apm-span-*| sort startTime| trendline sma(5, durationInNanos) as latency_trendaddtotals
Section titled “addtotals”(stable, since 3.5)
Add row and column totals to aggregation results.
Syntax:
addtotals [col=<bool>] [row=<bool>] [fieldname=<name>] [labelfield=<field>] [label=<string>] [<field-list>]addcoltotals
Section titled “addcoltotals”(stable, since 3.5)
Add a totals row at the bottom of results.
Syntax:
addcoltotals [labelfield=<field>] [label=<string>] [<field-list>]transpose
Section titled “transpose”(stable, since 3.5)
Transpose rows to columns - useful for pivoting aggregation results.
Syntax:
transpose [<int>] [header_field=<field>] [include_empty=<bool>] [column_name=<string>]Sorting and limiting
Section titled “Sorting and limiting”Sort results by one or more fields.
Syntax:
sort [<count>] [+|-] <field> [, [+|-] <field>]...sort [<count>] <field> [asc|desc] [, <field> [asc|desc]]...| Parameter | Required | Description |
|---|---|---|
<field> | Yes | Field to sort by |
+ or asc | No | Ascending (default) |
- or desc | No | Descending |
<count> | No | Number of results to return |
Example - Most recent logs first:
| sort - time| head 20Example - Slowest traces:
source = otel-v1-apm-span-*| sort - durationInNanos| head 10Return the first N results. Default is 10.
Syntax:
head [<size>] [from <offset>]| Parameter | Required | Description |
|---|---|---|
<size> | No | Number of results (default: 10) |
<offset> | No | Number of results to skip |
Example:
| sort - time| head 50reverse
Section titled “reverse”(experimental, since 3.2)
Reverse the display order of results.
Syntax:
reverseDeduplication and ranking
Section titled “Deduplication and ranking”Remove duplicate documents based on field values.
Syntax:
dedup [<count>] <field-list> [keepempty=<bool>] [consecutive=<bool>]| Parameter | Required | Description |
|---|---|---|
<field-list> | Yes | Fields that define uniqueness |
<count> | No | Number of duplicates to keep per group (default: 1) |
keepempty | No | Keep documents with null values (default: false) |
consecutive | No | Only remove consecutive duplicates (default: false) |
Example - One log per unique service:
| dedup `resource.attributes.service.name`Find the most common values of a field.
Syntax:
top [<N>] <field-list> [by <group-field>]| Parameter | Required | Description |
|---|---|---|
<N> | No | Number of top values (default: 10) |
<field-list> | Yes | Fields to find top values for |
Example - Top services by log volume:
| top 5 `resource.attributes.service.name`Find the least common values of a field - useful for spotting anomalies.
Syntax:
rare <field-list> [by <group-field>]Example - Rarest severity levels:
| rare severityTextText extraction and pattern matching
Section titled “Text extraction and pattern matching”Extract fields from text using regular expressions with named capture groups.
Syntax:
parse <field> <regex-pattern>Example - Extract HTTP status codes from log bodies:
| parse body 'HTTP/\d\.\d"\s+(?<statusCode>\d{3})'| stats count() as requests by statusCode(stable, since 2.4)
Extract fields using grok patterns - a higher-level abstraction over regex using predefined patterns like %{IP}, %{NUMBER}, %{GREEDYDATA}.
Syntax:
grok <field> <grok-pattern>Example - Parse structured log lines:
| grok body '%{IP:client_ip} - %{DATA:user} \[%{HTTPDATE:timestamp}\] "%{WORD:method} %{DATA:url}"'| head 20(experimental, since 3.3)
Extract fields from text using regex named capture groups, with additional options for sed-mode text substitution.
Syntax:
rex [mode=<extract|sed>] field=<field> <pattern> [max_match=<int>]| Parameter | Required | Description |
|---|---|---|
field | Yes | Source field |
<pattern> | Yes | Regex with named groups (?<name>...) |
mode | No | extract (default) or sed for substitution |
max_match | No | Max matches to extract (default: 1) |
Example - Extract key-value pairs from logs:
| rex field=body "status=(?<status>\w+)\s+latency=(?<latency>\d+)"| head 20(experimental, since 3.3)
Extract fields from structured JSON data within a text field.
Syntax:
spath input=<field> [output=<field>] [path=<json-path>]Example:
| spath input=body path=error.message output=error_msg| where isnotnull(error_msg)| stats count() by error_msgpatterns
Section titled “patterns”(stable, since 2.4)
Automatically discover log patterns by extracting and clustering similar log lines. This is one of PPL’s most powerful observability features - it replaces hours of manual regex work with a single command.
Syntax:
patterns <field> [method=simple_pattern|brain] [mode=label|aggregation] [max_sample_count=<int>]| Parameter | Required | Description |
|---|---|---|
<field> | Yes | Text field to analyze |
method | No | simple_pattern (default) or brain for smarter clustering |
mode | No | label adds pattern field, aggregation groups by pattern |
max_sample_count | No | Sample logs per pattern (default: 10) |
Example - Discover log patterns:
| patterns body method=simple_pattern mode=aggregationData combination and enrichment
Section titled “Data combination and enrichment”(stable, since 3.0)
Combine two datasets together. Supports inner, left, right, full, semi, anti, and cross joins.
Syntax:
[joinType] join [left=<alias>] [right=<alias>] on <condition> <right-dataset>| Parameter | Required | Description |
|---|---|---|
joinType | No | inner (default), left, right, full, semi, anti, cross |
on <condition> | Yes | Join condition |
<right-dataset> | Yes | Index name or subsearch |
Example - Correlate logs with trace data:
source = logs-otel-v1*| left join on traceId = traceId [ source = otel-v1-apm-span-* ]lookup
Section titled “lookup”(experimental, since 3.0)
Enrich data by looking up values from a reference index.
Syntax:
lookup <lookupIndex> <lookupKey> [as <sourceKey>] [replace|append <field-list>]| Parameter | Required | Description |
|---|---|---|
<lookupIndex> | Yes | Reference index to look up from |
<lookupKey> | Yes | Key field in the lookup index |
replace or append | No | replace overwrites, append fills nulls only |
append
Section titled “append”(experimental, since 3.3)
Append results of a subsearch to the bottom of the main search results.
Syntax:
append [<subsearch>]Example - Combine log stats with trace stats:
source = logs-otel-v1*| stats count() as log_count by `resource.attributes.service.name`| append [ source = otel-v1-apm-span-* | stats count() as log_count by serviceName as `resource.attributes.service.name` ]appendcol
Section titled “appendcol”(experimental, since 3.1)
Append subsearch results as additional columns alongside the main results.
Syntax:
appendcol [override=<bool>] [<subsearch>]multisearch
Section titled “multisearch”(experimental, since 3.4)
Execute multiple search queries and combine results.
Syntax:
multisearch [<subsearch>] [, <subsearch>]...Multivalue fields
Section titled “Multivalue fields”mvcombine
Section titled “mvcombine”(stable, since 3.4)
Combine values of a field across rows into a multivalue array.
Syntax:
mvcombine <field>(stable, since 3.6)
Convert a multivalue field to a single string by joining elements with newlines.
Syntax:
nomv <field>mvexpand
Section titled “mvexpand”(stable, since 3.6)
Expand a multi-valued field into separate documents (one per value).
Syntax:
mvexpand <field> [limit=<int>]Machine learning
Section titled “Machine learning”(stable, since 2.5)
Apply machine learning algorithms directly in your query pipeline.
Syntax (Anomaly Detection - RCF):
ml action='train' algorithm='rcf' [time_field=<field>] [anomaly_rate=<float>]Syntax (Clustering - K-Means):
ml action='train' algorithm='kmeans' [centroids=<int>] [iterations=<int>] [distance_type=<type>]| Parameter | Required | Description |
|---|---|---|
algorithm | Yes | rcf (Random Cut Forest) or kmeans |
time_field | Yes (RCF time-series) | Timestamp field for time-series anomaly detection |
centroids | No | Number of clusters for kmeans (default: 2) |
anomaly_rate | No | Expected anomaly rate for RCF (default: 0.005) |
Example - Anomaly detection on time-series data:
source = otel-v1-apm-span-*| stats avg(durationInNanos) as avg_latency by span(startTime, 1m) as minute| ml action='train' algorithm='rcf' time_field='minute'| where is_anomaly = 1kmeans
Section titled “kmeans”(stable, since 1.3)
Apply k-means clustering directly on query results.
Syntax:
kmeans [centroids=<int>] [iterations=<int>] [distance_type=COSINE|L1|EUCLIDEAN]Metadata and debugging
Section titled “Metadata and debugging”describe
Section titled “describe”(stable, since 2.1)
Query the metadata (field names, types) of an index.
Syntax:
describe <table-name>Example:
describe logs-otel-v1*explain
Section titled “explain”(stable, since 3.1)
Show the execution plan of a query - useful for debugging and optimization.
Syntax:
explain [simple|standard|cost|extended] <query>showdatasources
Section titled “showdatasources”(stable, since 2.4)
List all configured data sources in the PPL engine.
Syntax:
show datasourcesGraph traversal
Section titled “Graph traversal”graphlookup
Section titled “graphlookup”(experimental, since 3.6)
Perform recursive graph traversal on a collection using BFS - useful for tracing service dependency chains.
Syntax:
graphlookup source=<index> connectFromField=<field> connectToField=<field> as <alias> [maxDepth=<int>] [depthField=<field>]All commands at a glance
Section titled “All commands at a glance”| Command | Since | Status | Description |
|---|---|---|---|
| search | 1.0 | stable | Retrieve documents from an index |
| where | 1.0 | stable | Filter with boolean expressions |
| fields | 1.0 | stable | Keep or remove fields |
| table | 3.3 | experimental | Alias for fields with enhanced syntax |
| rename | 1.0 | stable | Rename fields |
| eval | 1.0 | stable | Evaluate expressions, create fields |
| convert | 3.5 | experimental | Convert field values to numeric |
| replace | 3.4 | experimental | Replace text in fields |
| fillnull | 3.0 | experimental | Fill null values |
| expand | 3.1 | experimental | Expand nested arrays |
| flatten | 3.1 | experimental | Flatten struct fields |
| stats | 1.0 | stable | Aggregation and grouping |
| eventstats | 3.1 | experimental | Aggregation appended to each event |
| streamstats | 3.4 | experimental | Cumulative/rolling statistics |
| bin | 3.3 | experimental | Group into numeric/time buckets |
| timechart | 3.3 | experimental | Time-based charts |
| chart | 3.4 | experimental | Aggregation with row/column splits |
| trendline | 3.0 | experimental | Moving averages |
| addtotals | 3.5 | stable | Row and column totals |
| addcoltotals | 3.5 | stable | Column totals |
| transpose | 3.5 | stable | Transpose rows to columns |
| sort | 1.0 | stable | Sort results |
| reverse | 3.2 | experimental | Reverse result order |
| head | 1.0 | stable | Return first N results |
| dedup | 1.0 | stable | Remove duplicates |
| top | 1.0 | stable | Most common values |
| rare | 1.0 | stable | Least common values |
| parse | 1.3 | stable | Regex field extraction |
| grok | 2.4 | stable | Grok pattern extraction |
| rex | 3.3 | experimental | Regex extraction with options |
| regex | 3.3 | experimental | Regex-based filtering |
| spath | 3.3 | experimental | JSON field extraction |
| patterns | 2.4 | stable | Log pattern discovery |
| join | 3.0 | stable | Combine datasets |
| append | 3.3 | experimental | Append subsearch results |
| appendcol | 3.1 | experimental | Append as columns |
| lookup | 3.0 | experimental | Enrich from lookup index |
| multisearch | 3.4 | experimental | Multi-query combination |
| subquery | 3.0 | experimental | Nested query filtering |
| ml | 2.5 | stable | Machine learning algorithms |
| kmeans | 1.3 | stable | K-means clustering |
| mvcombine | 3.4 | stable | Combine multivalue fields |
| nomv | 3.6 | stable | Multivalue to string |
| mvexpand | 3.6 | stable | Expand multivalue fields |
| graphlookup | 3.6 | experimental | Recursive graph traversal |
| describe | 2.1 | stable | Index metadata |
| explain | 3.1 | stable | Query execution plan |
| showdatasources | 2.4 | stable | List data sources |
Syntax conventions
Section titled “Syntax conventions”| Notation | Meaning |
|---|---|
<placeholder> | Replace with actual value |
[optional] | Can be omitted |
(a | b) | Required choice between options |
[a | b] | Optional choice between options |
... | Preceding element can repeat |
Further reading
Section titled “Further reading”- Function Reference - 200+ built-in functions
- Observability Examples - Real-world OTel queries
- PPL source documentation - Upstream PPL docs in the OpenSearch SQL plugin