Add Ids DSL query support via Lucene delegation - #22684
Conversation
…m/terms guards Signed-off-by: Kamal <askkamal@amazon.com>
Signed-off-by: Kamal <askkamal@amazon.com>
Signed-off-by: Kamal <askkamal@amazon.com>
Signed-off-by: Kamal <askkamal@amazon.com>
…atorTests Signed-off-by: Kamal <askkamal@amazon.com>
PR Reviewer Guide 🔍(Review updated until commit 4a65abf)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 4a65abf
Previous suggestionsSuggestions up to commit 5152851
|
|
❕ Gradle check result for 5152851: UNSTABLE Please review all flaky tests that succeeded after retry and create an issue if one does not already exist to track the flaky failure. |
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #22684 +/- ##
============================================
+ Coverage 71.48% 71.54% +0.06%
- Complexity 76960 77014 +54
============================================
Files 6156 6156
Lines 358444 358444
Branches 52246 52246
============================================
+ Hits 256240 256457 +217
+ Misses 81792 81597 -195
+ Partials 20412 20390 -22 ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
… encoding failure Signed-off-by: Kamal <askkamal@amazon.com>
|
Persistent review updated to latest commit 4a65abf |
Description
Adds support for the OpenSearch
idsquery in the Calcite-based DSL query executor.The query is translated into a fieldless delegated predicate (
IDS(MAP(...), ...)) andserialized back to a vanilla
IdsQueryBuilderfor execution by the Lucene backend.What: A translator (
IdsQueryTranslator) convertsIdsQueryBuilderinto a CalciteRexCall, and a serializer (IdsQuerySerializer) reconstructs theIdsQueryBuilderon the backend side so Lucene can execute it natively.
Why: The
idsquery is a fundamental filtering primitive. Users need it forpoint-lookups, deletion scopes, and boolean compositions with other predicates.
Where: Two modules are touched:
dsl-query-executor— translator, registry wiring, golden-file tests, integration test stub.analytics-backend-lucene— serializer, registry wiring, serializer tests.A
ScalarFunction.IDSenum constant is added toanalytics-frameworkto register thefunction in the capability matrix.
Why Lucene Delegation
_idis stored Uid-encoded in both the Parquet and Lucene segments:IdFieldMapper.preParsecallsUid.encodeIdbefore indexing.IdParquetFieldwrites those same encoded bytes to the Parquet column.GetServicecallsUid.decodeIdon read to recover the original string.A naïve native comparison of a raw query string against the stored bytes silently
returns zero rows. Delegating to vanilla's
IdsQueryBuilderkeeps encoding insideIdFieldType.termsQuerywhere it belongs — the single canonical encode/compare path.A future optimisation could pre-encode the query literals with
Uid.encodeIdand pusha native byte-comparison predicate directly to Parquet. This is legitimate but not
necessary for correctness today; the delegation path is zero-risk.
Why the Call Is Fieldless
This is the design's most notable property.
The
idsquery targets the implicit_idmetadata field, which is not a column inthe user-visible Calcite row type. No field operand is emitted in the
RexCall:Routing works because
OpenSearchFilterRule.java(lines 238-240) routesFULL_TEXTfunctions that carry no field reference to
filterBackendsAnyFormat(function, FieldType.TEXT). The capability pair(IDS, TEXT)is already registered by theexisting loop over
ScalarFunctionvalues inLuceneAnalyticsBackendPlugin.Consequence:
_idis never added to the Calcite row type. The change is entirelyoutside the schema shared with the PPL and SQL paths — no schema pollution, no
downstream breakage risk.
Supported and Rejected Parameters
valuesboostConversionException)_nameConversionException)matched_queriesis not surfaced bySearchResponseBuilder.typesIdsQueryBuilderhas no field for it. The strict two-argObjectParseratIdsQueryBuilder.java:135throwsXContentParseExceptionat parse time — a runtime rejection check could never fire.Operand Encoding
Each id is carried as a separate indexed MAP operand:
Why not a comma-joined string? An
_idmay legally contain commas. A joinedstring would be lossy. The indexed-MAP scheme preserves each id verbatim through the
extractOptionalParamscontract.Both
IdsQueryTranslatorTests.testIdContainingCommaandIdsQuerySerializerTests.testSerializeIdContainingCommaassert lossless round-tripfor ids containing commas.
Behaviour
valuesFALSERexLiteral(match-nothing). MirrorsIdsQueryBuilder.doRewriteline 157 rewriting toMatchNoneQueryBuilder.IdsQueryBuilder.ids()returns aSet; the translator emits one operand per unique id. Verified bytestDuplicateIdsAreDeduplicated.HashSetiteration order.Plan Shape
Single-id plan (
ids_query_single.json):Multi-id plan (
ids_query_multiple.json):E2E Validation
Live-cluster validation was performed on a combined test branch against a composite-format
index (Parquet primary, Lucene secondary) with six documents.
ids=[id0]ids=[id0, id1]bool.must=[ids(id0,id1,id3), term(region=us-east-1)]bool.must_not=[ids(id0)]bool.should=[ids(id0,id1), ids(id2,id3)]MSM=2bool.should3 wildcards MSM=2 + statsextended_statson latencyexistson_id+ statsField '_id' not found in schema)termsagg on_idGroup-by field '_id' not found in schema)Rows 13, 14, and 15 are the bool-composition shapes (mixed-backend AND, negated,
OR with
minimum_should_match). All three match the baseline exactly.Note:
idsinside aboolrequires the bool translator, which lives on a separatebranch. That coverage came from a combined test branch that merged both feature branches.
Known Divergences from Legacy
_search_searchboostrejected.matched_queriesSearchResponseBuilderdoes not propagate named queries;_namerejected.delegated_predicateis opaque to the Calcite pruning planner.existsquery on_idField '_id' not found in schema_idis deliberately not a schema column; matches pre-existing behaviour.termsaggregation on_idGroup-by field '_id' not found in schemasorton_idField '_id' not found in schemaSearchHits.empty(true))SearchResponseBuilder.build()returns empty hits for all queries; not ids-specific. Integration tests parked with@AwaitsFix.Testing
dsl-query-executoranalytics-backend-luceneanalytics-frameworkGolden files:
src/test/resources/golden/ids_query_single.jsonsrc/test/resources/golden/ids_query_multiple.jsonUnit test coverage (translator —
IdsQueryTranslatorTests):FALSEliteralboost→ConversionException_name→ConversionExceptiongetQueryType()returnsIdsQueryBuilder.classUnit test coverage (serializer —
IdsQuerySerializerTests):NamedWriteableRegistryvalues.notAnIntkey →IllegalArgumentExceptionwith diagnostic messageQuerySerializerRegistrycontainsScalarFunction.IDSentryIntegration tests (
DslIdsQueryIT):@AwaitsFixbecauseSearchResponseBuilder.build()returnsSearchHits.empty(true)— no query returns end-to-end hits yet.Suites not run:
DslIdsQueryIT— blocked bySearchResponseBuilderempty-hits behaviour (see above)._searchendpoint — requires multi-plugin cluster startup; validatedmanually on a combined branch (results table above).
Check List
integration test stub compiled and parked.
idsquery is already part of theOpenSearch Query DSL specification.
user-facing documentation change needed until the feature exits experimental status.
Related Issues
Resolves #[Issue number to be closed when this PR is merged]
Check List
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.