Skip to content

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)).

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
| command3

In 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`

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>]
ParameterRequiredDescription
<index>YesIndex name or pattern to query
<boolean-expression>NoInitial filter condition

Example - Get all logs:

source = logs-otel-v1*

Example - Search with inline filter:

source = logs-otel-v1* severityText = 'ERROR'

Try in playground →


Filter results using boolean expressions. Only rows where the expression evaluates to true are returned.

Syntax:

where <boolean-expression>
ParameterRequiredDescription
<boolean-expression>YesCondition 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 20

Try in playground →

Example - Compound conditions:

| where severityNumber >= 17 AND `resource.attributes.service.name` = 'checkout'
| head 20

Try in playground →


(experimental, since 3.3)

Filter results by matching field values against a regular expression pattern.

Syntax:

regex <field> = <pattern>
regex <field> != <pattern>
ParameterRequiredDescription
<field>YesField to match against
<pattern>YesJava regex pattern

Example - Filter services matching a pattern:

| regex `resource.attributes.service.name` = ".*agent.*"
| head 20

Try in playground →


(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
]

Keep or remove fields from search results.

Syntax:

fields [+|-] <field-list>
ParameterRequiredDescription
<field-list>YesComma-delimited list of fields
+ or -No+ includes (default), - excludes

Example - Select specific fields:

| fields time, body, severityText, `resource.attributes.service.name`
| head 20

Example - Exclude fields:

| fields - traceId, spanId
| head 20

Try in playground →


(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 20

Try in playground →


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 20

Try in playground →


Evaluate 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 10

Example - Concatenate fields:

| eval service_operation = concat(`resource.attributes.service.name`, '/', body)
| head 20

Try in playground →


(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>] [, ...]

(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 20

Try in playground →


(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 20

Try in playground →


(experimental, since 3.1)

Expand a nested array field into multiple documents (one per array element).

Syntax:

expand <field> [as <alias>]

(experimental, since 3.1)

Flatten a struct/object field into separate top-level fields.

Syntax:

flatten <field> [as (<alias-list>)]

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>]
ParameterRequiredDescription
<aggregation>YesAggregation function (count, sum, avg, max, min, etc.)
by <field-list>NoGroup results by one or more fields
span(<field>, <interval>)NoCreate time or numeric buckets

Example - Count logs by service:

| stats count() as log_count by `resource.attributes.service.name`

Try in playground →

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_rate

Try in playground →

Example - Time-bucketed log volume:

| stats count() as volume by span(time, 5m) as time_bucket

Try in playground →


(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 * 2

(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>]
ParameterRequiredDescription
currentNoInclude current event in calculation (default: true)
windowNoNumber 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_ms

(experimental, since 3.3)

Create time-based aggregations - perfect for dashboards and trend analysis.

Syntax:

timechart [timefield=<field>] [span=<interval>] <aggregation> [by <field>]
ParameterRequiredDescription
timefieldNoTime field (default: @timestamp)
spanNoTime interval (default: 1m)
limitNoMax 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`

Try in playground →


(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`, severityText

Try in playground →


(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_trend

(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>]

(stable, since 3.5)

Add a totals row at the bottom of results.

Syntax:

addcoltotals [labelfield=<field>] [label=<string>] [<field-list>]

(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>]

Sort results by one or more fields.

Syntax:

sort [<count>] [+|-] <field> [, [+|-] <field>]...
sort [<count>] <field> [asc|desc] [, <field> [asc|desc]]...
ParameterRequiredDescription
<field>YesField to sort by
+ or ascNoAscending (default)
- or descNoDescending
<count>NoNumber of results to return

Example - Most recent logs first:

| sort - time
| head 20

Try in playground →

Example - Slowest traces:

source = otel-v1-apm-span-*
| sort - durationInNanos
| head 10

Return the first N results. Default is 10.

Syntax:

head [<size>] [from <offset>]
ParameterRequiredDescription
<size>NoNumber of results (default: 10)
<offset>NoNumber of results to skip

Example:

| sort - time
| head 50

Try in playground →


(experimental, since 3.2)

Reverse the display order of results.

Syntax:

reverse

Remove duplicate documents based on field values.

Syntax:

dedup [<count>] <field-list> [keepempty=<bool>] [consecutive=<bool>]
ParameterRequiredDescription
<field-list>YesFields that define uniqueness
<count>NoNumber of duplicates to keep per group (default: 1)
keepemptyNoKeep documents with null values (default: false)
consecutiveNoOnly remove consecutive duplicates (default: false)

Example - One log per unique service:

| dedup `resource.attributes.service.name`

Try in playground →


Find the most common values of a field.

Syntax:

top [<N>] <field-list> [by <group-field>]
ParameterRequiredDescription
<N>NoNumber of top values (default: 10)
<field-list>YesFields to find top values for

Example - Top services by log volume:

| top 5 `resource.attributes.service.name`

Try in playground →


Find the least common values of a field - useful for spotting anomalies.

Syntax:

rare <field-list> [by <group-field>]

Example - Rarest severity levels:

| rare severityText

Try in playground →


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

Try in playground →


(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

Try in playground →


(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>]
ParameterRequiredDescription
fieldYesSource field
<pattern>YesRegex with named groups (?<name>...)
modeNoextract (default) or sed for substitution
max_matchNoMax matches to extract (default: 1)

Example - Extract key-value pairs from logs:

| rex field=body "status=(?<status>\w+)\s+latency=(?<latency>\d+)"
| head 20

Try in playground →


(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_msg

Try in playground →


(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>]
ParameterRequiredDescription
<field>YesText field to analyze
methodNosimple_pattern (default) or brain for smarter clustering
modeNolabel adds pattern field, aggregation groups by pattern
max_sample_countNoSample logs per pattern (default: 10)

Example - Discover log patterns:

| patterns body method=simple_pattern mode=aggregation

Try in playground →


(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>
ParameterRequiredDescription
joinTypeNoinner (default), left, right, full, semi, anti, cross
on <condition>YesJoin condition
<right-dataset>YesIndex name or subsearch

Example - Correlate logs with trace data:

source = logs-otel-v1*
| left join on traceId = traceId [
source = otel-v1-apm-span-*
]

(experimental, since 3.0)

Enrich data by looking up values from a reference index.

Syntax:

lookup <lookupIndex> <lookupKey> [as <sourceKey>] [replace|append <field-list>]
ParameterRequiredDescription
<lookupIndex>YesReference index to look up from
<lookupKey>YesKey field in the lookup index
replace or appendNoreplace overwrites, append fills nulls only

(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`
]

