From 2050ad12541293aa05a8db4f19917210e0a40bf8 Mon Sep 17 00:00:00 2001 From: Harsha Vamsi Kalluri Date: Wed, 14 Jun 2023 13:22:33 -0700 Subject: [PATCH] Fix suggestion classes for Term, Phrase, and Completion. (#477) (#529) * Fixes Completion, Phrase, and Term suggesters Signed-off-by: Harsha Vamsi Kalluri * Use resource files instead of JSON strings Signed-off-by: Harsha Vamsi Kalluri * Minor fixes to docs + checksytle Signed-off-by: Harsha Vamsi Kalluri * Adding ES license headers for copied over files Signed-off-by: Harsha Vamsi Kalluri * Fix example formatting Signed-off-by: Harsha Vamsi Kalluri * Update tests to use builders instead of JSON Signed-off-by: Harsha Vamsi Kalluri * Removing un-necessary test resources Signed-off-by: Harsha Vamsi Kalluri --------- Signed-off-by: Harsha Vamsi Kalluri (cherry picked from commit 68e2b6f03ff47ced215f600a7c9f5dd28b0d4595) --- CHANGELOG.md | 2 +- USER_GUIDE.md | 212 ++++++++++++++ .../client/json/ExternallyTaggedUnion.java | 85 ++++-- .../_types/aggregations/Aggregate.java | 10 +- .../core/search/CompletionSuggest.java | 161 +++++++++++ .../core/search/FieldSuggester.java | 179 ++++++------ .../opensearch/core/search/PhraseSuggest.java | 127 +++++++++ .../core/search/PhraseSuggestOption.java | 17 +- .../opensearch/core/search/SearchResult.java | 22 +- .../{SuggestOption.java => Suggest.java} | 83 +++--- .../opensearch/core/search/SuggestBase.java | 165 +++++++++++ .../core/search/SuggestOptionBuilders.java | 2 +- .../core/search/SuggestVariant.java | 43 +++ .../opensearch/core/search/Suggestion.java | 262 ------------------ .../opensearch/core/search/TermSuggest.java | 126 +++++++++ .../integTest/AbstractRequestIT.java | 235 +++++++++++++++- .../AbstractSearchTemplateRequestIT.java | 6 +- 17 files changed, 1299 insertions(+), 438 deletions(-) create mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/CompletionSuggest.java create mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggest.java rename java-client/src/main/java/org/opensearch/client/opensearch/core/search/{SuggestOption.java => Suggest.java} (62%) create mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestBase.java create mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestVariant.java delete mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggestion.java create mode 100644 java-client/src/main/java/org/opensearch/client/opensearch/core/search/TermSuggest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 07b9b3d4d7..6861358877 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Removed ### Fixed - +- Fixed Suggesters for Completion, Term, and Phrase and refactored the Suggestion class ([#477](https://github.com/opensearch-project/opensearch-java/pull/477)) ### Security ## [2.5.0] - 06/02/2023 diff --git a/USER_GUIDE.md b/USER_GUIDE.md index 80ad396a15..af5558e895 100644 --- a/USER_GUIDE.md +++ b/USER_GUIDE.md @@ -11,6 +11,11 @@ - [Search for the documents](#search-for-the-documents) - [Get raw JSON results](#get-raw-json-results) - [Search documents using a match query](#search-documents-using-a-match-query) + - [Search documents using suggesters](#search-documents-using-suggesters) + - [App Data class](#app-data-class) + - [Using completion suggester](#using-completion-suggester) + - [Using term suggester](#using-term-suggester) + - [Using phrase suggester](#using-phrase-suggester) - [Bulk requests](#bulk-requests) - [Aggregations](#aggregations) - [Delete the document](#delete-the-document) @@ -175,6 +180,213 @@ for (int i = 0; i < searchResponse.hits().hits().size(); i++) { } ``` +## Search documents using suggesters + +### App Data class + +```java +public static class AppData { + + private int intValue; + private String msg; + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } +} +``` + +### Using completion suggester + +```java +String index = "completion-suggester"; + +Property intValueProp = new Property.Builder() + .long_(v -> v) + .build(); +Property msgCompletionProp = new Property.Builder() + .completion(c -> c) + .build(); +client.indices().create(c -> c + .index(index) + .mappings(m -> m + .properties("intValue", intValueProp) + .properties("msg", msgCompletionProp))); + +AppData appData = new AppData(); +appData.setIntValue(1337); +appData.setMsg("foo"); + +client.index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + +appData.setIntValue(1338); +appData.setMsg("foobar"); + +client.index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + +String suggesterName = "msgSuggester"; + +CompletionSuggester completionSuggester = FieldSuggesterBuilders.completion() + .field("msg") + .size(1) + .build(); +FieldSuggester fieldSuggester = new FieldSuggester.Builder().prefix("foo") + .completion(completionSuggester) + .build(); +Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); +SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + +SearchResponse response = client.search(searchRequest, AppData.class); +``` + +### Using term suggester + +```java + String index = "term-suggester"; + +// term suggester does not require a special mapping +client.indices().create(c -> c + .index(index)); + +AppData appData = new AppData(); +appData.setIntValue(1337); +appData.setMsg("foo"); + +client.index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + +appData.setIntValue(1338); +appData.setMsg("foobar"); + +client.index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + +String suggesterName = "msgSuggester"; + +TermSuggester termSuggester = FieldSuggesterBuilders.term() + .field("msg") + .size(1) + .build(); +FieldSuggester fieldSuggester = new FieldSuggester.Builder().text("fool") + .term(termSuggester) + .build(); +Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); +SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + +SearchResponse response = client.search(searchRequest, AppData.class); +``` + +### Using phrase suggester + +```java +String index = "test-phrase-suggester"; + +ShingleTokenFilter shingleTokenFilter = new ShingleTokenFilter.Builder().minShingleSize("2") + .maxShingleSize("3") + .build(); + +Analyzer analyzer = new Analyzer.Builder() + .custom(new CustomAnalyzer.Builder().tokenizer("standard") + .filter(Arrays.asList("lowercase", "shingle")).build()) + .build(); + +TokenFilter tokenFilter = new TokenFilter.Builder() + .definition(new TokenFilterDefinition.Builder() + .shingle(shingleTokenFilter).build()) + .build(); + +IndexSettingsAnalysis indexSettingsAnalysis = new IndexSettingsAnalysis.Builder() + .analyzer("trigram", analyzer) + .filter("shingle", tokenFilter) + .build(); + +IndexSettings settings = new IndexSettings.Builder().analysis(indexSettingsAnalysis).build(); + +TypeMapping mapping = new TypeMapping.Builder().properties("msg", new Property.Builder() + .text(new TextProperty.Builder().fields("trigram", new Property.Builder() + .text(new TextProperty.Builder().analyzer("trigram").build()) + .build()).build()) + .build()).build(); + +client.indices().create(c -> c + .index(index) + .settings(settings) + .mappings(mapping)); + +AppData appData = new AppData(); +appData.setIntValue(1337); +appData.setMsg("Design Patterns"); + +client.index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + +appData.setIntValue(1338); +appData.setMsg("Software Architecture Patterns Explained"); + +client.index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + +String suggesterName = "msgSuggester"; + +PhraseSuggester phraseSuggester = FieldSuggesterBuilders.phrase() + .field("msg.trigram") + .build(); +FieldSuggester fieldSuggester = new FieldSuggester.Builder().text("design paterns") + .phrase(phraseSuggester) + .build(); +Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); +SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + +SearchResponse response = client.search(searchRequest, AppData.class); +``` + ## Bulk requests ```java diff --git a/java-client/src/main/java/org/opensearch/client/json/ExternallyTaggedUnion.java b/java-client/src/main/java/org/opensearch/client/json/ExternallyTaggedUnion.java index 84a6e6e7e4..94627156e8 100644 --- a/java-client/src/main/java/org/opensearch/client/json/ExternallyTaggedUnion.java +++ b/java-client/src/main/java/org/opensearch/client/json/ExternallyTaggedUnion.java @@ -33,14 +33,21 @@ package org.opensearch.client.json; import org.opensearch.client.util.TaggedUnion; + + import jakarta.json.stream.JsonGenerator; import jakarta.json.stream.JsonParser; import jakarta.json.stream.JsonParsingException; +import java.util.ArrayList; import java.util.EnumSet; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.function.BiFunction; +import java.util.function.Function; + +import javax.annotation.Nullable; import static jakarta.json.stream.JsonParser.Event; @@ -50,31 +57,43 @@ * encodes a name+type in a single JSON property. * */ -public interface ExternallyTaggedUnion { +public class ExternallyTaggedUnion { + + private ExternallyTaggedUnion() {} /** * A deserializer for externally-tagged unions. Since the union variant discriminant is provided externally, this cannot be a * regular {@link JsonpDeserializer} as the caller has to provide the discriminant value. */ - class Deserializer, Member> { + public static class Deserializer, Member> { private final Map> deserializers; - private final BiFunction unionCtor; + private final Function unionCtor; + @Nullable + private final BiFunction unKnownUnionCtor; - public Deserializer(Map> deserializers, BiFunction unionCtor) { + public Deserializer(Map> deserializers, + Function unionCtor) { this.deserializers = deserializers; this.unionCtor = unionCtor; + this.unKnownUnionCtor = null; + } + + public Deserializer(Map> deserializers, + Function unionCtor, BiFunction unKnownUnionCtor) { + this.deserializers = deserializers; + this.unionCtor = unionCtor; + this.unKnownUnionCtor = unKnownUnionCtor; } - /** - * Deserialize a union value, given its type. - */ - public Union deserialize(String type, JsonParser parser, JsonpMapper mapper) { + public Union deserialize(String type, JsonParser parser, JsonpMapper mapper, Event event) { JsonpDeserializer deserializer = deserializers.get(type); if (deserializer == null) { - throw new JsonParsingException("Unknown variant type '" + type + "'", parser.getLocation()); + if (unKnownUnionCtor != null) { + return unKnownUnionCtor.apply(type, JsonData._DESERIALIZER.deserialize(parser, mapper, event)); + } } - return unionCtor.apply(type, deserializer.deserialize(parser, mapper)); + return unionCtor.apply(deserializer.deserialize(parser, mapper, event)); } /** @@ -86,8 +105,9 @@ public TypedKeysDeserializer typedKeys() { } } - class TypedKeysDeserializer> extends JsonpDeserializerBase> { + public static class TypedKeysDeserializer> extends JsonpDeserializerBase> { Deserializer deserializer; + protected TypedKeysDeserializer(Deserializer deser) { super(EnumSet.of(Event.START_OBJECT)); this.deserializer = deser; @@ -107,22 +127,53 @@ public void deserializeEntry(String key, JsonParser parser, JsonpMapper mapper, int hashPos = key.indexOf('#'); if (hashPos == -1) { throw new JsonParsingException( - "Property name '" + key + "' is not in the 'type#name' format. Make sure the request has 'typed_keys' set.", - parser.getLocation() - ); + "Property name '" + key + + "' is not in the 'type#name' format. Make sure the request has 'typed_keys' set.", + parser.getLocation()); } String type = key.substring(0, hashPos); String name = key.substring(hashPos + 1); - targetMap.put(name, deserializer.deserialize(type, parser, mapper)); + targetMap.put(name, deserializer.deserialize(type, parser, mapper, parser.next())); } } + + public static > JsonpDeserializer>> arrayDeserializer( + TypedKeysDeserializer deserializer) { + return JsonpDeserializer.of( + EnumSet.of(Event.START_OBJECT), + (parser, mapper, event) -> { + Map> result = new HashMap<>(); + String key = null; + while ((event = parser.next()) != Event.END_OBJECT) { + JsonpUtils.expectEvent(parser, event, Event.KEY_NAME); + // Split key and type + key = parser.getString(); + int hashPos = key.indexOf('#'); + + String type = key.substring(0, hashPos); + String name = key.substring(hashPos + 1); + + List list = new ArrayList<>(); + JsonpUtils.expectNextEvent(parser, Event.START_ARRAY); + try { + while ((event = parser.next()) != Event.END_ARRAY) { + list.add(deserializer.deserializer.deserialize(type, parser, mapper, event)); + } + } catch (Exception e) { + throw e; + } + result.put(name, list); + } + return result; + }); + } /** * Serialize an externally tagged union using the typed keys encoding. */ - static > void serializeTypedKeys( + public static > void serializeTypedKeys( Map map, JsonGenerator generator, JsonpMapper mapper ) { generator.writeStartObject(); @@ -133,7 +184,7 @@ public void deserializeEntry(String key, JsonParser parser, JsonpMapper mapper, /** * Serialize an externally tagged union using the typed keys encoding, without the enclosing start/end object. */ - static > void serializeTypedKeysInner( + public static > void serializeTypedKeysInner( Map map, JsonGenerator generator, JsonpMapper mapper ) { for (Map.Entry entry: map.entrySet()) { diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/Aggregate.java b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/Aggregate.java index f2eb3e72ae..e939ba13f5 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/Aggregate.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/_types/aggregations/Aggregate.java @@ -37,6 +37,7 @@ package org.opensearch.client.opensearch._types.aggregations; import org.opensearch.client.json.ExternallyTaggedUnion; +import org.opensearch.client.json.JsonData; import org.opensearch.client.json.JsonEnum; import org.opensearch.client.json.JsonpDeserializer; import org.opensearch.client.json.JsonpMapper; @@ -192,6 +193,8 @@ public enum Kind implements JsonEnum { WeightedAvg("weighted_avg"), + _Custom(null), + ; private final String jsonValue; @@ -233,6 +236,11 @@ private Aggregate(Builder builder) { } + public Aggregate(String kind, JsonData value) { + this._kind = Kind._Custom; + this._value = (AggregateVariant) value; + } + public static Aggregate of(Function> fn) { return fn.apply(new Builder()).build(); } @@ -2119,6 +2127,6 @@ public Aggregate build() { deserializers.put("weighted_avg", WeightedAvgAggregate._DESERIALIZER); _TYPED_KEYS_DESERIALIZER = new ExternallyTaggedUnion.Deserializer<>(deserializers, - (name, value) -> new Aggregate(value)).typedKeys(); + Aggregate::new, Aggregate::new).typedKeys(); } } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/CompletionSuggest.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/CompletionSuggest.java new file mode 100644 index 0000000000..0d94f54e88 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/CompletionSuggest.java @@ -0,0 +1,161 @@ +/* + * 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. + */ + + /* +* Licensed to Elasticsearch B.V. under one or more contributor +* license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright +* ownership. Elasticsearch B.V. licenses this file to you under +* the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.opensearch.core.search; + +import java.util.List; +import java.util.function.Function; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.JsonpSerializer; +import org.opensearch.client.json.NamedDeserializer; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch.core.search.Suggest.Kind; +import org.opensearch.client.util.ApiTypeHelper; +import org.opensearch.client.util.ObjectBuilder; + +import jakarta.json.stream.JsonGenerator; + +@JsonpDeserializable +public class CompletionSuggest extends SuggestBase implements SuggestVariant { + private final List> options; + + @Nullable + private final JsonpSerializer tDocumentSerializer; + + private CompletionSuggest(Builder builder) { + super(builder); + + this.options = ApiTypeHelper.unmodifiableRequired(builder.options, this, "options"); + this.tDocumentSerializer = builder.tDocumentSerializer; + + } + + public static CompletionSuggest of( + Function, ObjectBuilder>> fn) { + return fn.apply(new Builder<>()).build(); + } + + public final List> options() { + return this.options; + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + + super.serializeInternal(generator, mapper); + if (ApiTypeHelper.isDefined(this.options)) { + generator.writeKey("options"); + generator.writeStartArray(); + for (CompletionSuggestOption item0 : this.options) { + item0.serialize(generator, mapper); + + } + generator.writeEnd(); + + } + + } + + public static class Builder extends SuggestBase.AbstractBuilder> + implements + ObjectBuilder> { + private List> options; + + @Nullable + private JsonpSerializer tDocumentSerializer; + + public final Builder options(List> list) { + this.options = _listAddAll(this.options, list); + return this; + } + + public final Builder options(CompletionSuggestOption value, + CompletionSuggestOption... values) { + this.options = _listAdd(this.options, value, values); + return this; + } + + public final Builder options( + Function, ObjectBuilder>> fn) { + return options(fn.apply(new CompletionSuggestOption.Builder()).build()); + } + + public final Builder tDocumentSerializer(@Nullable JsonpSerializer value) { + this.tDocumentSerializer = value; + return this; + } + + @Override + protected Builder self() { + return this; + } + + public CompletionSuggest build() { + _checkSingleUse(); + + return new CompletionSuggest(this); + } + } + + public static JsonpDeserializer> createCompletionSuggestDeserializer( + JsonpDeserializer tDocumentDeserializer) { + return ObjectBuilderDeserializer.createForObject((Supplier>) Builder::new, + op -> CompletionSuggest.setupCompletionSuggestDeserializer(op, tDocumentDeserializer)); + }; + + public static final JsonpDeserializer> _DESERIALIZER = JsonpDeserializer + .lazy(() -> createCompletionSuggestDeserializer( + new NamedDeserializer<>("org.opensearch.client:Deserializer:_global.search._types.TDocument"))); + + protected static void setupCompletionSuggestDeserializer( + ObjectDeserializer> op, + JsonpDeserializer tDocumentDeserializer) { + SuggestBase.setupSuggestBaseDeserializer(op); + op.add(Builder::options, + JsonpDeserializer.arrayDeserializer( + CompletionSuggestOption.createCompletionSuggestOptionDeserializer(tDocumentDeserializer)), + "options"); + + } + + @Override + public Kind _suggestionKind() { + return Suggest.Kind.Completion; + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/FieldSuggester.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/FieldSuggester.java index 77091848a3..50e0836df8 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/FieldSuggester.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/FieldSuggester.java @@ -45,12 +45,13 @@ import org.opensearch.client.json.ObjectDeserializer; 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; import jakarta.json.stream.JsonGenerator; import java.util.function.Function; +import javax.annotation.Nullable; + // typedef: _global.search._types.FieldSuggester @@ -69,14 +70,7 @@ public enum Kind implements JsonEnum { Phrase("phrase"), - Prefix("prefix"), - - Regex("regex"), - Term("term"), - - Text("text"), - ; private final String jsonValue; @@ -94,6 +88,15 @@ public String jsonValue() { private final Kind _kind; private final Object _value; + @Nullable + private final String prefix; + + @Nullable + private final String regex; + + @Nullable + private final String text; + @Override public final Kind _kind() { return _kind; @@ -108,6 +111,9 @@ public FieldSuggester(FieldSuggesterVariant value) { this._kind = ApiTypeHelper.requireNonNull(value._fieldSuggesterKind(), this, ""); this._value = ApiTypeHelper.requireNonNull(value, this, ""); + this.prefix = null; + this.regex = null; + this.text = null; } @@ -115,6 +121,9 @@ private FieldSuggester(Builder builder) { this._kind = ApiTypeHelper.requireNonNull(builder._kind, builder, ""); this._value = ApiTypeHelper.requireNonNull(builder._value, builder, ""); + this.prefix = builder.prefix; + this.regex = builder.regex; + this.text = builder.text; } @@ -122,6 +131,30 @@ public static FieldSuggester of(Function> return fn.apply(new Builder()).build(); } + /** + * API name: {@code prefix} + */ + @Nullable + public final String prefix() { + return this.prefix; + } + + /** + * API name: {@code regex} + */ + @Nullable + public final String regex() { + return this.regex; + } + + /** + * API name: {@code text} + */ + @Nullable + public final String text() { + return this.text; + } + /** * Is this variant instance of kind {@code completion}? */ @@ -156,40 +189,6 @@ public PhraseSuggester phrase() { return TaggedUnionUtils.get(this, Kind.Phrase); } - /** - * Is this variant instance of kind {@code prefix}? - */ - public boolean isPrefix() { - return _kind == Kind.Prefix; - } - - /** - * Get the {@code prefix} variant value. - * - * @throws IllegalStateException - * if the current variant is not of the {@code prefix} kind. - */ - public String prefix() { - return TaggedUnionUtils.get(this, Kind.Prefix); - } - - /** - * Is this variant instance of kind {@code regex}? - */ - public boolean isRegex() { - return _kind == Kind.Regex; - } - - /** - * Get the {@code regex} variant value. - * - * @throws IllegalStateException - * if the current variant is not of the {@code regex} kind. - */ - public String regex() { - return TaggedUnionUtils.get(this, Kind.Regex); - } - /** * Is this variant instance of kind {@code term}? */ @@ -207,23 +206,6 @@ public TermSuggester term() { return TaggedUnionUtils.get(this, Kind.Term); } - /** - * Is this variant instance of kind {@code text}? - */ - public boolean isText() { - return _kind == Kind.Text; - } - - /** - * Get the {@code text} variant value. - * - * @throws IllegalStateException - * if the current variant is not of the {@code text} kind. - */ - public String text() { - return TaggedUnionUtils.get(this, Kind.Text); - } - @Override @SuppressWarnings("unchecked") public void serialize(JsonGenerator generator, JsonpMapper mapper) { @@ -233,30 +215,36 @@ public void serialize(JsonGenerator generator, JsonpMapper mapper) { generator.writeKey(_kind.jsonValue()); if (_value instanceof JsonpSerializable) { ((JsonpSerializable) _value).serialize(generator, mapper); - } else { - switch (_kind) { - case Prefix : - generator.write(((String) this._value)); - - break; - case Regex : - generator.write(((String) this._value)); - - break; - case Text : - generator.write(((String) this._value)); - - break; - } + } + if (this.prefix != null) { + generator.writeKey("prefix"); + generator.write(this.prefix); + } + if (this.regex != null) { + generator.writeKey("regex"); + generator.write(this.regex); + } + if (this.text != null) { + generator.writeKey("text"); + generator.write(this.text); } generator.writeEnd(); } - public static class Builder extends ObjectBuilderBase implements ObjectBuilder { + public static class Builder extends SuggesterBase.AbstractBuilder implements ObjectBuilder { private Kind _kind; private Object _value; + + @Nullable + private String prefix; + + @Nullable + private String regex; + + @Nullable + private String text; public ObjectBuilder completion(CompletionSuggester v) { this._kind = Kind.Completion; @@ -280,31 +268,37 @@ public ObjectBuilder phrase( return this.phrase(fn.apply(new PhraseSuggester.Builder()).build()); } - public ObjectBuilder prefix(String v) { - this._kind = Kind.Prefix; + public ObjectBuilder term(TermSuggester v) { + this._kind = Kind.Term; this._value = v; return this; } - public ObjectBuilder regex(String v) { - this._kind = Kind.Regex; - this._value = v; - return this; + public ObjectBuilder term(Function> fn) { + return this.term(fn.apply(new TermSuggester.Builder()).build()); } - public ObjectBuilder term(TermSuggester v) { - this._kind = Kind.Term; - this._value = v; + /** + * API name: {@code prefix} + */ + public final Builder prefix(@Nullable String value) { + this.prefix = value; return this; } - public ObjectBuilder term(Function> fn) { - return this.term(fn.apply(new TermSuggester.Builder()).build()); + /** + * API name: {@code regex} + */ + public final Builder regex(@Nullable String value) { + this.regex = value; + return this; } - public ObjectBuilder text(String v) { - this._kind = Kind.Text; - this._value = v; + /** + * API name: {@code text} + */ + public final Builder text(@Nullable String value) { + this.text = value; return this; } @@ -313,15 +307,20 @@ public FieldSuggester build() { return new FieldSuggester(this); } + @Override + protected Builder self() { + return this; + } + } protected static void setupFieldSuggesterDeserializer(ObjectDeserializer op) { op.add(Builder::completion, CompletionSuggester._DESERIALIZER, "completion"); op.add(Builder::phrase, PhraseSuggester._DESERIALIZER, "phrase"); + op.add(Builder::term, TermSuggester._DESERIALIZER, "term"); op.add(Builder::prefix, JsonpDeserializer.stringDeserializer(), "prefix"); op.add(Builder::regex, JsonpDeserializer.stringDeserializer(), "regex"); - op.add(Builder::term, TermSuggester._DESERIALIZER, "term"); op.add(Builder::text, JsonpDeserializer.stringDeserializer(), "text"); } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggest.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggest.java new file mode 100644 index 0000000000..b52cbe63e3 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggest.java @@ -0,0 +1,127 @@ +/* + * 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. + */ + + /* +* Licensed to Elasticsearch B.V. under one or more contributor +* license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright +* ownership. Elasticsearch B.V. licenses this file to you under +* the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.opensearch.core.search; + +import java.util.List; +import java.util.function.Function; + +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch.core.search.Suggest.Kind; +import org.opensearch.client.util.ApiTypeHelper; +import org.opensearch.client.util.ObjectBuilder; + +import jakarta.json.stream.JsonGenerator; + +@JsonpDeserializable +public class PhraseSuggest extends SuggestBase implements SuggestVariant { + private final List options; + + + private PhraseSuggest(Builder builder) { + super(builder); + + this.options = ApiTypeHelper.unmodifiableRequired(builder.options, this, "options"); + + } + + public static PhraseSuggest of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public final List options() { + return this.options; + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + + super.serializeInternal(generator, mapper); + if (ApiTypeHelper.isDefined(this.options)) { + generator.writeKey("options"); + generator.writeStartArray(); + for (PhraseSuggestOption item0 : this.options) { + item0.serialize(generator, mapper); + + } + generator.writeEnd(); + + } + + } + + public static class Builder extends SuggestBase.AbstractBuilder implements ObjectBuilder { + private List options; + + public final Builder options(List list) { + this.options = _listAddAll(this.options, list); + return this; + } + + public final Builder options(PhraseSuggestOption value, PhraseSuggestOption... values) { + this.options = _listAdd(this.options, value, values); + return this; + } + + public final Builder options(Function> fn) { + return options(fn.apply(new PhraseSuggestOption.Builder()).build()); + } + + @Override + protected Builder self() { + return this; + } + + public PhraseSuggest build() { + _checkSingleUse(); + + return new PhraseSuggest(this); + } + } + + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy(Builder::new, + PhraseSuggest::setupPhraseSuggestDeserializer); + + protected static void setupPhraseSuggestDeserializer(ObjectDeserializer op) { + SuggestBase.setupSuggestBaseDeserializer(op); + op.add(Builder::options, JsonpDeserializer.arrayDeserializer(PhraseSuggestOption._DESERIALIZER), "options"); + + } + + @Override + public Kind _suggestionKind() { + return Suggest.Kind.Phrase; + } +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggestOption.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggestOption.java index f64ebfbdd5..3d0a15d8c0 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggestOption.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/PhraseSuggestOption.java @@ -48,6 +48,8 @@ import jakarta.json.stream.JsonGenerator; import java.util.function.Function; +import javax.annotation.Nullable; + // typedef: _global.search._types.PhraseSuggestOption @@ -55,6 +57,7 @@ public class PhraseSuggestOption implements JsonpSerializable { private final String text; + @Nullable private final String highlighted; private final double score; @@ -64,7 +67,7 @@ public class PhraseSuggestOption implements JsonpSerializable { private PhraseSuggestOption(Builder builder) { this.text = ApiTypeHelper.requireNonNull(builder.text, this, "text"); - this.highlighted = ApiTypeHelper.requireNonNull(builder.highlighted, this, "highlighted"); + this.highlighted = builder.highlighted; this.score = ApiTypeHelper.requireNonNull(builder.score, this, "score"); } @@ -81,8 +84,9 @@ public final String text() { } /** - * Required - API name: {@code highlighted} + * API name: {@code highlighted} */ + @Nullable public final String highlighted() { return this.highlighted; } @@ -108,8 +112,10 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { generator.writeKey("text"); generator.write(this.text); - generator.writeKey("highlighted"); - generator.write(this.highlighted); + if (this.highlighted != null) { + generator.writeKey("highlighted"); + generator.write(this.highlighted); + } generator.writeKey("score"); generator.write(this.score); @@ -125,6 +131,7 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { public static class Builder extends ObjectBuilderBase implements ObjectBuilder { private String text; + @Nullable private String highlighted; private Double score; @@ -140,7 +147,7 @@ public final Builder text(String value) { /** * Required - API name: {@code highlighted} */ - public final Builder highlighted(String value) { + public final Builder highlighted(@Nullable String value) { this.highlighted = value; return this; } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SearchResult.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SearchResult.java index 69933e43ee..7d3d084dd9 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SearchResult.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SearchResult.java @@ -17,7 +17,7 @@ import org.opensearch.client.opensearch._types.aggregations.Aggregate; import org.opensearch.client.opensearch.core.search.HitsMetadata; import org.opensearch.client.opensearch.core.search.Profile; -import org.opensearch.client.opensearch.core.search.Suggestion; +import org.opensearch.client.opensearch.core.search.Suggest; import org.opensearch.client.json.ExternallyTaggedUnion; import org.opensearch.client.json.JsonData; import org.opensearch.client.json.JsonpDeserializable; @@ -76,7 +76,7 @@ public abstract class SearchResult implements JsonpSerializable { @Nullable private final String scrollId; - private final Map>> suggest; + private final Map>> suggest; @Nullable private final Boolean terminatedEarly; @@ -207,7 +207,7 @@ public final String scrollId() { /** * API name: {@code suggest} */ - public final Map>> suggest() { + public final Map>> suggest() { return this.suggest; } @@ -301,11 +301,11 @@ protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { if (ApiTypeHelper.isDefined(this.suggest)) { generator.writeKey("suggest"); generator.writeStartObject(); - for (Map.Entry>> item0 : this.suggest.entrySet()) { + for (Map.Entry>> item0 : this.suggest.entrySet()) { generator.writeKey(item0.getKey()); generator.writeStartArray(); if (item0.getValue() != null) { - for (Suggestion item1 : item0.getValue()) { + for (Suggest item1 : item0.getValue()) { item1.serialize(generator, mapper); } @@ -365,7 +365,7 @@ protected abstract static class AbstractBuilder>> suggest; + private Map>> suggest; @Nullable private Boolean terminatedEarly; @@ -556,7 +556,7 @@ public final BuilderT scrollId(@Nullable String value) { *

* Adds all entries of map to suggest. */ - public final BuilderT suggest(Map>> map) { + public final BuilderT suggest(Map>> map) { this.suggest = _mapPutAll(this.suggest, map); return self(); } @@ -566,7 +566,7 @@ public final BuilderT suggest(Map>> map) { *

* Adds an entry to suggest. */ - public final BuilderT suggest(String key, List> value) { + public final BuilderT suggest(String key, List> value) { this.suggest = _mapPut(this.suggest, key, value); return self(); } @@ -610,9 +610,9 @@ protected static >arrayDeserializer( + Suggest.createSuggestDeserializer(tDocumentDeserializer)), "suggest"); + ; op.add(AbstractBuilder::terminatedEarly, JsonpDeserializer.booleanDeserializer(), "terminated_early"); } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOption.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggest.java similarity index 62% rename from java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOption.java rename to java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggest.java index 72f97f9c84..33cd35e581 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOption.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggest.java @@ -36,24 +36,28 @@ package org.opensearch.client.opensearch.core.search; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import org.opensearch.client.json.ExternallyTaggedUnion; import org.opensearch.client.json.JsonpDeserializer; import org.opensearch.client.json.JsonpMapper; import org.opensearch.client.json.JsonpSerializable; -import org.opensearch.client.json.JsonpSerializer; -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; + import jakarta.json.stream.JsonGenerator; -import java.util.function.Function; // typedef: _global.search._types.SuggestOption -public class SuggestOption implements TaggedUnion, JsonpSerializable { +public class Suggest implements TaggedUnion, JsonpSerializable { public enum Kind { Completion, Phrase, Term @@ -61,7 +65,7 @@ public enum Kind { } private final Kind _kind; - private final Object _value; + private final SuggestVariant _value; @Override public final Kind _kind() { @@ -69,26 +73,25 @@ public final Kind _kind() { } @Override - public final Object _get() { + public final SuggestVariant _get() { return _value; } - private final JsonpSerializer tDocumentSerializer = null; - private SuggestOption(Kind kind, Object value) { - this._kind = kind; - this._value = value; + public Suggest(SuggestVariant value) { + this._kind = ApiTypeHelper.requireNonNull(value._suggestionKind(), this, ""); + this._value = ApiTypeHelper.requireNonNull(value, this, ""); } - private SuggestOption(Builder builder) { + private Suggest(Builder builder) { this._kind = ApiTypeHelper.requireNonNull(builder._kind, builder, ""); this._value = ApiTypeHelper.requireNonNull(builder._value, builder, ""); } - public static SuggestOption of( - Function, ObjectBuilder>> fn) { + public static Suggest of( + Function, ObjectBuilder>> fn) { return fn.apply(new Builder<>()).build(); } @@ -105,7 +108,7 @@ public boolean isCompletion() { * @throws IllegalStateException * if the current variant is not of the {@code completion} kind. */ - public CompletionSuggestOption completion() { + public CompletionSuggest completion() { return TaggedUnionUtils.get(this, Kind.Completion); } @@ -122,7 +125,7 @@ public boolean isPhrase() { * @throws IllegalStateException * if the current variant is not of the {@code phrase} kind. */ - public PhraseSuggestOption phrase() { + public PhraseSuggest phrase() { return TaggedUnionUtils.get(this, Kind.Phrase); } @@ -139,7 +142,7 @@ public boolean isTerm() { * @throws IllegalStateException * if the current variant is not of the {@code term} kind. */ - public TermSuggestOption term() { + public TermSuggest term() { return TaggedUnionUtils.get(this, Kind.Term); } @@ -153,58 +156,60 @@ public void serialize(JsonGenerator generator, JsonpMapper mapper) { public static class Builder extends ObjectBuilderBase implements - ObjectBuilder> { + ObjectBuilder> { private Kind _kind; - private Object _value; + private SuggestVariant _value; - public ObjectBuilder> completion(CompletionSuggestOption v) { + public ObjectBuilder> completion(CompletionSuggest v) { this._kind = Kind.Completion; this._value = v; return this; } - public ObjectBuilder> completion( - Function, - ObjectBuilder>> fn) { - return this.completion(fn.apply(new CompletionSuggestOption.Builder()).build()); + public ObjectBuilder> completion( + Function, + ObjectBuilder>> fn) { + return this.completion(fn.apply(new CompletionSuggest.Builder()).build()); } - public ObjectBuilder> phrase(PhraseSuggestOption v) { + public ObjectBuilder> phrase(PhraseSuggest v) { this._kind = Kind.Phrase; this._value = v; return this; } - public ObjectBuilder> phrase( - Function> fn) { - return this.phrase(fn.apply(new PhraseSuggestOption.Builder()).build()); + public ObjectBuilder> phrase( + Function> fn) { + return this.phrase(fn.apply(new PhraseSuggest.Builder()).build()); } - public ObjectBuilder> term(TermSuggestOption v) { + public ObjectBuilder> term(TermSuggest v) { this._kind = Kind.Term; this._value = v; return this; } - public ObjectBuilder> term( - Function> fn) { - return this.term(fn.apply(new TermSuggestOption.Builder()).build()); + public ObjectBuilder> term( + Function> fn) { + return this.term(fn.apply(new TermSuggest.Builder()).build()); } - public SuggestOption build() { + public Suggest build() { _checkSingleUse(); - return new SuggestOption<>(this); + return new Suggest<>(this); } } - public static JsonpDeserializer> createSuggestOptionDeserializer( + public static ExternallyTaggedUnion.TypedKeysDeserializer> createSuggestDeserializer( JsonpDeserializer tDocumentDeserializer) { - return new UnionDeserializer.Builder, Kind, Object>(SuggestOption::new, - false).addMember(Kind.Completion, - CompletionSuggestOption.createCompletionSuggestOptionDeserializer(tDocumentDeserializer)) - .addMember(Kind.Phrase, PhraseSuggestOption._DESERIALIZER) - .addMember(Kind.Term, TermSuggestOption._DESERIALIZER).build(); + Map> deserializers = new HashMap<>(); + deserializers.put("completion", CompletionSuggest.createCompletionSuggestDeserializer(tDocumentDeserializer)); + deserializers.put("phrase", PhraseSuggest._DESERIALIZER); + deserializers.put("term", TermSuggest._DESERIALIZER); + + return new ExternallyTaggedUnion.Deserializer, SuggestVariant>(deserializers, + Suggest::new).typedKeys(); } } diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestBase.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestBase.java new file mode 100644 index 0000000000..85cba9dad5 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestBase.java @@ -0,0 +1,165 @@ +/* + * 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. + */ + +/* + * Licensed to Elasticsearch B.V. under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch B.V. licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +//---------------------------------------------------- +// THIS CODE IS GENERATED. MANUAL EDITS WILL BE LOST. +//---------------------------------------------------- + +package org.opensearch.client.opensearch.core.search; + +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.JsonpSerializable; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.util.ApiTypeHelper; +import org.opensearch.client.util.ObjectBuilder; +import org.opensearch.client.util.ObjectBuilderBase; + +import jakarta.json.stream.JsonGenerator; + +// typedef: _global.search._types.Suggest + + + +public abstract class SuggestBase implements JsonpSerializable { + private final int length; + + private final int offset; + + private final String text; + + // --------------------------------------------------------------------------------------------- + + protected SuggestBase(AbstractBuilder builder) { + + this.length = ApiTypeHelper.requireNonNull(builder.length, this, "length"); + this.offset = ApiTypeHelper.requireNonNull(builder.offset, this, "offset"); + this.text = ApiTypeHelper.requireNonNull(builder.text, this, "text"); + + } + + /** + * Required - API name: {@code length} + */ + public final int length() { + return this.length; + } + + /** + * Required - API name: {@code offset} + */ + public final int offset() { + return this.offset; + } + + /** + * Required - API name: {@code text} + */ + public final String text() { + return this.text; + } + + /** + * Serialize this object to JSON. + */ + public void serialize(JsonGenerator generator, JsonpMapper mapper) { + generator.writeStartObject(); + serializeInternal(generator, mapper); + generator.writeEnd(); + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + + generator.writeKey("length"); + generator.write(this.length); + + generator.writeKey("offset"); + generator.write(this.offset); + + generator.writeKey("text"); + generator.write(this.text); + + } + + // --------------------------------------------------------------------------------------------- + + /** + * Builder for {@link SuggestBase}. + */ + + public abstract static class AbstractBuilder> extends ObjectBuilderBase { + private Integer length; + + private Integer offset; + + private String text; + + /** + * Required - API name: {@code length} + */ + public final BuilderT length(int value) { + this.length = value; + return self(); + } + + /** + * Required - API name: {@code offset} + */ + public final BuilderT offset(int value) { + this.offset = value; + return self(); + } + + /** + * Required - API name: {@code text} + */ + public final BuilderT text(String value) { + this.text = value; + return self(); + } + + + protected abstract BuilderT self(); + + } + + + protected static > void setupSuggestBaseDeserializer( + ObjectDeserializer op) { + + op.add(AbstractBuilder::length, JsonpDeserializer.integerDeserializer(), "length"); + op.add(AbstractBuilder::offset, JsonpDeserializer.integerDeserializer(), "offset"); + op.add(AbstractBuilder::text, JsonpDeserializer.stringDeserializer(), "text"); + + } + +} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOptionBuilders.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOptionBuilders.java index 0e5e4a8a8b..26bf13823c 100644 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOptionBuilders.java +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestOptionBuilders.java @@ -37,7 +37,7 @@ package org.opensearch.client.opensearch.core.search; /** - * Builders for {@link SuggestOption} variants. + * Builders for {@link Suggest} variants. */ public class SuggestOptionBuilders { private SuggestOptionBuilders() { diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestVariant.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestVariant.java new file mode 100644 index 0000000000..f3230775d6 --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/SuggestVariant.java @@ -0,0 +1,43 @@ +/* + * 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. + */ + + /* +* Licensed to Elasticsearch B.V. under one or more contributor +* license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright +* ownership. Elasticsearch B.V. licenses this file to you under +* the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.opensearch.core.search; + +public interface SuggestVariant { + + Suggest.Kind _suggestionKind(); + + default Suggest _toSuggestion() { + return new Suggest(this); + } + +} \ No newline at end of file diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggestion.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggestion.java deleted file mode 100644 index e6b32c3a5c..0000000000 --- a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/Suggestion.java +++ /dev/null @@ -1,262 +0,0 @@ -/* - * 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. - */ - -/* - * Licensed to Elasticsearch B.V. under one or more contributor - * license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright - * ownership. Elasticsearch B.V. licenses this file to you under - * the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -/* - * Modifications Copyright OpenSearch Contributors. See - * GitHub history for details. - */ - -//---------------------------------------------------- -// THIS CODE IS GENERATED. MANUAL EDITS WILL BE LOST. -//---------------------------------------------------- - -package org.opensearch.client.opensearch.core.search; - -import org.opensearch.client.json.JsonpDeserializer; -import org.opensearch.client.json.JsonpMapper; -import org.opensearch.client.json.JsonpSerializable; -import org.opensearch.client.json.JsonpSerializer; -import org.opensearch.client.json.ObjectBuilderDeserializer; -import org.opensearch.client.json.ObjectDeserializer; -import org.opensearch.client.util.ApiTypeHelper; -import org.opensearch.client.util.ObjectBuilder; -import org.opensearch.client.util.ObjectBuilderBase; -import jakarta.json.stream.JsonGenerator; -import java.util.List; -import java.util.function.Function; -import java.util.function.Supplier; -import javax.annotation.Nullable; - -// typedef: _global.search._types.Suggest - - - -public class Suggestion implements JsonpSerializable { - private final int length; - - private final int offset; - - private final List> options; - - private final String text; - - @Nullable - private final JsonpSerializer tSerializer; - - // --------------------------------------------------------------------------------------------- - - private Suggestion(Builder builder) { - - this.length = ApiTypeHelper.requireNonNull(builder.length, this, "length"); - this.offset = ApiTypeHelper.requireNonNull(builder.offset, this, "offset"); - this.options = ApiTypeHelper.unmodifiableRequired(builder.options, this, "options"); - this.text = ApiTypeHelper.requireNonNull(builder.text, this, "text"); - this.tSerializer = builder.tSerializer; - - } - - public static Suggestion of(Function, ObjectBuilder>> fn) { - return fn.apply(new Builder<>()).build(); - } - - /** - * Required - API name: {@code length} - */ - public final int length() { - return this.length; - } - - /** - * Required - API name: {@code offset} - */ - public final int offset() { - return this.offset; - } - - /** - * Required - API name: {@code options} - */ - public final List> options() { - return this.options; - } - - /** - * Required - API name: {@code text} - */ - public final String text() { - return this.text; - } - - /** - * Serialize this object to JSON. - */ - public void serialize(JsonGenerator generator, JsonpMapper mapper) { - generator.writeStartObject(); - serializeInternal(generator, mapper); - generator.writeEnd(); - } - - protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { - - generator.writeKey("length"); - generator.write(this.length); - - generator.writeKey("offset"); - generator.write(this.offset); - - if (ApiTypeHelper.isDefined(this.options)) { - generator.writeKey("options"); - generator.writeStartArray(); - for (SuggestOption item0 : this.options) { - item0.serialize(generator, mapper); - - } - generator.writeEnd(); - - } - generator.writeKey("text"); - generator.write(this.text); - - } - - // --------------------------------------------------------------------------------------------- - - /** - * Builder for {@link Suggestion}. - */ - - public static class Builder extends ObjectBuilderBase implements ObjectBuilder> { - private Integer length; - - private Integer offset; - - private List> options; - - private String text; - - @Nullable - private JsonpSerializer tSerializer; - - /** - * Required - API name: {@code length} - */ - public final Builder length(int value) { - this.length = value; - return this; - } - - /** - * Required - API name: {@code offset} - */ - public final Builder offset(int value) { - this.offset = value; - return this; - } - - /** - * Required - API name: {@code options} - *

- * Adds all elements of list to options. - */ - public final Builder options(List> list) { - this.options = _listAddAll(this.options, list); - return this; - } - - /** - * Required - API name: {@code options} - *

- * Adds one or more values to options. - */ - public final Builder options(SuggestOption value, SuggestOption... values) { - this.options = _listAdd(this.options, value, values); - return this; - } - - /** - * Required - API name: {@code options} - *

- * Adds a value to options using a builder lambda. - */ - public final Builder options(Function, ObjectBuilder>> fn) { - return options(fn.apply(new SuggestOption.Builder()).build()); - } - - /** - * Required - API name: {@code text} - */ - public final Builder text(String value) { - this.text = value; - return this; - } - - /** - * Serializer for T. If not set, an attempt will be made to find a serializer - * from the JSON context. - */ - public final Builder tSerializer(@Nullable JsonpSerializer value) { - this.tSerializer = value; - return this; - } - - /** - * Builds a {@link Suggestion}. - * - * @throws NullPointerException - * if some of the required fields are null. - */ - public Suggestion build() { - _checkSingleUse(); - - return new Suggestion(this); - } - } - - // --------------------------------------------------------------------------------------------- - - /** - * Create a JSON deserializer for Suggestion - */ - public static JsonpDeserializer> createSuggestionDeserializer( - JsonpDeserializer tDeserializer) { - return ObjectBuilderDeserializer.createForObject((Supplier>) Builder::new, - op -> Suggestion.setupSuggestionDeserializer(op, tDeserializer)); - }; - - protected static void setupSuggestionDeserializer(ObjectDeserializer> op, - JsonpDeserializer tDeserializer) { - - op.add(Builder::length, JsonpDeserializer.integerDeserializer(), "length"); - op.add(Builder::offset, JsonpDeserializer.integerDeserializer(), "offset"); - op.add(Builder::options, - JsonpDeserializer.arrayDeserializer(SuggestOption.createSuggestOptionDeserializer(tDeserializer)), - "options"); - op.add(Builder::text, JsonpDeserializer.stringDeserializer(), "text"); - - } - -} diff --git a/java-client/src/main/java/org/opensearch/client/opensearch/core/search/TermSuggest.java b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/TermSuggest.java new file mode 100644 index 0000000000..37ebdbb17c --- /dev/null +++ b/java-client/src/main/java/org/opensearch/client/opensearch/core/search/TermSuggest.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + + /* +* Licensed to Elasticsearch B.V. under one or more contributor +* license agreements. See the NOTICE file distributed with +* this work for additional information regarding copyright +* ownership. Elasticsearch B.V. licenses this file to you under +* the Apache License, Version 2.0 (the "License"); you may +* not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, +* software distributed under the License is distributed on an +* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +* KIND, either express or implied. See the License for the +* specific language governing permissions and limitations +* under the License. +*/ + +/* + * Modifications Copyright OpenSearch Contributors. See + * GitHub history for details. + */ + +package org.opensearch.client.opensearch.core.search; + +import java.util.List; +import java.util.function.Function; + +import org.opensearch.client.json.JsonpDeserializable; +import org.opensearch.client.json.JsonpDeserializer; +import org.opensearch.client.json.JsonpMapper; +import org.opensearch.client.json.ObjectBuilderDeserializer; +import org.opensearch.client.json.ObjectDeserializer; +import org.opensearch.client.opensearch.core.search.Suggest.Kind; +import org.opensearch.client.util.ApiTypeHelper; +import org.opensearch.client.util.ObjectBuilder; + +import jakarta.json.stream.JsonGenerator; + +@JsonpDeserializable +public class TermSuggest extends SuggestBase implements SuggestVariant { + private final List options; + + private TermSuggest(Builder builder) { + super(builder); + + this.options = ApiTypeHelper.unmodifiableRequired(builder.options, this, "options"); + + } + + public static TermSuggest of(Function> fn) { + return fn.apply(new Builder()).build(); + } + + public final List options() { + return this.options; + } + + protected void serializeInternal(JsonGenerator generator, JsonpMapper mapper) { + + super.serializeInternal(generator, mapper); + if (ApiTypeHelper.isDefined(this.options)) { + generator.writeKey("options"); + generator.writeStartArray(); + for (TermSuggestOption item0 : this.options) { + item0.serialize(generator, mapper); + + } + generator.writeEnd(); + + } + + } + + public static class Builder extends SuggestBase.AbstractBuilder implements ObjectBuilder { + private List options; + + public final Builder options(List list) { + this.options = _listAddAll(this.options, list); + return this; + } + + public final Builder options(TermSuggestOption value, TermSuggestOption... values) { + this.options = _listAdd(this.options, value, values); + return this; + } + + public final Builder options(Function> fn) { + return options(fn.apply(new TermSuggestOption.Builder()).build()); + } + + @Override + protected Builder self() { + return this; + } + + public TermSuggest build() { + _checkSingleUse(); + + return new TermSuggest(this); + } + } + + public static final JsonpDeserializer _DESERIALIZER = ObjectBuilderDeserializer.lazy(Builder::new, + TermSuggest::setupTermSuggestDeserializer); + + protected static void setupTermSuggestDeserializer(ObjectDeserializer op) { + SuggestBase.setupSuggestBaseDeserializer(op); + op.add(Builder::options, JsonpDeserializer.arrayDeserializer(TermSuggestOption._DESERIALIZER), "options"); + + } + + @Override + public Kind _suggestionKind() { + return Suggest.Kind.Term; + } +} diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java index 0435cb9f72..c8529fc470 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractRequestIT.java @@ -42,7 +42,14 @@ import org.opensearch.client.opensearch._types.aggregations.Aggregate; import org.opensearch.client.opensearch._types.aggregations.HistogramAggregate; import org.opensearch.client.opensearch._types.aggregations.TermsAggregation; +import org.opensearch.client.opensearch._types.analysis.Analyzer; +import org.opensearch.client.opensearch._types.analysis.CustomAnalyzer; +import org.opensearch.client.opensearch._types.analysis.ShingleTokenFilter; +import org.opensearch.client.opensearch._types.analysis.TokenFilter; +import org.opensearch.client.opensearch._types.analysis.TokenFilterDefinition; import org.opensearch.client.opensearch._types.mapping.Property; +import org.opensearch.client.opensearch._types.mapping.TextProperty; +import org.opensearch.client.opensearch._types.mapping.TypeMapping; import org.opensearch.client.opensearch._types.query_dsl.BoolQuery; import org.opensearch.client.opensearch._types.query_dsl.TermsQuery; import org.opensearch.client.opensearch.cat.NodesResponse; @@ -65,16 +72,21 @@ import org.opensearch.client.opensearch.core.search.FieldSuggester; import org.opensearch.client.opensearch.core.search.FieldSuggesterBuilders; import org.opensearch.client.opensearch.core.search.Hit; +import org.opensearch.client.opensearch.core.search.PhraseSuggester; import org.opensearch.client.opensearch.core.search.Suggester; +import org.opensearch.client.opensearch.core.search.TermSuggester; import org.opensearch.client.opensearch.indices.CreateIndexResponse; import org.opensearch.client.opensearch.indices.GetIndexResponse; import org.opensearch.client.opensearch.indices.GetIndicesSettingsResponse; import org.opensearch.client.opensearch.indices.GetMappingResponse; +import org.opensearch.client.opensearch.indices.IndexSettings; +import org.opensearch.client.opensearch.indices.IndexSettingsAnalysis; import org.opensearch.client.opensearch.indices.IndexState; import org.opensearch.client.opensearch.model.ModelTestCase; import org.opensearch.client.transport.endpoints.BooleanResponse; import java.io.IOException; +import java.util.Arrays; import java.util.Collections; import java.util.List; import java.util.Map; @@ -542,9 +554,10 @@ public void testDefaultIndexSettings() throws IOException { } @Test - public void testCompletionSuggester() throws IOException { + public void testCompletionSuggesterFailure() throws IOException { - String index = "test-completion-suggester"; + String index = "test-completion-suggester-failure"; + Property intValueProp = new Property.Builder() .long_(v -> v) @@ -569,7 +582,7 @@ public void testCompletionSuggester() throws IOException { .refresh(Refresh.True)); appData.setIntValue(1338); - appData.setMsg("bar"); + appData.setMsg("foobar"); javaClient().index(b -> b .index(index) @@ -577,16 +590,17 @@ public void testCompletionSuggester() throws IOException { .document(appData) .refresh(Refresh.True)); + String suggesterName = "msgSuggester"; + CompletionSuggester completionSuggester = FieldSuggesterBuilders.completion() .field("msg") .size(1) .build(); - FieldSuggester fieldSuggester = new FieldSuggester.Builder() + FieldSuggester fieldSuggester = new FieldSuggester.Builder().prefix("xyz") .completion(completionSuggester) .build(); Suggester suggester = new Suggester.Builder() - .suggesters(Collections.singletonMap("msgSuggester", fieldSuggester)) - .text("foo") + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) .build(); SearchRequest searchRequest = new SearchRequest.Builder() .index(index) @@ -595,6 +609,9 @@ public void testCompletionSuggester() throws IOException { SearchResponse response = javaClient().search(searchRequest, AppData.class); assertTrue(response.suggest().size() > 0); + assertTrue(response.suggest().keySet().contains(suggesterName)); + assertNotNull(response.suggest().get(suggesterName)); + assertEquals(response.suggest().get(suggesterName).get(0).completion().options().size(), 0); } @Test @@ -605,7 +622,8 @@ public void testPit() throws IOException { version = version.split("-")[0]; } assumeTrue("The PIT is supported in OpenSearch 2.4.0 and later", - Version.fromString(version).onOrAfter(Version.fromString("2.4.0"))); + Version.fromString(version).onOrAfter(Version.fromString("2.4.0"))); + String index = "test-point-in-time"; javaClient().indices().create(c -> c @@ -620,7 +638,6 @@ public void testPit() throws IOException { .id("1") .document(appData) .refresh(Refresh.True)); - CreatePitRequest createPitRequest = new CreatePitRequest.Builder() .targetIndexes(Collections.singletonList(index)) .keepAlive(new Time.Builder().time("100m").build()).build(); @@ -652,6 +669,208 @@ public void testPit() throws IOException { assertTrue(deletePitResponse.pits().get(0).successful()); } + public void testCompletionSuggester() throws IOException { + + String index = "test-completion-suggester"; + + Property intValueProp = new Property.Builder() + .long_(v -> v) + .build(); + Property msgCompletionProp = new Property.Builder() + .completion(c -> c) + .build(); + javaClient().indices().create(c -> c + .index(index) + .mappings(m -> m + .properties("intValue", intValueProp) + .properties("msg", msgCompletionProp))); + + AppData appData = new AppData(); + appData.setIntValue(1337); + appData.setMsg("foo"); + + javaClient().index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + + appData.setIntValue(1338); + appData.setMsg("foobar"); + + javaClient().index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + + String suggesterName = "msgSuggester"; + + CompletionSuggester completionSuggester = FieldSuggesterBuilders.completion() + .field("msg") + .size(1) + .build(); + FieldSuggester fieldSuggester = new FieldSuggester.Builder().prefix("foo") + .completion(completionSuggester) + .build(); + Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); + SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + + SearchResponse response = javaClient().search(searchRequest, AppData.class); + assertTrue(response.suggest().size() > 0); + assertTrue(response.suggest().keySet().contains(suggesterName)); + assertNotNull(response.suggest().get(suggesterName)); + assertNotNull(response.suggest().get(suggesterName).get(0).completion().options()); + assertTrue(response.suggest().get(suggesterName).get(0).isCompletion()); + assertNotNull(response.suggest().get(suggesterName).get(0).completion().options()); + assertEquals(response.suggest().get(suggesterName).get(0).completion().options().get(0) + .text(), "foo"); + } + + @Test + public void testTermSuggester() throws IOException { + + String index = "test-term-suggester"; + + // term suggester does not require a special mapping + javaClient().indices().create(c -> c + .index(index)); + + AppData appData = new AppData(); + appData.setIntValue(1337); + appData.setMsg("foo"); + + javaClient().index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + + appData.setIntValue(1338); + appData.setMsg("foobar"); + + javaClient().index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + + String suggesterName = "msgSuggester"; + + TermSuggester termSuggester = FieldSuggesterBuilders.term() + .field("msg") + .size(1) + .build(); + FieldSuggester fieldSuggester = new FieldSuggester.Builder().text("fool") + .term(termSuggester) + .build(); + Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); + SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + + SearchResponse response = javaClient().search(searchRequest, AppData.class); + assertTrue(response.suggest().size() > 0); + assertTrue(response.suggest().keySet().contains(suggesterName)); + assertNotNull(response.suggest().get(suggesterName)); + assertTrue(response.suggest().get(suggesterName).get(0).isTerm()); + assertNotNull(response.suggest().get(suggesterName).get(0).term().options()); + assertEquals(response.suggest().get(suggesterName).get(0).term().options().get(0) + .text(), "foo"); + } + + @Test + public void testPhraseSuggester() throws IOException { + + String index = "test-phrase-suggester"; + + ShingleTokenFilter shingleTokenFilter = new ShingleTokenFilter.Builder().minShingleSize("2") + .maxShingleSize("3") + .build(); + + Analyzer analyzer = new Analyzer.Builder() + .custom(new CustomAnalyzer.Builder().tokenizer("standard") + .filter(Arrays.asList("lowercase","shingle")).build()) + .build(); + + TokenFilter tokenFilter = new TokenFilter.Builder() + .definition(new TokenFilterDefinition.Builder() + .shingle(shingleTokenFilter).build()) + .build(); + + IndexSettingsAnalysis indexSettingsAnalysis = new IndexSettingsAnalysis.Builder() + .analyzer("trigram", analyzer) + .filter("shingle", tokenFilter) + .build(); + + IndexSettings settings = new IndexSettings.Builder().analysis(indexSettingsAnalysis).build(); + + TypeMapping mapping = new TypeMapping.Builder().properties("msg", new Property.Builder() + .text(new TextProperty.Builder().fields("trigram", new Property.Builder() + .text(new TextProperty.Builder().analyzer("trigram").build()) + .build()).build()) + .build()).build(); + + + javaClient().indices().create(c -> c + .index(index) + .settings(settings) + .mappings(mapping)); + + AppData appData = new AppData(); + appData.setIntValue(1337); + appData.setMsg("Design Patterns"); + + javaClient().index(b -> b + .index(index) + .id("1") + .document(appData) + .refresh(Refresh.True)); + + appData.setIntValue(1338); + appData.setMsg("Software Architecture Patterns Explained"); + + javaClient().index(b -> b + .index(index) + .id("2") + .document(appData) + .refresh(Refresh.True)); + + String suggesterName = "msgSuggester"; + + PhraseSuggester phraseSuggester = FieldSuggesterBuilders.phrase() + .field("msg.trigram") + .build(); + FieldSuggester fieldSuggester = new FieldSuggester.Builder().text("design paterns") + .phrase(phraseSuggester) + .build(); + Suggester suggester = new Suggester.Builder() + .suggesters(Collections.singletonMap(suggesterName, fieldSuggester)) + .build(); + SearchRequest searchRequest = new SearchRequest.Builder() + .index(index) + .suggest(suggester) + .build(); + + SearchResponse response = javaClient().search(searchRequest, AppData.class); + assertTrue(response.suggest().size() > 0); + assertTrue(response.suggest().keySet().contains(suggesterName)); + assertNotNull(response.suggest().get(suggesterName)); + assertNotNull(response.suggest().get(suggesterName).get(0)); + assertTrue(response.suggest().get(suggesterName).get(0).isPhrase()); + assertNotNull(response.suggest().get(suggesterName).get(0).phrase().options()); + assertEquals(response.suggest().get(suggesterName).get(0).phrase().options().get(0) + .text(), "design patterns"); + } + // @Test // public void testValueBodyResponse() throws Exception { // DiskUsageResponse resp = highLevelClient().indices().diskUsage(b -> b diff --git a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractSearchTemplateRequestIT.java b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractSearchTemplateRequestIT.java index 4a4f82a62f..da8f1007e7 100644 --- a/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractSearchTemplateRequestIT.java +++ b/java-client/src/test/java/org/opensearch/client/opensearch/integTest/AbstractSearchTemplateRequestIT.java @@ -58,12 +58,12 @@ public void testTemplateSearchSuggest() throws Exception { assertEquals(0, searchResponse.aggregations().size()); // intentional typo - searchResponse = sendTemplateRequest(index, "Docunent", true, false); + searchResponse = sendTemplateRequest(index, "Docuent", true, false); assertEquals(0, searchResponse.hits().hits().size()); assertEquals(1, searchResponse.suggest().size()); - var options = searchResponse.suggest().get("term#test-suggest").get(0).options(); + var options = searchResponse.suggest().get("test-suggest").get(0).term().options(); assertEquals(1, options.size()); - assertEquals("document", options.get(0).term().text()); + assertEquals("document", options.get(0).text()); assertEquals(0, searchResponse.aggregations().size()); }