-
Notifications
You must be signed in to change notification settings - Fork 240
Add gRPC supports for Search foundation; match-all query #2071
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
dayana-j
wants to merge
29
commits into
opensearch-project:main
Choose a base branch
from
dayana-j:grpc-search
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
29 commits
Select commit
Hold shift + click to select a range
8a5f19b
feat: add transparent gRPC transport as separate java-client-grpc module
dayana-j 51cd03e
ci: retrigger CI
dayana-j a7bbd43
Making the FieldMappingUtil package private
dayana-j 874c174
Making this package private
dayana-j b7575b4
Making BasicAuthInterceptor package private
dayana-j a667a09
Remove unused toHttpStatus method and fix test package for package-pr…
dayana-j b8d8720
refactor: split AWS SigV4 into AwsGrpcTransport per maintainer feedback
dayana-j bd70c72
refactor: remove silent REST fallback from HybridTransport
dayana-j b9811ba
docs: update comments to reflect routing instead of fallback
dayana-j 94f2d6c
fix: make integration test base classes self-contained
dayana-j 9aae7f1
fix: lower java-client-grpc baseline from JDK 11 to JDK 8
dayana-j 178f0bc
fix: remove explicit jackson test deps (transitive via java-client)
dayana-j e6bf191
fix: wire up integration tests with java21 source set
dayana-j 69a7311
ci: add OpenSearch 3.5.0 to integration test matrix
dayana-j 4b98fc8
fix: replace wildcard import with explicit imports (spotless)
dayana-j 9659742
fix: remove java21 classes from unitTest task
dayana-j 32aeab0
fix: spotless formatting for samples (license header, import order)
dayana-j 4480aa3
fix: skip gRPC integration tests when port is unreachable
dayana-j 70df55b
fix: only enable gRPC in testcontainer for OpenSearch 3.5.0+
dayana-j ae935be
ci: exclude grpc integration tests until OpenSearch 3.5.0 is released
dayana-j 32f38c9
fix: graceful skip on container failure + re-enable 3.5.0 in CI
dayana-j b6b098f
fix: spotless formatting on GrpcTestContainerRule
dayana-j 8f5a6ae
fix: remove grpcStatusToHttpStatus from FieldMappingUtil
dayana-j 2d2b0c6
fix: skip gRPC tests early with assumeTrue for unsupported versions
dayana-j 87ba2ff
fix: make GrpcChannelFactory methods package-private
dayana-j cf6e8ce
fix: apply maintainer suggestions on visibility and naming
dayana-j f476ce2
Merge branch 'opensearch-project:main' into grpc-java
dayana-j 3ff92a1
Merge branch 'opensearch-project:main' into grpc-java
dayana-j 601508e
feat: implement search over gRPC with match_all query support
dayana-j File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
108 changes: 108 additions & 0 deletions
108
...rc/main/java/org/opensearch/client/transport/grpc/translation/SearchRequestConverter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.client.transport.grpc.translation; | ||
|
|
||
| import org.opensearch.client.json.JsonpMapper; | ||
| import org.opensearch.client.opensearch._types.query_dsl.MatchAllQuery; | ||
| import org.opensearch.client.opensearch._types.query_dsl.Query; | ||
| import org.opensearch.client.opensearch.core.SearchRequest; | ||
|
|
||
| /** | ||
| * Converts opensearch-java SearchRequest to protobuf SearchRequest. | ||
| * | ||
| * Currently supported query types: | ||
| * - match_all — matches all documents | ||
| * | ||
| * Query types not yet implemented (these can be executed over gRPC once | ||
| * the converter is extended — the server already supports them): | ||
| * - term, terms, terms_set | ||
| * - match, match_phrase, match_phrase_prefix, match_bool_prefix, multi_match | ||
| * - bool, constant_score, function_score | ||
| * - range, prefix, wildcard, regexp, fuzzy, exists, ids | ||
| * - nested, geo_distance, geo_bounding_box | ||
| * - knn, hybrid, script | ||
| * | ||
| * To add a new query type, implement a convertXxx() method and add a case | ||
| * to convertQuery(Query). | ||
| */ | ||
| public class SearchRequestConverter { | ||
|
|
||
| /** | ||
| * Convert an opensearch-java SearchRequest to a protobuf SearchRequest. | ||
| * | ||
| * @param request the client SearchRequest | ||
| * @param jsonpMapper the JSON mapper (for future use with script queries) | ||
| * @return the protobuf SearchRequest | ||
| */ | ||
| public static org.opensearch.protobufs.SearchRequest toProto(SearchRequest request, JsonpMapper jsonpMapper) { | ||
| org.opensearch.protobufs.SearchRequest.Builder protoBuilder = org.opensearch.protobufs.SearchRequest.newBuilder(); | ||
|
|
||
| // Set index(es) | ||
| if (request.index() != null && !request.index().isEmpty()) { | ||
| protoBuilder.addAllIndex(request.index()); | ||
| } | ||
|
|
||
| // Build SearchRequestBody | ||
| org.opensearch.protobufs.SearchRequestBody.Builder bodyBuilder = org.opensearch.protobufs.SearchRequestBody.newBuilder(); | ||
|
|
||
| // Set size | ||
| if (request.size() != null) { | ||
| bodyBuilder.setSize(request.size()); | ||
| } | ||
|
|
||
| // Set from (pagination offset) | ||
| if (request.from() != null) { | ||
| bodyBuilder.setFrom(request.from()); | ||
| } | ||
|
|
||
| // Convert query | ||
| if (request.query() != null) { | ||
| bodyBuilder.setQuery(convertQuery(request.query())); | ||
| } | ||
|
|
||
| protoBuilder.setSearchRequestBody(bodyBuilder.build()); | ||
| return protoBuilder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Convert an opensearch-java Query to a protobuf QueryContainer. | ||
| * | ||
| * @throws UnsupportedOperationException if the query type is not yet supported | ||
| */ | ||
| private static org.opensearch.protobufs.QueryContainer convertQuery(Query query) { | ||
| org.opensearch.protobufs.QueryContainer.Builder containerBuilder = org.opensearch.protobufs.QueryContainer.newBuilder(); | ||
|
|
||
| if (query.isMatchAll()) { | ||
| containerBuilder.setMatchAll(convertMatchAll(query.matchAll())); | ||
| } else { | ||
| throw new UnsupportedOperationException( | ||
| "Query type '" | ||
| + query._kind() | ||
| + "' is not yet supported for gRPC transport in this version. " | ||
| + "Use REST transport for this query type." | ||
| ); | ||
| } | ||
|
|
||
| return containerBuilder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Convert MatchAllQuery to protobuf. | ||
| * MatchAllQuery has only optional boost and _name fields. | ||
| */ | ||
| private static org.opensearch.protobufs.MatchAllQuery convertMatchAll(MatchAllQuery matchAll) { | ||
| org.opensearch.protobufs.MatchAllQuery.Builder builder = org.opensearch.protobufs.MatchAllQuery.newBuilder(); | ||
|
|
||
| if (matchAll.boost() != null) { | ||
| builder.setBoost(matchAll.boost().floatValue()); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
| } |
174 changes: 174 additions & 0 deletions
174
...c/main/java/org/opensearch/client/transport/grpc/translation/SearchResponseConverter.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| /* | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| * | ||
| * The OpenSearch Contributors require contributions made to | ||
| * this file be licensed under the Apache-2.0 license or a | ||
| * compatible open source license. | ||
| */ | ||
|
|
||
| package org.opensearch.client.transport.grpc.translation; | ||
|
|
||
| import java.io.ByteArrayInputStream; | ||
| import java.io.InputStream; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import org.opensearch.client.json.JsonpMapper; | ||
| import org.opensearch.client.opensearch._types.ShardStatistics; | ||
| import org.opensearch.client.opensearch.core.SearchResponse; | ||
| import org.opensearch.client.opensearch.core.search.Hit; | ||
| import org.opensearch.client.opensearch.core.search.HitsMetadata; | ||
| import org.opensearch.client.opensearch.core.search.TotalHits; | ||
| import org.opensearch.client.opensearch.core.search.TotalHitsRelation; | ||
|
|
||
| /** | ||
| * Converts protobuf SearchResponse to opensearch-java SearchResponse format. | ||
| * <p> | ||
| * Currently supports match_all queries. Other query types (term, match, bool, etc.) | ||
| * are not yet implemented in the client-side converter but can be executed over gRPC | ||
| * once the request converter supports them — the response format is the same regardless | ||
| * of query type. | ||
| * <p> | ||
| * Note: Unsupported query types will throw UnsupportedOperationException at the request | ||
| * conversion stage. The response converter handles all response formats since the | ||
| * SearchResponse structure is query-agnostic. | ||
| */ | ||
| public class SearchResponseConverter { | ||
|
|
||
| /** | ||
| * Convert a protobuf SearchResponse to an opensearch-java SearchResponse. | ||
| * | ||
| * @param protoResponse the protobuf SearchResponse from the server | ||
| * @param jsonpMapper the JSON mapper for _source deserialization | ||
| * @param tDocumentClass the target class for hit _source deserialization | ||
| * @param <TDocument> the document type | ||
| * @return the opensearch-java SearchResponse | ||
| */ | ||
| public static <TDocument> SearchResponse<TDocument> fromProto( | ||
| org.opensearch.protobufs.SearchResponse protoResponse, | ||
| JsonpMapper jsonpMapper, | ||
| Class<TDocument> tDocumentClass | ||
| ) { | ||
| SearchResponse.Builder<TDocument> builder = new SearchResponse.Builder<TDocument>(); | ||
|
|
||
| // took | ||
| builder.took(protoResponse.getTook()); | ||
|
|
||
| // timed_out | ||
| builder.timedOut(protoResponse.getTimedOut()); | ||
|
|
||
| // _shards | ||
| if (protoResponse.hasXShards()) { | ||
| org.opensearch.protobufs.ShardStatistics protoShards = protoResponse.getXShards(); | ||
| builder.shards( | ||
| new ShardStatistics.Builder().total(protoShards.getTotal()) | ||
| .successful(protoShards.getSuccessful()) | ||
| .failed(protoShards.getFailed()) | ||
| .build() | ||
| ); | ||
| } else { | ||
| builder.shards(new ShardStatistics.Builder().total(0).successful(0).failed(0).build()); | ||
| } | ||
|
|
||
| // hits | ||
| if (protoResponse.hasHits()) { | ||
| builder.hits(convertHitsMetadata(protoResponse.getHits(), jsonpMapper, tDocumentClass)); | ||
| } else { | ||
| builder.hits(new HitsMetadata.Builder<TDocument>().hits(new ArrayList<>()).build()); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| private static <TDocument> HitsMetadata<TDocument> convertHitsMetadata( | ||
| org.opensearch.protobufs.HitsMetadata protoHits, | ||
| JsonpMapper jsonpMapper, | ||
| Class<TDocument> tDocumentClass | ||
| ) { | ||
| HitsMetadata.Builder<TDocument> builder = new HitsMetadata.Builder<TDocument>(); | ||
|
|
||
| // total hits | ||
| if (protoHits.hasTotal()) { | ||
| org.opensearch.protobufs.HitsMetadataTotal protoTotal = protoHits.getTotal(); | ||
| if (protoTotal.hasTotalHits()) { | ||
| org.opensearch.protobufs.TotalHits totalHits = protoTotal.getTotalHits(); | ||
| TotalHitsRelation relation = totalHits.getRelation() == org.opensearch.protobufs.TotalHitsRelation.TOTAL_HITS_RELATION_EQ | ||
| ? TotalHitsRelation.Eq | ||
| : TotalHitsRelation.Gte; | ||
| builder.total(new TotalHits.Builder().value(totalHits.getValue()).relation(relation).build()); | ||
| } | ||
| } | ||
|
|
||
| // max_score | ||
| if (protoHits.hasMaxScore()) { | ||
| org.opensearch.protobufs.HitsMetadataMaxScore maxScore = protoHits.getMaxScore(); | ||
| if (maxScore.hasFloat()) { | ||
| builder.maxScore(maxScore.getFloat()); | ||
| } | ||
| } | ||
|
|
||
| // individual hits | ||
| List<Hit<TDocument>> hits = new ArrayList<>(); | ||
| for (org.opensearch.protobufs.HitsMetadataHitsInner protoHit : protoHits.getHitsList()) { | ||
| hits.add(convertHit(protoHit, jsonpMapper, tDocumentClass)); | ||
| } | ||
| builder.hits(hits); | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| private static <TDocument> Hit<TDocument> convertHit( | ||
| org.opensearch.protobufs.HitsMetadataHitsInner protoHit, | ||
| JsonpMapper jsonpMapper, | ||
| Class<TDocument> tDocumentClass | ||
| ) { | ||
| Hit.Builder<TDocument> builder = new Hit.Builder<TDocument>(); | ||
|
|
||
| // _index | ||
| builder.index(protoHit.getXIndex()); | ||
|
|
||
| // _id | ||
| builder.id(protoHit.getXId()); | ||
|
|
||
| // _score | ||
| if (protoHit.hasXScore()) { | ||
| org.opensearch.protobufs.HitXScore score = protoHit.getXScore(); | ||
| if (score.hasDouble()) { | ||
| builder.score(score.getDouble()); | ||
| } | ||
| } | ||
|
|
||
| // _version | ||
| if (protoHit.hasXVersion()) { | ||
| builder.version(protoHit.getXVersion()); | ||
| } | ||
|
|
||
| // _seq_no | ||
| if (protoHit.hasXSeqNo()) { | ||
| builder.seqNo(protoHit.getXSeqNo()); | ||
| } | ||
|
|
||
| // _primary_term | ||
| if (protoHit.hasXPrimaryTerm()) { | ||
| builder.primaryTerm(protoHit.getXPrimaryTerm()); | ||
| } | ||
|
|
||
| // _source — decode bytes and deserialize to TDocument | ||
| if (!protoHit.getXSource().isEmpty()) { | ||
| TDocument source = deserializeSource(protoHit.getXSource().toByteArray(), jsonpMapper, tDocumentClass); | ||
| builder.source(source); | ||
| } | ||
|
|
||
| return builder.build(); | ||
| } | ||
|
|
||
| /** | ||
| * Decode _source bytes from protobuf hit to a Java object. | ||
| * The server returns _source as UTF-8 JSON bytes. | ||
| */ | ||
| static <TDocument> TDocument deserializeSource(byte[] sourceBytes, JsonpMapper jsonpMapper, Class<TDocument> tDocumentClass) { | ||
| InputStream stream = new ByteArrayInputStream(sourceBytes); | ||
| jakarta.json.stream.JsonParser parser = jsonpMapper.jsonProvider().createParser(stream); | ||
| parser.next(); // advance to first token | ||
| return jsonpMapper.deserialize(parser, tDocumentClass); | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @dayana-j , I think this change needs a bit more thoughts:
_searchis limited to onlymatch_allquery (it is not enough to filter endpoint, we need params or/and payload)match_alllike queries even if REST based transport supports everything elseThat would significantly limit its applicability I believe. The suggestion I have is to enhance endpoint support with
requestargumentGrpcTransport.isEndpointSupported(endpoint, request)so the GRPC transport could check endpoint and perform the request conversion (yes, could be a bit wasteful work but at least we will properly route to REST transport till all queries are covered).Curious WDYT? Thank you.