Merge pull request #19877 from areek/fix/remove_completion_payload

Remove payload option from completion suggester
This commit is contained in:
Areek Zillur 2016-08-09 15:27:29 -04:00 committed by GitHub
commit 16d93e5a53
10 changed files with 23 additions and 378 deletions

View File

@ -78,7 +78,6 @@ public class CompletionSuggester extends Suggester<CompletionSuggestionContext>
TopSuggestDocsCollector collector = new TopDocumentsCollector(suggestionContext.getSize()); TopSuggestDocsCollector collector = new TopDocumentsCollector(suggestionContext.getSize());
suggest(searcher, suggestionContext.toQuery(), collector); suggest(searcher, suggestionContext.toQuery(), collector);
int numResult = 0; int numResult = 0;
List<LeafReaderContext> leaves = searcher.getIndexReader().leaves();
for (TopSuggestDocs.SuggestScoreDoc suggestScoreDoc : collector.get().scoreLookupDocs()) { for (TopSuggestDocs.SuggestScoreDoc suggestScoreDoc : collector.get().scoreLookupDocs()) {
TopDocumentsCollector.SuggestDoc suggestDoc = (TopDocumentsCollector.SuggestDoc) suggestScoreDoc; TopDocumentsCollector.SuggestDoc suggestDoc = (TopDocumentsCollector.SuggestDoc) suggestScoreDoc;
// collect contexts // collect contexts
@ -86,31 +85,9 @@ public class CompletionSuggester extends Suggester<CompletionSuggestionContext>
if (fieldType.hasContextMappings() && suggestDoc.getContexts().isEmpty() == false) { if (fieldType.hasContextMappings() && suggestDoc.getContexts().isEmpty() == false) {
contexts = fieldType.getContextMappings().getNamedContexts(suggestDoc.getContexts()); contexts = fieldType.getContextMappings().getNamedContexts(suggestDoc.getContexts());
} }
// collect payloads
final Map<String, List<Object>> payload = new HashMap<>(0);
List<String> payloadFields = suggestionContext.getPayloadFields();
if (payloadFields.isEmpty() == false) {
final int readerIndex = ReaderUtil.subIndex(suggestDoc.doc, leaves);
final LeafReaderContext subReaderContext = leaves.get(readerIndex);
final int subDocId = suggestDoc.doc - subReaderContext.docBase;
for (String field : payloadFields) {
MapperService mapperService = suggestionContext.getShardContext().getMapperService();
MappedFieldType payloadFieldType = mapperService.fullName(field);
if (payloadFieldType != null) {
QueryShardContext shardContext = suggestionContext.getShardContext();
final AtomicFieldData data = shardContext.getForField(payloadFieldType)
.load(subReaderContext);
final ScriptDocValues scriptValues = data.getScriptValues();
scriptValues.setNextDocId(subDocId);
payload.put(field, new ArrayList<>(scriptValues.getValues()));
} else {
throw new IllegalArgumentException("payload field [" + field + "] does not exist");
}
}
}
if (numResult++ < suggestionContext.getSize()) { if (numResult++ < suggestionContext.getSize()) {
CompletionSuggestion.Entry.Option option = new CompletionSuggestion.Entry.Option(suggestDoc.doc, CompletionSuggestion.Entry.Option option = new CompletionSuggestion.Entry.Option(suggestDoc.doc,
new Text(suggestDoc.key.toString()), suggestDoc.score, contexts, payload); new Text(suggestDoc.key.toString()), suggestDoc.score, contexts);
completionSuggestEntry.addOption(option); completionSuggestEntry.addOption(option);
} else { } else {
break; break;

View File

@ -194,14 +194,12 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
public static class Option extends Suggest.Suggestion.Entry.Option { public static class Option extends Suggest.Suggestion.Entry.Option {
private Map<String, Set<CharSequence>> contexts; private Map<String, Set<CharSequence>> contexts;
private Map<String, List<Object>> payload;
private ScoreDoc doc; private ScoreDoc doc;
private InternalSearchHit hit; private InternalSearchHit hit;
public Option(int docID, Text text, float score, Map<String, Set<CharSequence>> contexts, Map<String, List<Object>> payload) { public Option(int docID, Text text, float score, Map<String, Set<CharSequence>> contexts) {
super(text, score); super(text, score);
this.doc = new ScoreDoc(docID, score); this.doc = new ScoreDoc(docID, score);
this.payload = payload;
this.contexts = contexts; this.contexts = contexts;
} }
@ -216,10 +214,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
public Map<String, List<Object>> getPayload() {
return payload;
}
public Map<String, Set<CharSequence>> getContexts() { public Map<String, Set<CharSequence>> getContexts() {
return contexts; return contexts;
} }
@ -248,17 +242,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
} else { } else {
builder.field("score", getScore()); builder.field("score", getScore());
} }
if (payload.size() > 0) {
builder.startObject("payload");
for (Map.Entry<String, List<Object>> entry : payload.entrySet()) {
builder.startArray(entry.getKey());
for (Object payload : entry.getValue()) {
builder.value(payload);
}
builder.endArray();
}
builder.endObject();
}
if (contexts.size() > 0) { if (contexts.size() > 0) {
builder.startObject("contexts"); builder.startObject("contexts");
for (Map.Entry<String, Set<CharSequence>> entry : contexts.entrySet()) { for (Map.Entry<String, Set<CharSequence>> entry : contexts.entrySet()) {
@ -281,17 +264,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
this.hit = InternalSearchHit.readSearchHit(in, this.hit = InternalSearchHit.readSearchHit(in,
InternalSearchHits.streamContext().streamShardTarget(ShardTargetType.STREAM)); InternalSearchHits.streamContext().streamShardTarget(ShardTargetType.STREAM));
} }
int payloadSize = in.readInt();
this.payload = new LinkedHashMap<>(payloadSize);
for (int i = 0; i < payloadSize; i++) {
String payloadName = in.readString();
int nValues = in.readVInt();
List<Object> values = new ArrayList<>(nValues);
for (int j = 0; j < nValues; j++) {
values.add(in.readGenericValue());
}
this.payload.put(payloadName, values);
}
int contextSize = in.readInt(); int contextSize = in.readInt();
this.contexts = new LinkedHashMap<>(contextSize); this.contexts = new LinkedHashMap<>(contextSize);
for (int i = 0; i < contextSize; i++) { for (int i = 0; i < contextSize; i++) {
@ -315,15 +287,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
} else { } else {
out.writeBoolean(false); out.writeBoolean(false);
} }
out.writeInt(payload.size());
for (Map.Entry<String, List<Object>> entry : payload.entrySet()) {
out.writeString(entry.getKey());
List<Object> values = entry.getValue();
out.writeVInt(values.size());
for (Object value : values) {
out.writeGenericValue(value);
}
}
out.writeInt(contexts.size()); out.writeInt(contexts.size());
for (Map.Entry<String, Set<CharSequence>> entry : contexts.entrySet()) { for (Map.Entry<String, Set<CharSequence>> entry : contexts.entrySet()) {
out.writeString(entry.getKey()); out.writeString(entry.getKey());
@ -341,14 +304,6 @@ public final class CompletionSuggestion extends Suggest.Suggestion<CompletionSug
stringBuilder.append(getText()); stringBuilder.append(getText());
stringBuilder.append(" score:"); stringBuilder.append(" score:");
stringBuilder.append(getScore()); stringBuilder.append(getScore());
stringBuilder.append(" payload:[");
for (Map.Entry<String, List<Object>> entry : payload.entrySet()) {
stringBuilder.append(" ");
stringBuilder.append(entry.getKey());
stringBuilder.append(":");
stringBuilder.append(entry.getValue());
}
stringBuilder.append("]");
stringBuilder.append(" context:["); stringBuilder.append(" context:[");
for (Map.Entry<String, Set<CharSequence>> entry: contexts.entrySet()) { for (Map.Entry<String, Set<CharSequence>> entry: contexts.entrySet()) {
stringBuilder.append(" "); stringBuilder.append(" ");

View File

@ -62,7 +62,6 @@ import java.util.Objects;
*/ */
public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSuggestionBuilder> { public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSuggestionBuilder> {
static final String SUGGESTION_NAME = "completion"; static final String SUGGESTION_NAME = "completion";
static final ParseField PAYLOAD_FIELD = new ParseField("payload");
static final ParseField CONTEXTS_FIELD = new ParseField("contexts", "context"); static final ParseField CONTEXTS_FIELD = new ParseField("contexts", "context");
/** /**
@ -78,7 +77,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
private static ObjectParser<CompletionSuggestionBuilder.InnerBuilder, ParseFieldMatcherSupplier> TLP_PARSER = private static ObjectParser<CompletionSuggestionBuilder.InnerBuilder, ParseFieldMatcherSupplier> TLP_PARSER =
new ObjectParser<>(SUGGESTION_NAME, null); new ObjectParser<>(SUGGESTION_NAME, null);
static { static {
TLP_PARSER.declareStringArray(CompletionSuggestionBuilder.InnerBuilder::payload, PAYLOAD_FIELD);
TLP_PARSER.declareField((parser, completionSuggestionContext, context) -> { TLP_PARSER.declareField((parser, completionSuggestionContext, context) -> {
if (parser.currentToken() == XContentParser.Token.VALUE_BOOLEAN) { if (parser.currentToken() == XContentParser.Token.VALUE_BOOLEAN) {
if (parser.booleanValue()) { if (parser.booleanValue()) {
@ -108,7 +106,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
protected FuzzyOptions fuzzyOptions; protected FuzzyOptions fuzzyOptions;
protected RegexOptions regexOptions; protected RegexOptions regexOptions;
protected BytesReference contextBytes = null; protected BytesReference contextBytes = null;
protected List<String> payloadFields = Collections.emptyList();
public CompletionSuggestionBuilder(String field) { public CompletionSuggestionBuilder(String field) {
super(field); super(field);
@ -123,7 +120,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
fuzzyOptions = in.fuzzyOptions; fuzzyOptions = in.fuzzyOptions;
regexOptions = in.regexOptions; regexOptions = in.regexOptions;
contextBytes = in.contextBytes; contextBytes = in.contextBytes;
payloadFields = in.payloadFields;
} }
/** /**
@ -131,8 +127,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
*/ */
public CompletionSuggestionBuilder(StreamInput in) throws IOException { public CompletionSuggestionBuilder(StreamInput in) throws IOException {
super(in); super(in);
payloadFields = new ArrayList<>();
Collections.addAll(payloadFields, in.readStringArray());
fuzzyOptions = in.readOptionalWriteable(FuzzyOptions::new); fuzzyOptions = in.readOptionalWriteable(FuzzyOptions::new);
regexOptions = in.readOptionalWriteable(RegexOptions::new); regexOptions = in.readOptionalWriteable(RegexOptions::new);
contextBytes = in.readOptionalBytesReference(); contextBytes = in.readOptionalBytesReference();
@ -140,7 +134,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
@Override @Override
public void doWriteTo(StreamOutput out) throws IOException { public void doWriteTo(StreamOutput out) throws IOException {
out.writeStringArray(payloadFields.toArray(new String[payloadFields.size()]));
out.writeOptionalWriteable(fuzzyOptions); out.writeOptionalWriteable(fuzzyOptions);
out.writeOptionalWriteable(regexOptions); out.writeOptionalWriteable(regexOptions);
out.writeOptionalBytesReference(contextBytes); out.writeOptionalBytesReference(contextBytes);
@ -194,16 +187,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
return this; return this;
} }
/**
* Sets the fields to be returned as suggestion payload.
* Note: Only doc values enabled fields are supported
*/
public CompletionSuggestionBuilder payload(List<String> fields) {
Objects.requireNonNull(fields, "payload must not be null");
this.payloadFields = fields;
return this;
}
/** /**
* Sets query contexts for completion * Sets query contexts for completion
* @param queryContexts named query contexts * @param queryContexts named query contexts
@ -348,13 +331,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
@Override @Override
protected XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException { protected XContentBuilder innerToXContent(XContentBuilder builder, Params params) throws IOException {
if (payloadFields.isEmpty() == false) {
builder.startArray(PAYLOAD_FIELD.getPreferredName());
for (String field : payloadFields) {
builder.value(field);
}
builder.endArray();
}
if (fuzzyOptions != null) { if (fuzzyOptions != null) {
fuzzyOptions.toXContent(builder, params); fuzzyOptions.toXContent(builder, params);
} }
@ -388,7 +364,6 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
// copy over common settings to each suggestion builder // copy over common settings to each suggestion builder
final MapperService mapperService = context.getMapperService(); final MapperService mapperService = context.getMapperService();
populateCommonFields(mapperService, suggestionContext); populateCommonFields(mapperService, suggestionContext);
suggestionContext.setPayloadFields(payloadFields);
suggestionContext.setFuzzyOptions(fuzzyOptions); suggestionContext.setFuzzyOptions(fuzzyOptions);
suggestionContext.setRegexOptions(regexOptions); suggestionContext.setRegexOptions(regexOptions);
MappedFieldType mappedFieldType = mapperService.fullName(suggestionContext.getField()); MappedFieldType mappedFieldType = mapperService.fullName(suggestionContext.getField());
@ -449,14 +424,13 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
@Override @Override
protected boolean doEquals(CompletionSuggestionBuilder other) { protected boolean doEquals(CompletionSuggestionBuilder other) {
return Objects.equals(payloadFields, other.payloadFields) && return Objects.equals(fuzzyOptions, other.fuzzyOptions) &&
Objects.equals(fuzzyOptions, other.fuzzyOptions) &&
Objects.equals(regexOptions, other.regexOptions) && Objects.equals(regexOptions, other.regexOptions) &&
Objects.equals(contextBytes, other.contextBytes); Objects.equals(contextBytes, other.contextBytes);
} }
@Override @Override
protected int doHashCode() { protected int doHashCode() {
return Objects.hash(payloadFields, fuzzyOptions, regexOptions, contextBytes); return Objects.hash(fuzzyOptions, regexOptions, contextBytes);
} }
} }

View File

@ -45,7 +45,6 @@ public class CompletionSuggestionContext extends SuggestionSearchContext.Suggest
private FuzzyOptions fuzzyOptions; private FuzzyOptions fuzzyOptions;
private RegexOptions regexOptions; private RegexOptions regexOptions;
private Map<String, List<ContextMapping.InternalQueryContext>> queryContexts = Collections.emptyMap(); private Map<String, List<ContextMapping.InternalQueryContext>> queryContexts = Collections.emptyMap();
private List<String> payloadFields = Collections.emptyList();
private CompletionFieldMapper2x.CompletionFieldType fieldType2x; private CompletionFieldMapper2x.CompletionFieldType fieldType2x;
private List<ContextQuery> contextQueries; private List<ContextQuery> contextQueries;
@ -73,14 +72,6 @@ public class CompletionSuggestionContext extends SuggestionSearchContext.Suggest
this.queryContexts = queryContexts; this.queryContexts = queryContexts;
} }
void setPayloadFields(List<String> fields) {
this.payloadFields = fields;
}
List<String> getPayloadFields() {
return payloadFields;
}
public FuzzyOptions getFuzzyOptions() { public FuzzyOptions getFuzzyOptions() {
return fuzzyOptions; return fuzzyOptions;
} }

View File

@ -149,7 +149,7 @@ public class SearchPhaseControllerTests extends ESTestCase {
float maxScore = randomIntBetween(suggestion.getSize(), (int) Float.MAX_VALUE); float maxScore = randomIntBetween(suggestion.getSize(), (int) Float.MAX_VALUE);
for (int i = 0; i < optionSize; i++) { for (int i = 0; i < optionSize; i++) {
completionEntry.addOption(new CompletionSuggestion.Entry.Option(i, new Text(""), maxScore, completionEntry.addOption(new CompletionSuggestion.Entry.Option(i, new Text(""), maxScore,
Collections.emptyMap(), Collections.emptyMap())); Collections.emptyMap()));
float dec = randomIntBetween(0, optionSize); float dec = randomIntBetween(0, optionSize);
if (dec <= maxScore) { if (dec <= maxScore) {
maxScore -= dec; maxScore -= dec;

View File

@ -246,156 +246,6 @@ public class CompletionSuggestSearchIT extends ESIntegTestCase {
assertSuggestions("foo", fuzzyPrefix, outputs); assertSuggestions("foo", fuzzyPrefix, outputs);
} }
public void testSuggestWithNumericPayload() throws Exception {
final CompletionMappingBuilder mapping = new CompletionMappingBuilder();
createIndexAndMapping(mapping);
int numDocs = 10;
List<IndexRequestBuilder> indexRequestBuilders = new ArrayList<>();
for (int i = 0; i < numDocs; i++) {
XContentBuilder source= jsonBuilder()
.startObject()
.field(FIELD, "suggestion" + i)
.field("count", i)
.endObject();
indexRequestBuilders.add(client().prepareIndex(INDEX, TYPE, "" + i).setSource(source));
}
indexRandom(true, indexRequestBuilders);
CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg").
size(numDocs).payload(Collections.singletonList("count"));
SearchResponse searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix))
.execute().actionGet();
assertNoFailures(searchResponse);
CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo");
CompletionSuggestion.Entry options = completionSuggestion.getEntries().get(0);
assertThat(options.getOptions().size(), equalTo(numDocs));
for (CompletionSuggestion.Entry.Option option : options) {
Map<String, List<Object>> payloads = option.getPayload();
assertThat(payloads.keySet(), contains("count"));
}
}
public void testMissingPayloadField() throws Exception {
final CompletionMappingBuilder mapping = new CompletionMappingBuilder();
createIndexAndMapping(mapping);
List<IndexRequestBuilder> indexRequestBuilders = Arrays.asList(
client().prepareIndex(INDEX, TYPE, "1").setSource(FIELD, "suggestion", "test_field", "test"),
client().prepareIndex(INDEX, TYPE, "2").setSource(FIELD, "suggestion")
);
indexRandom(true, indexRequestBuilders);
CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg")
.payload(Collections.singletonList("test_field"));
SearchResponse searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix))
.execute().actionGet();
assertNoFailures(searchResponse);
CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo");
CompletionSuggestion.Entry options = completionSuggestion.getEntries().get(0);
assertThat(options.getOptions().size(), equalTo(2));
for (CompletionSuggestion.Entry.Option option : options.getOptions()) {
assertThat(option.getPayload().keySet(), contains("test_field"));
}
}
public void testPayload() throws Exception {
final CompletionMappingBuilder mapping = new CompletionMappingBuilder();
createIndexAndMapping(mapping);
List<IndexRequestBuilder> indexRequestBuilders = new ArrayList<>();
XContentBuilder source = jsonBuilder()
.startObject()
.startObject(FIELD)
.field("input", "suggest")
.field("weight", 1)
.endObject()
.field("title", "title1")
.field("count", 1)
.endObject();
indexRequestBuilders.add(client().prepareIndex(INDEX, TYPE, "1").setSource(source));
source = jsonBuilder()
.startObject()
.startObject(FIELD)
.field("input", "suggestion")
.field("weight", 2)
.endObject()
.field("title", "title2")
.field("count", 2)
.endObject();
indexRequestBuilders.add(client().prepareIndex(INDEX, TYPE, "2").setSource(source));
indexRandom(true, indexRequestBuilders);
CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg")
.payload(Arrays.asList("title", "count"));
SearchResponse searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix))
.execute().actionGet();
assertNoFailures(searchResponse);
CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo");
List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();
assertThat(options.size(), equalTo(2));
assertThat(options.get(0).getText().toString(), equalTo("suggestion"));
assertThat(options.get(0).getScore(), equalTo(2f));
assertThat(options.get(1).getText().toString(), equalTo("suggest"));
assertThat(options.get(1).getScore(), equalTo(1f));
Map<String, List<Object>> firstPayload = options.get(0).getPayload();
assertThat(firstPayload.keySet(), containsInAnyOrder("title", "count"));
assertThat((String) firstPayload.get("title").get(0), equalTo("title2"));
assertThat((long) firstPayload.get("count").get(0), equalTo(2L));
Map<String, List<Object>> secondPayload = options.get(1).getPayload();
assertThat(secondPayload.keySet(), containsInAnyOrder("title", "count"));
assertThat((String) secondPayload.get("title").get(0), equalTo("title1"));
assertThat((long) secondPayload.get("count").get(0), equalTo(1L));
}
public void testSuggestWithPayload() throws Exception {
final CompletionMappingBuilder mapping = new CompletionMappingBuilder();
createIndexAndMapping(mapping);
int numDocs = randomIntBetween(10, 100);
int numPayloadFields = randomIntBetween(2, 5);
List<IndexRequestBuilder> indexRequestBuilders = new ArrayList<>();
for (int i = 1; i <= numDocs; i++) {
XContentBuilder source = jsonBuilder()
.startObject()
.startObject(FIELD)
.field("input", "suggestion" + i)
.field("weight", i)
.endObject();
for (int j = 0; j < numPayloadFields; j++) {
source.field("test_field" + j, j + "value" + i);
}
source.endObject();
indexRequestBuilders.add(client().prepareIndex(INDEX, TYPE, "" + i).setSource(source));
}
indexRandom(true, indexRequestBuilders);
int suggestionSize = randomIntBetween(1, numDocs);
int numRequestedPayloadFields = randomIntBetween(2, numPayloadFields);
List<String> payloadFields = new ArrayList<>(numRequestedPayloadFields);
for (int i = 0; i < numRequestedPayloadFields; i++) {
payloadFields.add("test_field" + i + ".keyword");
}
CompletionSuggestionBuilder prefix = SuggestBuilders.completionSuggestion(FIELD).prefix("sugg")
.size(suggestionSize).payload(payloadFields);
SearchResponse searchResponse = client().prepareSearch(INDEX).suggest(new SuggestBuilder().addSuggestion("foo", prefix))
.execute().actionGet();
assertNoFailures(searchResponse);
CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion("foo");
CompletionSuggestion.Entry options = completionSuggestion.getEntries().get(0);
assertThat(options.getOptions().size(), equalTo(suggestionSize));
int id = numDocs;
for (CompletionSuggestion.Entry.Option option : options) {
assertThat(option.getText().toString(), equalTo("suggestion" + id));
assertThat(option.getPayload().size(), equalTo(numRequestedPayloadFields));
for (int i = 0; i < numRequestedPayloadFields; i++) {
List<Object> fieldValue = option.getPayload().get("test_field" + i + ".keyword");
assertNotNull(fieldValue);
assertThat(fieldValue.size(), equalTo(1));
assertThat((String)fieldValue.get(0), equalTo(i + "value" + id));
}
id--;
}
}
public void testSuggestDocument() throws Exception { public void testSuggestDocument() throws Exception {
final CompletionMappingBuilder mapping = new CompletionMappingBuilder(); final CompletionMappingBuilder mapping = new CompletionMappingBuilder();
createIndexAndMapping(mapping); createIndexAndMapping(mapping);

View File

@ -19,14 +19,10 @@
package org.elasticsearch.search.suggest.completion; package org.elasticsearch.search.suggest.completion;
import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase; import org.elasticsearch.search.suggest.AbstractSuggestionBuilderTestCase;
import org.elasticsearch.search.suggest.SuggestBuilder;
import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext; import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext;
import org.elasticsearch.search.suggest.completion.context.GeoQueryContext; import org.elasticsearch.search.suggest.completion.context.GeoQueryContext;
@ -35,9 +31,7 @@ import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale;
import java.util.Map; import java.util.Map;
import static org.hamcrest.Matchers.containsString;
public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTestCase<CompletionSuggestionBuilder> { public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTestCase<CompletionSuggestionBuilder> {
@ -76,9 +70,6 @@ public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTe
testBuilder.regex(randomAsciiOfLength(10), RegexOptionsTests.randomRegexOptions()); testBuilder.regex(randomAsciiOfLength(10), RegexOptionsTests.randomRegexOptions());
break; break;
} }
List<String> payloads = new ArrayList<>();
Collections.addAll(payloads, generateRandomStringArray(5, 10, false, false));
maybeSet(testBuilder::payload, payloads);
Map<String, List<? extends ToXContent>> contextMap = new HashMap<>(); Map<String, List<? extends ToXContent>> contextMap = new HashMap<>();
if (randomBoolean()) { if (randomBoolean()) {
int numContext = randomIntBetween(1, 5); int numContext = randomIntBetween(1, 5);
@ -116,13 +107,8 @@ public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTe
@Override @Override
protected void mutateSpecificParameters(CompletionSuggestionBuilder builder) throws IOException { protected void mutateSpecificParameters(CompletionSuggestionBuilder builder) throws IOException {
switch (randomIntBetween(0, 5)) { switch (randomIntBetween(0, 4)) {
case 0: case 0:
List<String> payloads = new ArrayList<>();
Collections.addAll(payloads, generateRandomStringArray(5, 10, false, false));
builder.payload(payloads);
break;
case 1:
int nCatContext = randomIntBetween(1, 5); int nCatContext = randomIntBetween(1, 5);
List<CategoryQueryContext> contexts = new ArrayList<>(nCatContext); List<CategoryQueryContext> contexts = new ArrayList<>(nCatContext);
for (int i = 0; i < nCatContext; i++) { for (int i = 0; i < nCatContext; i++) {
@ -130,7 +116,7 @@ public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTe
} }
builder.contexts(Collections.singletonMap(randomAsciiOfLength(10), contexts)); builder.contexts(Collections.singletonMap(randomAsciiOfLength(10), contexts));
break; break;
case 2: case 1:
int nGeoContext = randomIntBetween(1, 5); int nGeoContext = randomIntBetween(1, 5);
List<GeoQueryContext> geoContexts = new ArrayList<>(nGeoContext); List<GeoQueryContext> geoContexts = new ArrayList<>(nGeoContext);
for (int i = 0; i < nGeoContext; i++) { for (int i = 0; i < nGeoContext; i++) {
@ -138,39 +124,17 @@ public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTe
} }
builder.contexts(Collections.singletonMap(randomAsciiOfLength(10), geoContexts)); builder.contexts(Collections.singletonMap(randomAsciiOfLength(10), geoContexts));
break; break;
case 3: case 2:
builder.prefix(randomAsciiOfLength(10), FuzzyOptionsTests.randomFuzzyOptions()); builder.prefix(randomAsciiOfLength(10), FuzzyOptionsTests.randomFuzzyOptions());
break; break;
case 4: case 3:
builder.prefix(randomAsciiOfLength(10), randomFrom(Fuzziness.ZERO, Fuzziness.ONE, Fuzziness.TWO)); builder.prefix(randomAsciiOfLength(10), randomFrom(Fuzziness.ZERO, Fuzziness.ONE, Fuzziness.TWO));
break; break;
case 5: case 4:
builder.regex(randomAsciiOfLength(10), RegexOptionsTests.randomRegexOptions()); builder.regex(randomAsciiOfLength(10), RegexOptionsTests.randomRegexOptions());
break; break;
default: default:
throw new IllegalStateException("should not through"); throw new IllegalStateException("should not through");
} }
} }
/**
* Test that a malformed JSON suggestion request fails.
*/
public void testMalformedJsonRequestPayload() throws Exception {
final String field = RandomStrings.randomAsciiOfLength(random(), 10).toLowerCase(Locale.ROOT);
final String payload = "{\n" +
" \"bad-payload\" : { \n" +
" \"prefix\" : \"sug\",\n" +
" \"completion\" : { \n" +
" \"field\" : \"" + field + "\",\n " +
" \"payload\" : [ {\"payload\":\"field\"} ]\n" +
" }\n" +
" }\n" +
"}\n";
try {
final SuggestBuilder suggestBuilder = SuggestBuilder.fromXContent(newParseContext(payload), suggesters);
fail("Should not have been able to create SuggestBuilder from malformed JSON: " + suggestBuilder);
} catch (ParsingException e) {
assertThat(e.getMessage(), containsString("failed to parse field [payload]"));
}
}
} }

View File

@ -47,7 +47,7 @@ public class CompletionSuggestionTests extends ESTestCase {
for (int i = 0; i < totalResults; i++) { for (int i = 0; i < totalResults; i++) {
Suggest.Suggestion<CompletionSuggestion.Entry> suggestion = randomFrom(shardSuggestions); Suggest.Suggestion<CompletionSuggestion.Entry> suggestion = randomFrom(shardSuggestions);
suggestion.getEntries().get(0).addOption(new CompletionSuggestion.Entry.Option(i, new Text(""), suggestion.getEntries().get(0).addOption(new CompletionSuggestion.Entry.Option(i, new Text(""),
maxScore - i, Collections.emptyMap(), Collections.emptyMap())); maxScore - i, Collections.emptyMap()));
} }
CompletionSuggestion reducedSuggestion = CompletionSuggestion.reduceTo(shardSuggestions); CompletionSuggestion reducedSuggestion = CompletionSuggestion.reduceTo(shardSuggestions);
assertNotNull(reducedSuggestion); assertNotNull(reducedSuggestion);

View File

@ -195,80 +195,11 @@ returns this response:
// TESTRESPONSE // TESTRESPONSE
The configured weight for a suggestion is returned as `_score`. The configured weight for a suggestion is returned as `_score`.
The `text` field uses the `input` of your indexed suggestion. The document The `text` field uses the `input` of your indexed suggestion.
source is returned in `_source`. <<search-request-source-filtering, source filtering>> Suggestions are document oriented, the document source is
returned in `_source`. <<search-request-source-filtering, source filtering>>
parameters are supported for filtering the document source. parameters are supported for filtering the document source.
Suggestions are document oriented, you can specify fields to be
returned as part of suggestion payload. All field types (`string`,
`numeric`, `date`, etc) are supported.
For example, if you index a "title" field along with the suggestion
as follows:
[source,js]
--------------------------------------------------
PUT music/song/2?refresh
{
"suggest" : "Nirvana",
"title" : "Nevermind"
}
--------------------------------------------------
// CONSOLE
You can get the "title" as part of the suggestion
payload by specifying it as a `payload`:
[source,js]
--------------------------------------------------
POST music/_suggest?pretty
{
"song-suggest" : {
"prefix" : "n",
"completion" : {
"field" : "suggest",
"payload" : [ "title" ] <1>
}
}
}
--------------------------------------------------
// CONSOLE
// TEST[continued]
returns:
[source,js]
--------------------------------------------------
{
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"song-suggest" : [ {
"text" : "n",
"offset" : 0,
"length" : 1,
"options" : [ {
"text" : "Nirvana",
"_index": "music",
"_type": "song",
"_id": "2",
"_score" : 1.0,
"_source": {
"title": "Nevermind",
"suggest": "Nirvana"
},
"payload" : {
"title" : [ "Nevermind" ]
}
} ]
} ]
}
--------------------------------------------------
// TESTRESPONSE
<1> The fields to be returned as part of each suggestion payload.
The basic completion suggester query supports the following parameters: The basic completion suggester query supports the following parameters:
`field`:: The name of the field on which to run the query (required). `field`:: The name of the field on which to run the query (required).

View File

@ -233,7 +233,7 @@ setup:
- match: { result.0.options.0.text: "baz" } - match: { result.0.options.0.text: "baz" }
--- ---
"Suggestions with payload fields should work": "Suggestions with source should work":
- do: - do:
index: index:
@ -269,14 +269,17 @@ setup:
text: "b" text: "b"
completion: completion:
field: suggest_6 field: suggest_6
payload: [ title, count ]
- length: { result: 1 } - length: { result: 1 }
- length: { result.0.options: 2 } - length: { result.0.options: 2 }
- match: { result.0.options.0.text: "baz" } - match: { result.0.options.0.text: "baz" }
- match: { result.0.options.0.payload.title: ["title_baz"] } - match: { result.0.options.0._index: "test" }
- match: { result.0.options.0.payload.count: [3] } - match: { result.0.options.0._type: "test" }
- match: { result.0.options.0._source.title: "title_baz" }
- match: { result.0.options.0._source.count: 3 }
- match: { result.0.options.1.text: "bar" } - match: { result.0.options.1.text: "bar" }
- match: { result.0.options.1.payload.title: ["title_bar"] } - match: { result.0.options.1._index: "test" }
- match: { result.0.options.1.payload.count: [4] } - match: { result.0.options.1._type: "test" }
- match: { result.0.options.1._source.title: "title_bar" }
- match: { result.0.options.1._source.count: 4 }