Skip to content

[FEATURE] Add SQL, Lucene and Query DSL query languages to the OpenSearch plugin - #840

Open
Oliver-ke wants to merge 8 commits into
perses:mainfrom
Oliver-ke:feature/add-sql-lucane-dsl-opensearch-plugin
Open

Oliver-ke wants to merge 8 commits into
perses:mainfrom
Oliver-ke:feature/add-sql-lucane-dsl-opensearch-plugin

Conversation

@Oliver-ke

@Oliver-ke Oliver-ke commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Description

The OpenSearch log query could only be written in PPL. This PR adds a per-query Query language selector with four options:

Language Endpoint Index Notes
PPL (default) POST /_plugins/_ppl Optional (usually in source=) Unchanged
SQL POST /_plugins/_sql From the FROM clause Needs the SQL/PPL plugin, like PPL
Lucene POST /{index}/_search Required query_string query. An empty query returns everything in the time range
Query DSL POST /{index}/_search Required Raw _search body. The time range is added to it

The dashboard time range is added automatically for every language, unless Disable automatic time filtering is ticked. Lucene and Query DSL also get an optional Limit field, which sets size (default 500). All four languages return the same LogData shape, so logs panels treat them the same.

Changes

  • Spec: new optional fields queryLanguage (ppl | sql | lucene | dsl, default ppl) and limit. index is now required for lucene and dsl.
  • CUE schema: validates the rules above. query may be empty only for lucene. There are new valid and invalid schema test files for each language.
  • Go SDK: new QueryLanguage, Index and Limit options, with validation that matches the schema.
  • Client: new sql() and search() calls next to ppl(). The index in the _search URL is checked against an allowlist of characters, so it can't be used for path traversal.
  • Datasource editor: new proxied datasources allow /_plugins/_sql and /.*/_search as well as /_plugins/_ppl.
  • Query editor:
    • a language selector
    • help text for the index field that changes with the language
    • an "index required" error for Lucene and Query DSL
    • the Limit field
    • examples and docs links for each language
  • Results: _search hits are converted to log entries. Nested _source fields are flattened to dot-separated labels, and _id and _index are added as labels. hasMore is set when OpenSearch stopped counting or the result was cut off by size. Numeric epoch timestamps in seconds, milliseconds, microseconds or nanoseconds are recognised, including fractional ones.
  • Errors: when the Perses proxy blocks an endpoint with a 403, the error says which endpoints to add to allowedEndpoints

Screenshots

01-dashboard-all-languages 03-editor-sql 06-query-language-selector

Checklist

  • Pull request has a descriptive title and context useful to a reviewer.
  • Pull request title follows the [<catalog_entry>] <commit message> naming convention using one of the
    following catalog_entry values: FEATURE, ENHANCEMENT, BUGFIX, BREAKINGCHANGE, DOC,IGNORE.
  • All commits have DCO signoffs.

UI Changes

  • Changes that impact the UI include screenshots and/or screencasts of the relevant changes.
  • Code follows the UI guidelines.

Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
@Oliver-ke
Oliver-ke requested review from jgbernalp and removed request for a team September 29, 2026 08:58
…ane-dsl-opensearch-plugin

Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Public API regressions and limit, validation, and search-construction defects remain unresolved.

Review effort: Balanced
Findings: 7 Medium severity · 1 Low severity

Open (8)
What changed in this PR

Adds SQL, Lucene, and Query DSL support to OpenSearch log queries across runtime, UI, schema, Go SDK, documentation, and tests.

Changes:

  • Adds per-query language selection, limits, index validation, and time filtering.
  • Adds SQL and _search clients plus response conversion.
  • Extends CUE/Go contracts and test coverage.
