Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Add transparent gRPC transport with HybridTransport (bulk over gRPC, REST fallback), translation layer, TLS, basic auth, AWS SigV4, and JWT support ([#2062](https://github.com/opensearch-project/opensearch-java/pull/2062))
- Add search over gRPC with match_all query support, SearchRequestConverter, SearchResponseConverter, and _source deserialization ([#2071](https://github.com/opensearch-project/opensearch-java/pull/2071))
- Add `setAutomaticRetriesDisabled` to `ApacheHttpClient5TransportBuilder` to allow enabling automatic retries ([#2086](https://github.com/opensearch-project/opensearch-java/pull/2086))
- Added typed support for scored named queries by mapping the polymorphic `Hit.matched_queries` field to a new `MatchedQueries` tagged union, whose `names` variant carries a `List<String>` and whose `scores` variant carries a `Map<String, Double>`, and by sending `include_named_queries_score` as a query parameter ([#2098](https://github.com/opensearch-project/opensearch-java/pull/2098))

### Fixed
- Fix `unitTest` task not running the tests in the `test` source set ([#2074](https://github.com/opensearch-project/opensearch-java/pull/2074))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,8 @@ public final Boolean ignoreUnavailable() {
}

/**
* Whether to return scores with named queries. Default is false.
* Indicates whether <code>hit.matched_queries</code> should be rendered as a map that includes the name of the matched query associated
* with its score (true) or as an array containing the name of the matched queries (false)
* <p>
* API name: {@code include_named_queries_score}
* </p>
Expand Down Expand Up @@ -1055,11 +1056,6 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) {
this.highlight.serialize(generator, mapper);
}

if (this.includeNamedQueriesScore != null) {
generator.writeKey("include_named_queries_score");
generator.write(this.includeNamedQueriesScore);
}

if (ApiTypeHelper.isDefined(this.indicesBoost)) {
generator.writeKey("indices_boost");
generator.writeStartArray();
Expand Down Expand Up @@ -1954,7 +1950,8 @@ public final Builder ignoreUnavailable(@Nullable Boolean value) {
}

/**
* Whether to return scores with named queries. Default is false.
* Indicates whether <code>hit.matched_queries</code> should be rendered as a map that includes the name of the matched query
* associated with its score (true) or as an array containing the name of the matched queries (false)
* <p>
* API name: {@code include_named_queries_score}
* </p>
Expand Down Expand Up @@ -2728,7 +2725,6 @@ protected static void setupSearchRequestDeserializer(ObjectDeserializer<SearchRe
op.add(Builder::fields, JsonpDeserializer.arrayDeserializer(FieldAndFormat._DESERIALIZER), "fields");
op.add(Builder::from, JsonpDeserializer.integerDeserializer(), "from");
op.add(Builder::highlight, Highlight._DESERIALIZER, "highlight");
op.add(Builder::includeNamedQueriesScore, JsonpDeserializer.booleanDeserializer(), "include_named_queries_score");
op.add(
Builder::indicesBoost,
JsonpDeserializer.arrayDeserializer(JsonpDeserializer.stringMapDeserializer(JsonpDeserializer.floatDeserializer())),
Expand Down Expand Up @@ -2801,6 +2797,9 @@ protected void applyQueryParameters(@Nonnull Map<String, String> params) {
if (this.ignoreUnavailable != null) {
params.put("ignore_unavailable", String.valueOf(this.ignoreUnavailable));
}
if (this.includeNamedQueriesScore != null) {
params.put("include_named_queries_score", String.valueOf(this.includeNamedQueriesScore));
}
if (this.lenient != null) {
params.put("lenient", String.valueOf(this.lenient));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ public class Hit<TDocument> implements PlainJsonSerializable, ToCopyableBuilder<
private final Map<String, InnerHitsResult> innerHits;

@Nullable
private final JsonData matchedQueries;
private final MatchedQueries matchedQueries;

@Nonnull
private final Map<String, JsonData> metaFields;
Expand Down Expand Up @@ -231,7 +231,7 @@ public final Map<String, InnerHitsResult> innerHits() {
* </p>
*/
@Nullable
public final JsonData matchedQueries() {
public final MatchedQueries matchedQueries() {
return this.matchedQueries;
}

Expand Down Expand Up @@ -516,7 +516,7 @@ public static class Builder<TDocument> extends ObjectBuilderBase implements Copy
@Nullable
private Map<String, InnerHitsResult> innerHits;
@Nullable
private JsonData matchedQueries;
private MatchedQueries matchedQueries;
@Nullable
private Map<String, JsonData> metaFields;
@Nullable
Expand Down Expand Up @@ -783,11 +783,23 @@ public final Builder<TDocument> innerHits(String key, Function<InnerHitsResult.B
* </p>
*/
@Nonnull
public final Builder<TDocument> matchedQueries(@Nullable JsonData value) {
public final Builder<TDocument> matchedQueries(@Nullable MatchedQueries value) {
this.matchedQueries = value;
return this;
}

/**
* The names of queries that matched the document. When <code>include_named_queries_score</code> is false (default), returns an
* array of query names. When true, returns an object mapping query names to their scores.
* <p>
* API name: {@code matched_queries}
* </p>
*/
@Nonnull
public final Builder<TDocument> matchedQueries(Function<MatchedQueries.Builder, ObjectBuilder<MatchedQueries>> fn) {
return matchedQueries(fn.apply(new MatchedQueries.Builder()).build());
}

/**
* Contains metadata values for the documents.
*
Expand Down Expand Up @@ -996,7 +1008,7 @@ protected static <TDocument> void setupHitDeserializer(
);
op.add(Builder::index, JsonpDeserializer.stringDeserializer(), "_index");
op.add(Builder::innerHits, JsonpDeserializer.stringMapDeserializer(InnerHitsResult._DESERIALIZER), "inner_hits");
op.add(Builder::matchedQueries, JsonData._DESERIALIZER, "matched_queries");
op.add(Builder::matchedQueries, MatchedQueries._DESERIALIZER, "matched_queries");
op.add(Builder::nested, NestedIdentity._DESERIALIZER, "_nested");
op.add(Builder::node, JsonpDeserializer.stringDeserializer(), "_node");
op.add(Builder::primaryTerm, JsonpDeserializer.longDeserializer(), "_primary_term");
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
/*
* 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.opensearch.core.search;

import jakarta.json.stream.JsonGenerator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import javax.annotation.Nonnull;
import org.opensearch.client.json.JsonpDeserializable;
import org.opensearch.client.json.JsonpDeserializer;
import org.opensearch.client.json.JsonpMapper;
import org.opensearch.client.json.PlainJsonSerializable;
import org.opensearch.client.json.UnionDeserializer;
import org.opensearch.client.util.ApiTypeHelper;
import org.opensearch.client.util.ObjectBuilder;
import org.opensearch.client.util.ObjectBuilderBase;
import org.opensearch.client.util.TaggedUnion;
import org.opensearch.client.util.TaggedUnionUtils;

// typedef: core.search.MatchedQueries

/**
* The names of queries that matched a hit, with optional per-query scores.
*
* <p>
* The server returns matched_queries in one of two shapes, and never both: an array of query names when
* {@code include_named_queries_score} is not set, or an object mapping query names to scores when
* {@code include_named_queries_score} is true.
* </p>
*/
@JsonpDeserializable
public class MatchedQueries implements TaggedUnion<MatchedQueries.Kind, Object>, PlainJsonSerializable {

/**
* {@link MatchedQueries} variant kinds.
*/
public enum Kind {
Names,
Scores
}

private final Kind _kind;
private final Object _value;

@Override
public final Kind _kind() {
return _kind;
}

@Override
public final Object _get() {
return _value;
}

private MatchedQueries(Kind kind, Object value) {
this._kind = kind;
this._value = value;
}

private MatchedQueries(Builder builder) {
this._kind = ApiTypeHelper.requireNonNull(builder._kind, builder, "<variant kind>");
this._value = ApiTypeHelper.requireNonNull(builder._value, builder, "<variant value>");
}

public static MatchedQueries of(Function<MatchedQueries.Builder, ObjectBuilder<MatchedQueries>> fn) {
return fn.apply(new Builder()).build();
}

public static MatchedQueries ofNames(List<String> names) {
return new MatchedQueries(Kind.Names, ApiTypeHelper.requireNonNull(names, MatchedQueries.class, "names"));
}

public static MatchedQueries ofScores(Map<String, Double> scores) {
return new MatchedQueries(Kind.Scores, ApiTypeHelper.requireNonNull(scores, MatchedQueries.class, "scores"));
}

/**
* Is this variant instance of kind {@code names}?
*/
public boolean isNames() {
return _kind == Kind.Names;
}

/**
* Get the {@code names} variant value: the names of the queries that matched the hit, returned by the server when
* {@code include_named_queries_score} is not set.
*
* @throws IllegalStateException if the current variant is not the {@code names} kind.
*/
public List<String> names() {
return TaggedUnionUtils.get(this, Kind.Names);
}

/**
* Is this variant instance of kind {@code scores}?
*/
public boolean isScores() {
return _kind == Kind.Scores;
}

/**
* Get the {@code scores} variant value: a map from matched query name to score, returned by the server when
* {@code include_named_queries_score} is true.
*
* @throws IllegalStateException if the current variant is not the {@code scores} kind.
*/
public Map<String, Double> scores() {
return TaggedUnionUtils.get(this, Kind.Scores);
}

@Override
@SuppressWarnings("unchecked")
public void serialize(JsonGenerator generator, JsonpMapper mapper) {
switch (_kind) {
case Names:
generator.writeStartArray();
for (String name : (List<String>) _value) {
generator.write(name);
}
generator.writeEnd();
break;
case Scores:
generator.writeStartObject();
for (Map.Entry<String, Double> entry : ((Map<String, Double>) _value).entrySet()) {
generator.writeKey(entry.getKey());
generator.write(entry.getValue());
}
generator.writeEnd();
break;
}
}

@Nonnull
public Builder toBuilder() {
return new Builder(this);
}

@Nonnull
public static Builder builder() {
return new Builder();
}

public static class Builder extends ObjectBuilderBase implements ObjectBuilder<MatchedQueries> {
private Kind _kind;
private Object _value;

public Builder() {}

private Builder(MatchedQueries o) {
this._kind = o._kind;
this._value = o._value;
}

public ObjectBuilder<MatchedQueries> names(List<String> v) {
this._kind = Kind.Names;
this._value = v;
return this;
}

public ObjectBuilder<MatchedQueries> scores(Map<String, Double> v) {
this._kind = Kind.Scores;
this._value = v;
return this;
}

@Override
public MatchedQueries build() {
_checkSingleUse();
return new MatchedQueries(this);
}
}

private static JsonpDeserializer<MatchedQueries> buildMatchedQueriesDeserializer() {
return new UnionDeserializer.Builder<MatchedQueries, Kind, Object>(MatchedQueries::new, false).addMember(
Kind.Names,
JsonpDeserializer.arrayDeserializer(JsonpDeserializer.stringDeserializer())
).addMember(Kind.Scores, JsonpDeserializer.stringMapDeserializer(JsonpDeserializer.doubleDeserializer())).build();
}

public static final JsonpDeserializer<MatchedQueries> _DESERIALIZER = JsonpDeserializer.lazy(
MatchedQueries::buildMatchedQueriesDeserializer
);

@Override
public int hashCode() {
int result = 17;
result = 31 * result + Objects.hashCode(this._kind);
result = 31 * result + Objects.hashCode(this._value);
return result;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || this.getClass() != o.getClass()) return false;
MatchedQueries other = (MatchedQueries) o;
return Objects.equals(this._kind, other._kind) && Objects.equals(this._value, other._value);
}
}
Loading
Loading