summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorChristoph Büscher <christoph@elastic.co>2017-02-16 17:59:55 +0100
committerGitHub <noreply@github.com>2017-02-16 17:59:55 +0100
commit268d15ec4cdbc85330ee4f28ca6974ff27326de9 (patch)
treeb01959feb5e22ee5e55d3fcdce37e2274e294655 /core/src
parent76675229c7eb64ee2dbd41ec4a8258d74a3f1d03 (diff)
Adding fromXContent to Suggestion.Entry and subclasses (#23202)
This adds parsing from xContent to Suggestion.Entry and its subclasses for Terms-, Phrase- and CompletionSuggestion.Entry.
Diffstat (limited to 'core/src')
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/Suggest.java51
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java15
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/phrase/PhraseSuggestion.java16
-rw-r--r--core/src/main/java/org/elasticsearch/search/suggest/term/TermSuggestion.java14
-rw-r--r--core/src/test/java/org/elasticsearch/search/suggest/SuggestionEntryTests.java162
5 files changed, 238 insertions, 20 deletions
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 f1875564f8..36a780fec3 100644
--- a/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java
+++ b/core/src/main/java/org/elasticsearch/search/suggest/Suggest.java
@@ -25,6 +25,7 @@ import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.io.stream.Streamable;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.ToXContentObject;
import org.elasticsearch.common.xcontent.XContentBuilder;
@@ -371,37 +372,38 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex
/**
* Represents a part from the suggest text with suggested options.
*/
- public static class Entry<O extends Entry.Option> implements Iterable<O>, Streamable, ToXContent {
+ public static class Entry<O extends Entry.Option> implements Iterable<O>, Streamable, ToXContentObject {
- static class Fields {
-
- static final String TEXT = "text";
- static final String OFFSET = "offset";
- static final String LENGTH = "length";
- static final String OPTIONS = "options";
-
- }
+ private static final String TEXT = "text";
+ private static final String OFFSET = "offset";
+ private static final String LENGTH = "length";
+ protected static final String OPTIONS = "options";
protected Text text;
protected int offset;
protected int length;
- protected List<O> options;
+ protected List<O> options = new ArrayList<>(5);
public Entry(Text text, int offset, int length) {
this.text = text;
this.offset = offset;
this.length = length;
- this.options = new ArrayList<>(5);
}
- public Entry() {
+ protected Entry() {
}
public void addOption(O option) {
options.add(option);
}
+ protected void addOptions(List<O> options) {
+ for (O option : options) {
+ addOption(option);
+ }
+ }
+
protected void sort(Comparator<O> comparator) {
CollectionUtil.timSort(options, comparator);
}
@@ -539,10 +541,10 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex
@Override
public XContentBuilder toXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject();
- builder.field(Fields.TEXT, text);
- builder.field(Fields.OFFSET, offset);
- builder.field(Fields.LENGTH, length);
- builder.startArray(Fields.OPTIONS);
+ builder.field(TEXT, text);
+ builder.field(OFFSET, offset);
+ builder.field(LENGTH, length);
+ builder.startArray(OPTIONS);
for (Option option : options) {
option.toXContent(builder, params);
}
@@ -551,6 +553,23 @@ public class Suggest implements Iterable<Suggest.Suggestion<? extends Entry<? ex
return builder;
}
+ private static ObjectParser<Entry<Option>, Void> PARSER = new ObjectParser<>("SuggestionEntryParser", true, Entry::new);
+
+ static {
+ declareCommonFields(PARSER);
+ PARSER.declareObjectArray(Entry::addOptions, (p,c) -> Option.fromXContent(p), new ParseField(OPTIONS));
+ }
+
+ protected static void declareCommonFields(ObjectParser<? extends Entry<? extends Option>, Void> parser) {
+ parser.declareString((entry, text) -> entry.text = new Text(text), new ParseField(TEXT));
+ parser.declareInt((entry, offset) -> entry.offset = offset, new ParseField(OFFSET));
+ parser.declareInt((entry, length) -> entry.length = length, new ParseField(LENGTH));
+ }
+
+ public static Entry<? extends Option> fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
+
/**
* Contains the suggested text with its document frequency and score.
*/
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 33ff15fbbb..51b44a300d 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
@@ -194,8 +194,7 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
super(text, offset, length);
}
- protected Entry() {
- super();
+ Entry() {
}
@Override
@@ -203,6 +202,18 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
return new Option();
}
+ private static ObjectParser<Entry, Void> PARSER = new ObjectParser<>("CompletionSuggestionEntryParser", true,
+ Entry::new);
+
+ static {
+ declareCommonFields(PARSER);
+ PARSER.declareObjectArray(Entry::addOptions, (p,c) -> Option.fromXContent(p), new ParseField(OPTIONS));
+ }
+
+ public static Entry fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
+
public static class Option extends Suggest.Suggestion.Entry.Option {
private Map<String, Set<CharSequence>> contexts = Collections.emptyMap();
private ScoreDoc doc;
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 e673ccb128..3ea0d61d72 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
@@ -19,9 +19,12 @@
package org.elasticsearch.search.suggest.phrase;
+import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.text.Text;
+import org.elasticsearch.common.xcontent.ObjectParser;
+import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.suggest.Suggest;
import org.elasticsearch.search.suggest.Suggest.Suggestion;
@@ -69,7 +72,7 @@ public class PhraseSuggestion extends Suggest.Suggestion<PhraseSuggestion.Entry>
this.cutoffScore = cutoffScore;
}
- public Entry() {
+ Entry() {
}
/**
@@ -100,6 +103,17 @@ public class PhraseSuggestion extends Suggest.Suggestion<PhraseSuggestion.Entry>
}
}
+ private static ObjectParser<Entry, Void> PARSER = new ObjectParser<>("PhraseSuggestionEntryParser", true, Entry::new);
+
+ static {
+ declareCommonFields(PARSER);
+ PARSER.declareObjectArray(Entry::addOptions, (p,c) -> Option.fromXContent(p), new ParseField(OPTIONS));
+ }
+
+ public static Entry fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
+
@Override
public void readFrom(StreamInput in) throws IOException {
super.readFrom(in);
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 68aed8b80a..5f6cd310ad 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
@@ -24,6 +24,7 @@ import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.ConstructingObjectParser;
+import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.search.suggest.SortBy;
@@ -142,7 +143,7 @@ public class TermSuggestion extends Suggestion<TermSuggestion.Entry> {
public static class Entry extends
org.elasticsearch.search.suggest.Suggest.Suggestion.Entry<TermSuggestion.Entry.Option> {
- Entry(Text text, int offset, int length) {
+ public Entry(Text text, int offset, int length) {
super(text, offset, length);
}
@@ -154,6 +155,17 @@ public class TermSuggestion extends Suggestion<TermSuggestion.Entry> {
return new Option();
}
+ private static ObjectParser<Entry, Void> PARSER = new ObjectParser<>("TermSuggestionEntryParser", true, Entry::new);
+
+ static {
+ declareCommonFields(PARSER);
+ PARSER.declareObjectArray(Entry::addOptions, (p,c) -> Option.fromXContent(p), new ParseField(OPTIONS));
+ }
+
+ public static Entry fromXContent(XContentParser parser) {
+ return PARSER.apply(parser, null);
+ }
+
/**
* Contains the suggested text with its document frequency and score.
*/
diff --git a/core/src/test/java/org/elasticsearch/search/suggest/SuggestionEntryTests.java b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionEntryTests.java
new file mode 100644
index 0000000000..f1bf602ae6
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/search/suggest/SuggestionEntryTests.java
@@ -0,0 +1,162 @@
+/*
+ * 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.bytes.BytesReference;
+import org.elasticsearch.common.text.Text;
+import org.elasticsearch.common.xcontent.XContentParser;
+import org.elasticsearch.common.xcontent.XContentType;
+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.HashMap;
+import java.util.Map;
+import java.util.function.Function;
+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 SuggestionEntryTests extends ESTestCase {
+
+ @SuppressWarnings("rawtypes")
+ private static final Map<Class<? extends Entry>, Function<XContentParser, ? extends Entry>> ENTRY_PARSERS = new HashMap<>();
+ static {
+ ENTRY_PARSERS.put(Suggestion.Entry.class, Suggestion.Entry::fromXContent);
+ ENTRY_PARSERS.put(TermSuggestion.Entry.class, TermSuggestion.Entry::fromXContent);
+ ENTRY_PARSERS.put(PhraseSuggestion.Entry.class, PhraseSuggestion.Entry::fromXContent);
+ ENTRY_PARSERS.put(CompletionSuggestion.Entry.class, CompletionSuggestion.Entry::fromXContent);
+ }
+
+ /**
+ * Create a randomized Suggestion.Entry
+ */
+ @SuppressWarnings("unchecked")
+ public static <O extends Option> Entry<O> createTestItem(Class<? extends Entry> entryType) {
+ Text entryText = new Text(randomAsciiOfLengthBetween(5, 15));
+ int offset = randomInt();
+ int length = randomInt();
+ @SuppressWarnings("rawtypes")
+ Entry entry = null;
+ Supplier<Option> supplier = null;
+ if (entryType == Suggestion.Entry.class) {
+ entry = new Suggestion.Entry<>(entryText, offset, length);
+ supplier = SuggestionOptionTests::createTestItem;
+ } else if (entryType == TermSuggestion.Entry.class) {
+ entry = new TermSuggestion.Entry(entryText, offset, length);
+ supplier = TermSuggestionOptionTests::createTestItem;
+ } else if (entryType == PhraseSuggestion.Entry.class) {
+ entry = new PhraseSuggestion.Entry(entryText, offset, length, randomDouble());
+ supplier = SuggestionOptionTests::createTestItem;
+ } else if (entryType == CompletionSuggestion.Entry.class) {
+ entry = new CompletionSuggestion.Entry(entryText, offset, length);
+ supplier = CompletionSuggestionOptionTests::createTestItem;
+ }
+ int numOptions = randomIntBetween(0, 5);
+ for (int i = 0; i < numOptions; i++) {
+ entry.addOption(supplier.get());
+ }
+ return entry;
+ }
+
+ @SuppressWarnings("unchecked")
+ public void testFromXContent() throws IOException {
+ for (Class<? extends Entry> entryType : ENTRY_PARSERS.keySet()) {
+ Entry<Option> entry = createTestItem(entryType);
+ XContentType xContentType = randomFrom(XContentType.values());
+ boolean humanReadable = randomBoolean();
+ BytesReference originalBytes = toXContent(entry, xContentType, humanReadable);
+ Entry<Option> parsed;
+ try (XContentParser parser = createParser(xContentType.xContent(), originalBytes)) {
+ ensureExpectedToken(XContentParser.Token.START_OBJECT, parser.nextToken(), parser::getTokenLocation);
+ parsed = ENTRY_PARSERS.get(entry.getClass()).apply(parser);
+ assertEquals(XContentParser.Token.END_OBJECT, parser.currentToken());
+ assertNull(parser.nextToken());
+ }
+ assertEquals(entry.getClass(), parsed.getClass());
+ assertEquals(entry.getText(), parsed.getText());
+ assertEquals(entry.getLength(), parsed.getLength());
+ assertEquals(entry.getOffset(), parsed.getOffset());
+ assertEquals(entry.getOptions().size(), parsed.getOptions().size());
+ for (int i = 0; i < entry.getOptions().size(); i++) {
+ assertEquals(entry.getOptions().get(i).getClass(), parsed.getOptions().get(i).getClass());
+ }
+ assertToXContentEquivalent(originalBytes, toXContent(parsed, xContentType, 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);
+ BytesReference xContent = toXContent(entry, XContentType.JSON, randomBoolean());
+ assertEquals(
+ "{\"text\":\"entryText\","
+ + "\"offset\":42,"
+ + "\"length\":313,"
+ + "\"options\":["
+ + "{\"text\":\"someText\","
+ + "\"highlighted\":\"somethingHighlighted\","
+ + "\"score\":1.3,"
+ + "\"collate_match\":true}"
+ + "]}", xContent.utf8ToString());
+
+ org.elasticsearch.search.suggest.term.TermSuggestion.Entry.Option termOption =
+ new org.elasticsearch.search.suggest.term.TermSuggestion.Entry.Option(new Text("termSuggestOption"), 42, 3.13f);
+ entry = new Entry<>(new Text("entryText"), 42, 313);
+ entry.addOption(termOption);
+ xContent = toXContent(entry, XContentType.JSON, randomBoolean());
+ assertEquals(
+ "{\"text\":\"entryText\","
+ + "\"offset\":42,"
+ + "\"length\":313,"
+ + "\"options\":["
+ + "{\"text\":\"termSuggestOption\","
+ + "\"score\":3.13,"
+ + "\"freq\":42}"
+ + "]}", xContent.utf8ToString());
+
+ org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option completionOption =
+ new org.elasticsearch.search.suggest.completion.CompletionSuggestion.Entry.Option(-1, new Text("completionOption"),
+ 3.13f, Collections.singletonMap("key", Collections.singleton("value")));
+ entry = new Entry<>(new Text("entryText"), 42, 313);
+ entry.addOption(completionOption);
+ xContent = toXContent(entry, XContentType.JSON, randomBoolean());
+ assertEquals(
+ "{\"text\":\"entryText\","
+ + "\"offset\":42,"
+ + "\"length\":313,"
+ + "\"options\":["
+ + "{\"text\":\"completionOption\","
+ + "\"score\":3.13,"
+ + "\"contexts\":{\"key\":[\"value\"]}"
+ + "}"
+ + "]}", xContent.utf8ToString());
+ }
+
+}