File Description
opensearch/​src/​queries/​opensearch-log-query/​OpenSearchLogQueryEditor.tsx Adds language and limit controls.
opensearch/​src/​queries/​opensearch-log-query/​OpenSearchLogQueryEditor.test.tsx Tests editor behavior.
opensearch/​src/​queries/​opensearch-log-query/​OpenSearchLogQuery.tsx Defaults new queries to PPL.
opensearch/​src/​queries/​opensearch-log-query/​opensearch-log-query-types.ts Defines languages and fields.
opensearch/​src/​queries/​opensearch-log-query/​opensearch-log-query-types.test.ts Tests language resolution.
opensearch/​src/​queries/​opensearch-log-query/​index.ts Exports builders and converters.
opensearch/​src/​queries/​opensearch-log-query/​get-opensearch-log-data.ts Dispatches requests by language.
opensearch/​src/​queries/​opensearch-log-query/​get-opensearch-log-data.test.ts Tests dispatch and conversion.
opensearch/​src/​queries/​opensearch-log-query/​convert/​parse-timestamp.ts Normalizes timestamp formats.
opensearch/​src/​queries/​opensearch-log-query/​convert/​index.ts Exports conversion utilities.
opensearch/​src/​queries/​opensearch-log-query/​convert/​hits-to-logs.ts Converts search hits to logs.
opensearch/​src/​queries/​opensearch-log-query/​convert/​hits-to-logs.test.ts Tests hit conversion.
opensearch/​src/​queries/​opensearch-log-query/​convert/​datarows-to-logs.ts Converts PPL/SQL rows.
opensearch/​src/​queries/​opensearch-log-query/​build/​sql.ts Builds SQL requests.
opensearch/​src/​queries/​opensearch-log-query/​build/​sql.test.ts Tests SQL requests.
opensearch/​src/​queries/​opensearch-log-query/​build/​search.ts Provides shared search helpers.
opensearch/​src/​queries/​opensearch-log-query/​build/​ppl.ts Extracts PPL request construction.
opensearch/​src/​queries/​opensearch-log-query/​build/​lucene.ts Builds Lucene search bodies.
opensearch/​src/​queries/​opensearch-log-query/​build/​lucene.test.ts Tests Lucene bodies.
opensearch/​src/​queries/​opensearch-log-query/​build/​index.ts Exports request builders.
opensearch/​src/​queries/​opensearch-log-query/​build/​dsl.ts Builds Query DSL bodies.
opensearch/​src/​queries/​opensearch-log-query/​build/​dsl.test.ts Tests Query DSL handling.
opensearch/​src/​queries/​constants.ts Adds language metadata and defaults.
opensearch/​src/​model/​opensearch-client.ts Adds SQL/search transports and errors.
opensearch/​src/​model/​opensearch-client.test.ts Tests transports and path safety.
opensearch/​src/​model/​opensearch-client-types.ts Adds shared response types.
opensearch/​src/​datasources/​opensearch-datasource/​OpenSearchDatasourceEditor.tsx Allows new proxy endpoints.
opensearch/​src/​datasources/​opensearch-datasource/​OpenSearchDatasource.tsx Exposes new client methods.
opensearch/​sdk/​go/​query/​log/​options.go Adds language and limit options.
opensearch/​sdk/​go/​query/​log/​log.go Extends and validates Go specs.
opensearch/​sdk/​go/​query/​log/​log_test.go Tests Go SDK additions.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​sql.json Covers valid SQL.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​ppl.json Covers explicit PPL.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​no-language.json Covers legacy defaulting.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​lucene.json Covers valid Lucene.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​lucene-empty-query.json Covers blank Lucene queries.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​valid/​dsl.json Covers valid Query DSL.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​zero-limit.json Rejects zero limits.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​unknown-language.json Rejects unknown languages.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​sql-empty-query.json Rejects empty SQL.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​negative-limit.json Rejects negative limits.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​lucene-without-index.json Requires Lucene index.
opensearch/​schemas/​queries/​opensearch-log-query/​tests/​invalid/​dsl-without-index.json Requires Query DSL index.
opensearch/​schemas/​queries/​opensearch-log-query/​query.cue Defines the expanded schema.
opensearch/​README.md Advertises supported languages.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread opensearch/sdk/go/query/log/log.go
Comment thread opensearch/src/model/opensearch-client-types.ts
Comment thread opensearch/src/model/opensearch-client.ts
Comment thread opensearch/src/queries/opensearch-log-query/OpenSearchLogQueryEditor.tsx Outdated
Comment thread opensearch/src/queries/opensearch-log-query/build/dsl.ts Outdated
Comment thread opensearch/src/queries/opensearch-log-query/build/search.ts Outdated
Comment thread opensearch/src/queries/opensearch-log-query/convert/index.ts
Comment thread opensearch/README.md
@ibakshay

ibakshay commented Oct 3, 2026 •

Copy link
Copy Markdown
Contributor

Hi @Oliver-ke,
Thank you very much for the PR! Could you please address all the review comments from the copilot and fix merge conflicts? I will then do a manual review :)

…ane-dsl-opensearch-plugin

# Conflicts:
#	opensearch/src/datasources/opensearch-datasource/OpenSearchDatasource.tsx
#	opensearch/src/model/opensearch-client.ts
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
Signed-off-by: Kelechi Oliver Azorji <kelechioliver96@gmail.com>
@Oliver-ke