(experimental, since 3.1)

Append subsearch results as additional columns alongside the main results.

Syntax:

appendcol [override=<bool>] [<subsearch>]

(experimental, since 3.4)

Execute multiple search queries and combine results.

Syntax:

multisearch [<subsearch>] [, <subsearch>]...

(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>

(stable, since 3.6)

Expand a multi-valued field into separate documents (one per value).

Syntax:

mvexpand <field> [limit=<int>]

(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>]
ParameterRequiredDescription
algorithmYesrcf (Random Cut Forest) or kmeans
time_fieldYes (RCF time-series)Timestamp field for time-series anomaly detection
centroidsNoNumber of clusters for kmeans (default: 2)
anomaly_rateNoExpected 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 = 1

(stable, since 1.3)

Apply k-means clustering directly on query results.

Syntax:

kmeans [centroids=<int>] [iterations=<int>] [distance_type=COSINE|L1|EUCLIDEAN]

(stable, since 2.1)

Query the metadata (field names, types) of an index.

Syntax:

describe <table-name>

Example:

describe logs-otel-v1*

(stable, since 3.1)

Show the execution plan of a query - useful for debugging and optimization.

Syntax:

explain [simple|standard|cost|extended] <query>

(stable, since 2.4)

List all configured data sources in the PPL engine.

Syntax:

show datasources

(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>]

CommandSinceStatusDescription
search1.0stableRetrieve documents from an index
where1.0stableFilter with boolean expressions
fields1.0stableKeep or remove fields
table3.3experimentalAlias for fields with enhanced syntax
rename1.0stableRename fields
eval1.0stableEvaluate expressions, create fields
convert3.5experimentalConvert field values to numeric
replace3.4experimentalReplace text in fields
fillnull3.0experimentalFill null values
expand3.1experimentalExpand nested arrays
flatten3.1experimentalFlatten struct fields
stats1.0stableAggregation and grouping
eventstats3.1experimentalAggregation appended to each event
streamstats3.4experimentalCumulative/rolling statistics
bin3.3experimentalGroup into numeric/time buckets
timechart3.3experimentalTime-based charts
chart3.4experimentalAggregation with row/column splits
trendline3.0experimentalMoving averages
addtotals3.5stableRow and column totals
addcoltotals3.5stableColumn totals
transpose3.5stableTranspose rows to columns
sort1.0stableSort results
reverse3.2experimentalReverse result order
head1.0stableReturn first N results
dedup1.0stableRemove duplicates
top1.0stableMost common values
rare1.0stableLeast common values
parse1.3stableRegex field extraction
grok2.4stableGrok pattern extraction
rex3.3experimentalRegex extraction with options
regex3.3experimentalRegex-based filtering
spath3.3experimentalJSON field extraction
patterns2.4stableLog pattern discovery
join3.0stableCombine datasets
append3.3experimentalAppend subsearch results
appendcol3.1experimentalAppend as columns
lookup3.0experimentalEnrich from lookup index
multisearch3.4experimentalMulti-query combination
subquery3.0experimentalNested query filtering
ml2.5stableMachine learning algorithms
kmeans1.3stableK-means clustering
mvcombine3.4stableCombine multivalue fields
nomv3.6stableMultivalue to string
mvexpand3.6stableExpand multivalue fields
graphlookup3.6experimentalRecursive graph traversal
describe2.1stableIndex metadata
explain3.1stableQuery execution plan
showdatasources2.4stableList data sources
NotationMeaning
<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