diff options
author | Christoph Büscher <christoph@elastic.co> | 2017-02-15 16:52:17 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2017-02-15 16:52:17 +0100 |
commit | b9631442542e15784d1db8f7f88bf4e02179c8a9 (patch) | |
tree | 6406c8a220b3a04accee010d819219b72cad95bd /core/src/main/java/org/elasticsearch/search/suggest/completion | |
parent | 9316e8e8fe6d6fcb9f3d60ea64cd9b18aa4085c5 (diff) |
Add xcontent parsing to completion suggestion option (#23071)
This adds parsing from xContent to the CompletionSuggestion.Entry.Option.
The completion suggestion option also inlines the xContent rendering of the
containes SearchHit, so in order to reuse the SearchHit parser this also changes
the way SearchHit is parsed from using a loop-based parser to using a
ConstructingObjectParser that creates an intermediate map representation and
then later uses this output to create either a single SearchHit or use it with
additional fields defined in the parser for the completion suggestion option.
Diffstat (limited to 'core/src/main/java/org/elasticsearch/search/suggest/completion')
-rw-r--r-- | core/src/main/java/org/elasticsearch/search/suggest/completion/CompletionSuggestion.java | 72 |
1 files changed, 66 insertions, 6 deletions
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 e33e421f77..33ff15fbbb 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 @@ -20,23 +20,30 @@ package org.elasticsearch.search.suggest.completion; import org.apache.lucene.search.ScoreDoc; import org.apache.lucene.search.suggest.Lookup; +import org.elasticsearch.common.ParseField; import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamOutput; import org.elasticsearch.common.lucene.Lucene; import org.elasticsearch.common.text.Text; +import org.elasticsearch.common.xcontent.ObjectParser; import org.elasticsearch.common.xcontent.XContentBuilder; +import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.search.SearchHit; import org.elasticsearch.search.suggest.Suggest; +import org.elasticsearch.search.suggest.Suggest.Suggestion; import java.io.IOException; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; +import static org.elasticsearch.common.xcontent.XContentParserUtils.ensureExpectedToken; import static org.elasticsearch.search.suggest.Suggest.COMPARATOR; /** @@ -197,14 +204,16 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug } public static class Option extends Suggest.Suggestion.Entry.Option { - private Map<String, Set<CharSequence>> contexts; + private Map<String, Set<CharSequence>> contexts = Collections.emptyMap(); private ScoreDoc doc; private SearchHit hit; + public static final ParseField CONTEXTS = new ParseField("contexts"); + public Option(int docID, Text text, float score, Map<String, Set<CharSequence>> contexts) { super(text, score); this.doc = new ScoreDoc(docID, score); - this.contexts = contexts; + this.contexts = Objects.requireNonNull(contexts, "context map cannot be null"); } protected Option() { @@ -240,14 +249,14 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug @Override protected XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { - builder.field("text", getText()); + builder.field(TEXT.getPreferredName(), getText()); if (hit != null) { hit.toInnerXContent(builder, params); } else { - builder.field("score", getScore()); + builder.field(SCORE.getPreferredName(), getScore()); } if (contexts.size() > 0) { - builder.startObject("contexts"); + builder.startObject(CONTEXTS.getPreferredName()); for (Map.Entry<String, Set<CharSequence>> entry : contexts.entrySet()) { builder.startArray(entry.getKey()); for (CharSequence context : entry.getValue()) { @@ -260,6 +269,58 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug return builder; } + private static ObjectParser<Map<String, Object>, Void> PARSER = new ObjectParser<>("CompletionOptionParser", + true, HashMap::new); + + static { + SearchHit.declareInnerHitsParseFields(PARSER); + PARSER.declareString((map, value) -> map.put(Suggestion.Entry.Option.TEXT.getPreferredName(), value), + Suggestion.Entry.Option.TEXT); + PARSER.declareFloat((map, value) -> map.put(Suggestion.Entry.Option.SCORE.getPreferredName(), value), + Suggestion.Entry.Option.SCORE); + PARSER.declareObject((map, value) -> map.put(CompletionSuggestion.Entry.Option.CONTEXTS.getPreferredName(), value), + (p,c) -> parseContexts(p), CompletionSuggestion.Entry.Option.CONTEXTS); + } + + private static Map<String, Set<CharSequence>> parseContexts(XContentParser parser) throws IOException { + Map<String, Set<CharSequence>> contexts = new HashMap<>(); + while((parser.nextToken()) != XContentParser.Token.END_OBJECT) { + ensureExpectedToken(XContentParser.Token.FIELD_NAME, parser.currentToken(), parser::getTokenLocation); + String key = parser.currentName(); + ensureExpectedToken(XContentParser.Token.START_ARRAY, parser.nextToken(), parser::getTokenLocation); + Set<CharSequence> values = new HashSet<>(); + while((parser.nextToken()) != XContentParser.Token.END_ARRAY) { + ensureExpectedToken(XContentParser.Token.VALUE_STRING, parser.currentToken(), parser::getTokenLocation); + values.add(parser.text()); + } + contexts.put(key, values); + } + return contexts; + } + + public static Option fromXContent(XContentParser parser) { + Map<String, Object> values = PARSER.apply(parser, null); + + Text text = new Text((String) values.get(Suggestion.Entry.Option.TEXT.getPreferredName())); + Float score = (Float) values.get(Suggestion.Entry.Option.SCORE.getPreferredName()); + @SuppressWarnings("unchecked") + Map<String, Set<CharSequence>> contexts = (Map<String, Set<CharSequence>>) values + .get(CompletionSuggestion.Entry.Option.CONTEXTS.getPreferredName()); + if (contexts == null) { + contexts = Collections.emptyMap(); + } + + SearchHit hit = null; + // the option either prints SCORE or inlines the search hit + if (score == null) { + hit = SearchHit.createFromMap(values); + score = hit.getScore(); + } + CompletionSuggestion.Entry.Option option = new CompletionSuggestion.Entry.Option(-1, text, score, contexts); + option.setHit(hit); + return option; + } + @Override public void readFrom(StreamInput in) throws IOException { super.readFrom(in); @@ -317,7 +378,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug stringBuilder.append("]"); return stringBuilder.toString(); } - } } |