Copy link
Copy Markdown
Contributor Author

Hi @Oliver-ke, Thank you very much for the PR! Could you please address all the review comments from the copilot and fix merge conflicts? I will then do a manual review :)

@ibakshay fixes applied, kindly review

@ibakshay

Copy link
Copy Markdown
Contributor

Hi @Oliver-ke, Thank you very much for the PR! Could you please address all the review comments from the copilot and fix merge conflicts? I will then do a manual review :)

@ibakshay fixes applied, kindly review

Added to my TODO list. Looking forward :)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

{ timestampField = DEFAULT_TIMESTAMP_FIELD, disableTimeFilter = false, limit }: SearchBodyOptions = {},
): OpenSearchSearchRequestBody {
const parsed = parseBody(userJson);
const userQuery = parsed.query as Record<string, unknown> | undefined;
Comment on lines +71 to +75
const filter: Array<Record<string, unknown>> = [buildTimeRangeFilter(timestampField, start, end)];
if (userQuery) {
filter.push(userQuery);
}
body.query = { bool: { filter } };
Comment on lines +29 to +32
- A **current Perses release** — the OpenSearch plugin ships bundled, so there's
nothing to install (you'll confirm this in [Step 2](#step-2--run-perses)).
- An **OpenSearch** cluster with the SQL/PPL plugin — bundled by default.

Comment on lines +118 to +119
const rootCause = parsed?.error?.root_cause?.[0]?.reason;
const suffix = reason && details && reason !== details ? `${reason} — ${details}` : reason || details || rootCause;
@ibakshay

Copy link
Copy Markdown
Contributor

A few UX suggestions from trying the query editor locally. None are blockers, but they seem cheap to do in this PR:

  1. Move the query box up. It's currently the last field, after datasource, index, timestamp field, message field, the time-filter checkbox and Limit. Since it's the field people use most, I'd put it right after Query language / Datasource / Index. The optional fields (timestamp field, message field, time filtering, Limit) could then go in a collapsible "Options" section below it.

  2. Use a positive label for the time-filter checkbox. "Disable automatic time filtering" is a negative you have to think about for a second. Something like "Use dashboard time range", checked by default, says the same thing more directly. The spec field can stay as it is; this would only change the UI label.

  3. Datasource label is doubled. The field has a "Datasource" heading and an "OpenSearch Datasource" floating label right under it, which looks cramped next to the other fields. One label is enough.

  4. Switching language keeps the old query. If I switch a PPL query to Lucene, the PPL text stays in the box. The query then returns nothing or fails with an unrelated error. It'd be nicer to keep one query per language and restore it when switching back. Or, more simply, clear the box so the language's placeholder or examples show.

@ibakshay

Copy link
Copy Markdown
Contributor

I also found one bug when I was testing locallly.

So, When automatic time filtering is on (the default), the SQL query sends the time range in the _sql request's filter field. OpenSearch only supports filter in its legacy SQL engine, so every SQL query with the time filter enabled runs on the legacy engine. OpenSearch's new SQL engine only accepts query, fetch_size, parameters and cursor

I tested this on OpenSearch 2.19 and saw three problems:

  1. Wrong timestamps. The legacy engine reads microseconds as milliseconds. A document at 16:35:02.798478Z shows in the panel as 16:48:20.478Z, about 13 minutes off. Ticking "Disable automatic time filtering" fixes it. Microsecond and nanosecond timestamps are common, e.g. OTel logs.

  2. Valid queries fail. SELECT * FROM app-logs WHERE match(message, 'timeout') LIMIT 20 returns Invalid SQL query: syntax error, expect AGAINST, actual LIMIT. It works with the time filter disabled.

  3. Different results. DATE_FORMAT([@timestamp](https://github.com/timestamp)`, '%Y')returns%2026instead of2026`.

To reproduce directly against OpenSearch:

# new engine: correct
curl -XPOST localhost:9200/_plugins/_sql -H 'Content-Type: application/json' \
  -d '{"query":"SELECT `@timestamp` FROM app-logs ORDER BY `@timestamp` DESC LIMIT 1"}'
# => "2026-10-17 16:39:55.798478"

# same query with filter: legacy engine, shifted
curl -XPOST localhost:9200/_plugins/_sql -H 'Content-Type: application/json' \
  -d '{"query":"SELECT `@timestamp` FROM app-logs ORDER BY `@timestamp` DESC LIMIT 1","filter":{"range":{"@timestamp":{"gte":0,"format":"epoch_millis"}}}}'
# => "2026-10-17 16:53:13.478"

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants