diff options
author | Christoph Büscher <christoph@elastic.co> | 2017-02-20 15:45:10 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-20 15:45:10 +0100 |
commit | ea7deace5dd3c293dadb508979511c28d785588d (patch) | |
tree | 38f93d28c765169014cb6e458edd4bf00d157e3d | |
parent | ea9d51114c6da056fb3b1bae6a6ece4d29edf922 (diff) |
Adding fromXContent to Suggest and Suggestion class (#23226)
A follow up to #23202, this adds parsing from xContent and tests to the four Suggestion implementations
and the top level suggest element to be used later when parsing the entire SearchResponse.
7 files changed, 388 insertions, 7 deletions
diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java index 968ad8ac6b..0c6cb1c5cd 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentHelper.java @@ -448,12 +448,20 @@ public class XContentHelper { * {@link XContentType}. Wraps the output into a new anonymous object. */ public static BytesReference toXContent(ToXContent toXContent, XContentType xContentType, boolean humanReadable) throws IOException { + return toXContent(toXContent, xContentType, ToXContent.EMPTY_PARAMS, humanReadable); + } + + /** + * Returns the bytes that represent the XContent output of the provided {@link ToXContent} object, using the provided + * {@link XContentType}. Wraps the output into a new anonymous object. + */ + public static BytesReference toXContent(ToXContent toXContent, XContentType xContentType, Params params, boolean humanReadable) throws IOException { try (XContentBuilder builder = XContentBuilder.builder(xContentType.xContent())) { builder.humanReadable(humanReadable); if (toXContent.isFragment()) { builder.startObject(); } - toXContent.toXContent(builder, ToXContent.EMPTY_PARAMS); + toXContent.toXContent(builder, params); if (toXContent.isFragment()) { builder.endObject(); } diff --git a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java index 36a780fec3..ba5ad712f4 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.suggest; import org.apache.lucene.util.CollectionUtil; import org.elasticsearch.common.ParseField; +import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.io.stream.Streamable; @@ -47,17 +48,19 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constructorArg; import static org.elasticsearch.common.xcontent.ConstructingObjectParser.optionalConstructorArg; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; /** * Top level suggest result, containing the result for each suggestion. */ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? extends Option>>>, Streamable, ToXContent { - private static final String NAME = "suggest"; + static final String NAME = "suggest"; public static final Comparator<Option> COMPARATOR = (first, second) -> { int cmp = Float.compare(second.getScore(), first.getScore()); @@ -72,7 +75,7 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex private Map<String, Suggestion<? extends Entry<? extends Option>>> suggestMap; - public Suggest() { + private Suggest() { this(Collections.emptyList()); } @@ -167,6 +170,18 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex return builder; } + /** + * this parsing method assumes that the leading "suggest" field name has already been parsed by the caller + */ + public static Suggest fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.currentToken(), parser::getTokenLocation); + List<Suggestion<? extends Entry<? extends Option>>> suggestions = new ArrayList<>(); + while ((parser.nextToken()) != XContentParser.Token.END_OBJECT) { + suggestions.add(Suggestion.fromXContent(parser)); + } + return new Suggest(suggestions); + } + public static Suggest readSuggest(StreamInput in) throws IOException { Suggest result = new Suggest(); result.readFrom(in); @@ -216,7 +231,7 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex protected int size; protected final List<T> entries = new ArrayList<>(5); - public Suggestion() { + protected Suggestion() { } public Suggestion(String name, int size) { @@ -369,6 +384,52 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex return builder; } + public static Suggestion<? extends Entry<? extends Option>> fromXContent(XContentParser parser) throws IOException { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); + String typeAndName = parser.currentName(); + // we need to extract the type prefix from the name and throw error if it is not present + int delimiterPos = typeAndName.indexOf(InternalAggregation.TYPED_KEYS_DELIMITER); + String type = null; + String name = null; + if (delimiterPos > 0) { + type = typeAndName.substring(0, delimiterPos); + name = typeAndName.substring(delimiterPos + 1); + } else { + throw new ParsingException(parser.getTokenLocation(), + "Cannot parse suggestion response without type information. Set [" + RestSearchAction.TYPED_KEYS_PARAM + + "] parameter on the request to ensure the type information is added to the response output"); + } + Suggestion suggestion = null; + Function<XContentParser, Entry> entryParser = null; + // the "size" parameter and the SortBy for TermSuggestion cannot be parsed from the response, use default values + // TODO investigate if we can use NamedXContentRegistry instead of this switch + switch (type) { + case Suggestion.NAME: + suggestion = new Suggestion(name, -1); + entryParser = Suggestion.Entry::fromXContent; + break; + case PhraseSuggestion.NAME: + suggestion = new PhraseSuggestion(name, -1); + entryParser = PhraseSuggestion.Entry::fromXContent; + break; + case TermSuggestion.NAME: + suggestion = new TermSuggestion(name, -1, SortBy.SCORE); + entryParser = TermSuggestion.Entry::fromXContent; + break; + case CompletionSuggestion.NAME: + suggestion = new CompletionSuggestion(name, -1); + entryParser = CompletionSuggestion.Entry::fromXContent; + break; + default: + throw new ParsingException(parser.getTokenLocation(), "Unknown suggestion type [{}]", type); + } + ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.nextToken(), parser::getTokenLocation); + while ((parser.nextToken()) != XContentParser.Token.END_ARRAY) { + suggestion.addTerm(entryParser.apply(parser)); + } + return suggestion; + } + /** * Represents a part from the suggest text with suggested options. */ diff --git a/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java b/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java index 51b44a300d..ed45104d99 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java @@ -64,7 +64,7 @@ import static org.elasticsearch.search.suggest.Suggest.COMPARATOR; */ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSuggestion.Entry> { - private static final String NAME = "completion"; + public static final String NAME = "completion"; public static final int TYPE = 4; diff --git a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestion.java b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestion.java index 3ea0d61d72..fb646b03ae 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestion.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestion.java @@ -35,7 +35,7 @@ import java.io.IOException; */ public class PhraseSuggestion extends Suggest.Suggestion<PhraseSuggestion.Entry> { - private static final String NAME = "phrase"; + public static final String NAME = "phrase"; public static final int TYPE = 3; public PhraseSuggestion() { diff --git a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestion.java b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestion.java index 5f6cd310ad..31621fb63b 100644 --- a/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestion.java +++ b/core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestion.java @@ -41,7 +41,7 @@ import static org.elasticsearch.common.xcontent.ConstructingObjectParser.constru */ public class TermSuggestion extends Suggestion<TermSuggestion.Entry> { - private static final String NAME = "term"; + public static final String NAME = "term"; public static final Comparator<Suggestion.Entry.Option> SCORE = new Score(); public static final Comparator<Suggestion.Entry.Option> FREQUENCY = new Frequency(); diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestTests.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestTests.java index bed6d5997d..0948b9e2ff 100644 --- a/core/src/test/java/org/elasticsearch/search/suggest/SuggestTests.java +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestTests.java @@ -19,20 +19,92 @@ package org.elasticsearch.search.suggest; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.suggest.Suggest.Suggestion; +import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry; +import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option; import org.elasticsearch.search.suggest.completion.CompletionSuggestion; import org.elasticsearch.search.suggest.phrase.PhraseSuggestion; import org.elasticsearch.search.suggest.term.TermSuggestion; import org.elasticsearch.test.ESTestCase; +import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureFieldName; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; import static org.hamcrest.Matchers.equalTo; public class SuggestTests extends ESTestCase { + public static Suggest createTestItem() { + int numEntries = randomIntBetween(0, 5); + List<Suggestion<? extends Entry<? extends Option>>> suggestions = new ArrayList<>(); + for (int i = 0; i < numEntries; i++) { + suggestions.add(SuggestionTests.createTestItem()); + } + return new Suggest(suggestions); + } + + public void testFromXContent() throws IOException { + ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + Suggest suggest = createTestItem(); + XContentType xContentType = randomFrom(XContentType.values()); + boolean humanReadable = randomBoolean(); + BytesReference originalBytes = toXContent(suggest, xContentType, params, humanReadable); + Suggest parsed; + try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureFieldName(parser, parser.nextToken(), Suggest.NAME); + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + parsed = Suggest.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken()); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + assertEquals(suggest.size(), parsed.size()); + for (Suggestion suggestion : suggest) { + Suggestion<? extends Entry<? extends Option>> parsedSuggestion = parsed.getSuggestion(suggestion.getName()); + assertNotNull(parsedSuggestion); + assertEquals(suggestion.getClass(), parsedSuggestion.getClass()); + } + assertToXContentEquivalent(originalBytes, toXContent(parsed, xContentType, params, humanReadable), xContentType); + } + + public void testToXContent() throws IOException { + Option option = new Option(new Text("someText"), new Text("somethingHighlighted"), 1.3f, true); + Entry<Option> entry = new Entry<>(new Text("entryText"), 42, 313); + entry.addOption(option); + Suggestion<Entry<Option>> suggestion = new Suggestion<>("suggestionName", 5); + suggestion.addTerm(entry); + Suggest suggest = new Suggest(Collections.singletonList(suggestion)); + BytesReference xContent = toXContent(suggest, XContentType.JSON, randomBoolean()); + assertEquals( + "{\"suggest\":" + + "{\"suggestionName\":" + + "[{\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{\"text\":\"someText\"," + + "\"highlighted\":\"somethingHighlighted\"," + + "\"score\":1.3," + + "\"collate_match\":true}]" + + "}]" + + "}" + +"}", + xContent.utf8ToString()); + } + public void testFilter() throws Exception { List<Suggest.Suggestion<? extends Suggest.Suggestion.Entry<? extends Suggest.Suggestion.Entry.Option>>> suggestions; CompletionSuggestion completionSuggestion = new CompletionSuggestion(randomAsciiOfLength(10), 2); diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java new file mode 100644 index 0000000000..e17ead287a --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionTests.java @@ -0,0 +1,240 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch 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. + */ + +package org.elasticsearch.search.suggest; + +import org.elasticsearch.common.ParsingException; +import org.elasticsearch.common.bytes.BytesReference; +import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.xcontent.ToXContent; +import org.elasticsearch.common.xcontent.XContent; +import org.elasticsearch.common.xcontent.XContentParser; +import org.elasticsearch.common.xcontent.XContentType; +import org.elasticsearch.common.xcontent.json.JsonXContent; +import org.elasticsearch.rest.action.search.RestSearchAction; +import org.elasticsearch.search.suggest.Suggest.Suggestion; +import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry; +import org.elasticsearch.search.suggest.Suggest.Suggestion.Entry.Option; +import org.elasticsearch.search.suggest.completion.CompletionSuggestion; +import org.elasticsearch.search.suggest.phrase.PhraseSuggestion; +import org.elasticsearch.search.suggest.term.TermSuggestion; +import org.elasticsearch.test.ESTestCase; + +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; +import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertToXContentEquivalent; + +public class SuggestionTests extends ESTestCase { + + @SuppressWarnings({ "unchecked" }) + private static final Class<Suggestion<? extends Entry<? extends Option>>>[] SUGGESTION_TYPES = new Class[] { + Suggestion.class, TermSuggestion.class, PhraseSuggestion.class, CompletionSuggestion.class + }; + + public static Suggestion<? extends Entry<? extends Option>> createTestItem() { + return createTestItem(randomFrom(SUGGESTION_TYPES)); + } + + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static Suggestion<? extends Entry<? extends Option>> createTestItem(Class<? extends Suggestion> type) { + String name = randomAsciiOfLengthBetween(5, 10); + // note: size will not be rendered via "toXContent", only passed on internally on transport layer + int size = randomInt(); + Supplier<Entry> entrySupplier = null; + Suggestion suggestion = null; + if (type == Suggestion.class) { + suggestion = new Suggestion(name, size); + entrySupplier = () -> SuggestionEntryTests.createTestItem(Entry.class); + } else if (type == TermSuggestion.class) { + suggestion = new TermSuggestion(name, size, randomFrom(SortBy.values())); + entrySupplier = () -> SuggestionEntryTests.createTestItem(TermSuggestion.Entry.class); + } else if (type == PhraseSuggestion.class) { + suggestion = new PhraseSuggestion(name, size); + entrySupplier = () -> SuggestionEntryTests.createTestItem(PhraseSuggestion.Entry.class); + } else if (type == CompletionSuggestion.class) { + suggestion = new CompletionSuggestion(name, size); + entrySupplier = () -> SuggestionEntryTests.createTestItem(CompletionSuggestion.Entry.class); + } + int numEntries; + if (frequently()) { + if (type == CompletionSuggestion.class) { + numEntries = 1; // CompletionSuggestion can have max. one entry + } else { + numEntries = randomIntBetween(1, 5); + } + } else { + numEntries = 0; // also occasionally test zero entries + } + for (int i = 0; i < numEntries; i++) { + suggestion.addTerm(entrySupplier.get()); + } + return suggestion; + } + + @SuppressWarnings({ "rawtypes" }) + public void testFromXContent() throws IOException { + ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + for (Class<Suggestion<? extends Entry<? extends Option>>> type : SUGGESTION_TYPES) { + Suggestion suggestion = createTestItem(type); + XContentType xContentType = randomFrom(XContentType.values()); + boolean humanReadable = randomBoolean(); + BytesReference originalBytes = toXContent(suggestion, xContentType, params, humanReadable); + Suggestion parsed; + try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + parsed = Suggestion.fromXContent(parser); + assertEquals(XContentParser.Token.END_OBJECT, parser.nextToken()); + assertNull(parser.nextToken()); + } + assertEquals(suggestion.getName(), parsed.getName()); + assertEquals(suggestion.getEntries().size(), parsed.getEntries().size()); + // We don't parse size via xContent, instead we set it to -1 on the client side + assertEquals(-1, parsed.getSize()); + assertToXContentEquivalent(originalBytes, toXContent(parsed, xContentType, params, humanReadable), xContentType); + } + } + + /** + * test that we throw error if RestSearchAction.TYPED_KEYS_PARAM isn't set while rendering xContent + */ + public void testFromXContentFailsWithoutTypeParam() throws IOException { + XContentType xContentType = randomFrom(XContentType.values()); + BytesReference originalBytes = toXContent(createTestItem(), xContentType, ToXContent.EMPTY_PARAMS, randomBoolean()); + try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + ParsingException e = expectThrows(ParsingException.class, () -> Suggestion.fromXContent(parser)); + assertEquals( + "Cannot parse suggestion response without type information. " + + "Set [typed_keys] parameter on the request to ensure the type information " + + "is added to the response output", e.getMessage()); + } + } + + public void testUnknownSuggestionTypeThrows() throws IOException { + XContent xContent = JsonXContent.jsonXContent; + String suggestionString = + "{\"unknownType#suggestionName\":" + + "[{\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{\"text\":\"someText\"," + + "\"highlighted\":\"somethingHighlighted\"," + + "\"score\":1.3," + + "\"collate_match\":true}]" + + "}]" + + "}"; + try (XContentParser parser = xContent.createParser(xContentRegistry(), suggestionString)) { + ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation); + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.nextToken(), parser::getTokenLocation); + ParsingException e = expectThrows(ParsingException.class, () -> Suggestion.fromXContent(parser)); + assertEquals("Unknown suggestion type [unknownType]", e.getMessage()); + } + } + + public void testToXContent() throws IOException { + ToXContent.Params params = new ToXContent.MapParams(Collections.singletonMap(RestSearchAction.TYPED_KEYS_PARAM, "true")); + { + Option option = new Option(new Text("someText"), new Text("somethingHighlighted"), 1.3f, true); + Entry<Option> entry = new Entry<>(new Text("entryText"), 42, 313); + entry.addOption(option); + Suggestion<Entry<Option>> suggestion = new Suggestion<>("suggestionName", 5); + suggestion.addTerm(entry); + BytesReference xContent = toXContent(suggestion, XContentType.JSON, params, randomBoolean()); + assertEquals( + "{\"suggestion#suggestionName\":[{" + + "\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{" + + "\"text\":\"someText\"," + + "\"highlighted\":\"somethingHighlighted\"," + + "\"score\":1.3," + + "\"collate_match\":true}]" + + "}]" + + "}", xContent.utf8ToString()); + } + { + Option option = new Option(new Text("someText"), new Text("somethingHighlighted"), 1.3f, true); + PhraseSuggestion.Entry entry = new PhraseSuggestion.Entry(new Text("entryText"), 42, 313, 1.0); + entry.addOption(option); + PhraseSuggestion suggestion = new PhraseSuggestion("suggestionName", 5); + suggestion.addTerm(entry); + BytesReference xContent = toXContent(suggestion, XContentType.JSON, params, randomBoolean()); + assertEquals( + "{\"phrase#suggestionName\":[{" + + "\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{" + + "\"text\":\"someText\"," + + "\"highlighted\":\"somethingHighlighted\"," + + "\"score\":1.3," + + "\"collate_match\":true}]" + + "}]" + + "}", xContent.utf8ToString()); + } + { + TermSuggestion.Entry.Option option = new TermSuggestion.Entry.Option(new Text("someText"), 10, 1.3f); + TermSuggestion.Entry entry = new TermSuggestion.Entry(new Text("entryText"), 42, 313); + entry.addOption(option); + TermSuggestion suggestion = new TermSuggestion("suggestionName", 5, SortBy.SCORE); + suggestion.addTerm(entry); + BytesReference xContent = toXContent(suggestion, XContentType.JSON, params, randomBoolean()); + assertEquals( + "{\"term#suggestionName\":[{" + + "\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{" + + "\"text\":\"someText\"," + + "\"score\":1.3," + + "\"freq\":10}]" + + "}]" + + "}", xContent.utf8ToString()); + } + { + Map<String, Set<CharSequence>> contexts = Collections.singletonMap("key", Collections.singleton("value")); + CompletionSuggestion.Entry.Option option = new CompletionSuggestion.Entry.Option(1, new Text("someText"), 1.3f, contexts); + CompletionSuggestion.Entry entry = new CompletionSuggestion.Entry(new Text("entryText"), 42, 313); + entry.addOption(option); + CompletionSuggestion suggestion = new CompletionSuggestion("suggestionName", 5); + suggestion.addTerm(entry); + BytesReference xContent = toXContent(suggestion, XContentType.JSON, params, randomBoolean()); + assertEquals( + "{\"completion#suggestionName\":[{" + + "\"text\":\"entryText\"," + + "\"offset\":42," + + "\"length\":313," + + "\"options\":[{" + + "\"text\":\"someText\"," + + "\"score\":1.3," + + "\"contexts\":{\"key\":[\"value\"]}" + + "}]" + + "}]}", xContent.utf8ToString()); + } + } +}
\ No newline at end of file |