Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.xcontent.NamedXContentRegistry;
import org.elasticsearch.common.xcontent.ObjectParser;
import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.CompletionFieldMapper;
Expand All @@ -52,6 +54,7 @@
* indexing.
*/
public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSuggestionBuilder> {
private static final XContentType CONTEXT_BYTES_XCONTENT_TYPE = XContentType.JSON;
static final String SUGGESTION_NAME = "completion";
static final ParseField CONTEXTS_FIELD = new ParseField("contexts", "context");

Expand Down Expand Up @@ -86,7 +89,7 @@ public class CompletionSuggestionBuilder extends SuggestionBuilder<CompletionSug
PARSER.declareInt(CompletionSuggestionBuilder.InnerBuilder::shardSize, SHARDSIZE_FIELD);
PARSER.declareField((p, v, c) -> {
// Copy the current structure. We will parse, once the mapping is provided
XContentBuilder builder = XContentFactory.contentBuilder(XContentType.JSON);
XContentBuilder builder = XContentFactory.contentBuilder(CONTEXT_BYTES_XCONTENT_TYPE);
builder.copyCurrentStructure(p);
v.contextBytes = builder.bytes();
p.skipChildren();
Expand Down Expand Up @@ -186,7 +189,7 @@ public CompletionSuggestionBuilder regex(String regex, RegexOptions regexOptions
public CompletionSuggestionBuilder contexts(Map<String, List<? extends ToXContent>> queryContexts) {
Objects.requireNonNull(queryContexts, "contexts must not be null");
try {
XContentBuilder contentBuilder = XContentFactory.jsonBuilder();
XContentBuilder contentBuilder = XContentFactory.contentBuilder(CONTEXT_BYTES_XCONTENT_TYPE);
contentBuilder.startObject();
for (Map.Entry<String, List<? extends ToXContent>> contextEntry : queryContexts.entrySet()) {
contentBuilder.startArray(contextEntry.getKey());
Expand Down Expand Up @@ -255,33 +258,16 @@ public SuggestionContext build(QueryShardContext context) throws IOException {
suggestionContext.setFuzzyOptions(fuzzyOptions);
suggestionContext.setRegexOptions(regexOptions);
MappedFieldType mappedFieldType = mapperService.fullName(suggestionContext.getField());
if (mappedFieldType == null ||
mappedFieldType instanceof CompletionFieldMapper.CompletionFieldType == false) {
if (mappedFieldType == null || mappedFieldType instanceof CompletionFieldMapper.CompletionFieldType == false) {
throw new IllegalArgumentException("Field [" + suggestionContext.getField() + "] is not a completion suggest field");
}
if (mappedFieldType instanceof CompletionFieldMapper.CompletionFieldType) {
CompletionFieldMapper.CompletionFieldType type = (CompletionFieldMapper.CompletionFieldType) mappedFieldType;
suggestionContext.setFieldType(type);
if (type.hasContextMappings() && contextBytes != null) {
try (XContentParser contextParser = XContentFactory.xContent(contextBytes).createParser(context.getXContentRegistry(),
contextBytes)) {
if (type.hasContextMappings() && contextParser != null) {
ContextMappings contextMappings = type.getContextMappings();
contextParser.nextToken();
Map<String, List<ContextMapping.InternalQueryContext>> queryContexts = new HashMap<>(contextMappings.size());
assert contextParser.currentToken() == XContentParser.Token.START_OBJECT;
XContentParser.Token currentToken;
String currentFieldName;
while ((currentToken = contextParser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (currentToken == XContentParser.Token.FIELD_NAME) {
currentFieldName = contextParser.currentName();
final ContextMapping mapping = contextMappings.get(currentFieldName);
queryContexts.put(currentFieldName, mapping.parseQueryContext(contextParser));
}
}
suggestionContext.setQueryContexts(queryContexts);
}
}
Map<String, List<ContextMapping.InternalQueryContext>> queryContexts = parseContextBytes(contextBytes,
context.getXContentRegistry(), type.getContextMappings());
suggestionContext.setQueryContexts(queryContexts);
} else if (contextBytes != null) {
throw new IllegalArgumentException("suggester [" + type.name() + "] doesn't expect any context");
}
Expand All @@ -290,6 +276,25 @@ public SuggestionContext build(QueryShardContext context) throws IOException {
return suggestionContext;
}

static Map<String, List<ContextMapping.InternalQueryContext>> parseContextBytes(BytesReference contextBytes,
NamedXContentRegistry xContentRegistry, ContextMappings contextMappings) throws IOException {
try (XContentParser contextParser = XContentHelper.createParser(xContentRegistry, contextBytes, CONTEXT_BYTES_XCONTENT_TYPE)) {
contextParser.nextToken();
Map<String, List<ContextMapping.InternalQueryContext>> queryContexts = new HashMap<>(contextMappings.size());
assert contextParser.currentToken() == XContentParser.Token.START_OBJECT;
XContentParser.Token currentToken;
String currentFieldName;
while ((currentToken = contextParser.nextToken()) != XContentParser.Token.END_OBJECT) {
if (currentToken == XContentParser.Token.FIELD_NAME) {
currentFieldName = contextParser.currentName();
final ContextMapping<?> mapping = contextMappings.get(currentFieldName);
queryContexts.put(currentFieldName, mapping.parseQueryContext(contextParser));
}
}
return queryContexts;
}
}

@Override
public String getWriteableName() {
return SUGGESTION_NAME;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,14 @@ public void testBuild() throws IOException {
} else {
assertEquals("mapperServiceSearchAnalyzer", ((NamedAnalyzer) suggestionContext.getAnalyzer()).name());
}
assertSuggester(suggestionBuilder, suggestionContext);
assertSuggestionContext(suggestionBuilder, suggestionContext);
}
}

/**
* put suggester dependent assertions in the sub type test
* put implementation dependent assertions in the sub-type test
*/
protected abstract void assertSuggester(SB builder, SuggestionContext context);
protected abstract void assertSuggestionContext(SB builder, SuggestionContext context) throws IOException;

protected MappedFieldType mockFieldType() {
return mock(MappedFieldType.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,17 +29,19 @@
import org.elasticsearch.search.suggest.completion.context.CategoryQueryContext;
import org.elasticsearch.search.suggest.completion.context.ContextBuilder;
import org.elasticsearch.search.suggest.completion.context.ContextMapping;
import org.elasticsearch.search.suggest.completion.context.ContextMapping.InternalQueryContext;
import org.elasticsearch.search.suggest.completion.context.ContextMappings;
import org.elasticsearch.search.suggest.completion.context.GeoQueryContext;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static org.hamcrest.Matchers.instanceOf;

public class CompletionSuggesterBuilderTests extends AbstractSuggestionBuilderTestCase<CompletionSuggestionBuilder> {

private static final String[] SHUFFLE_PROTECTED_FIELDS = new String[] { CompletionSuggestionBuilder.CONTEXTS_FIELD.getPreferredName() };
Expand All @@ -54,17 +56,17 @@ protected CompletionSuggestionBuilder randomSuggestionBuilder() {
}

public static CompletionSuggestionBuilder randomCompletionSuggestionBuilder() {
// lazy initialization of context names and mappings, cannot be done in init method because other test
// lazy initialization of context names and mappings, cannot be done in some init method because other test
// also create random CompletionSuggestionBuilder instances
if (categoryContextName == null) {
categoryContextName = randomAlphaOfLength(10);
}
if (geoQueryContextName == null) {
geoQueryContextName = randomAlphaOfLength(10);
}
if (contextMappings == null) {
contextMappings = Arrays.asList(new ContextMapping[] { ContextBuilder.category(categoryContextName).build(),
ContextBuilder.geo(geoQueryContextName).build() });
if (contextMappings.isEmpty()) {
contextMappings.add(ContextBuilder.category(categoryContextName).build());
contextMappings.add(ContextBuilder.geo(geoQueryContextName).build());
}
// lazy initialization of context names and mappings, cannot be done in some init method because other test
// also create random CompletionSuggestionBuilder instances
Expand Down Expand Up @@ -165,6 +167,20 @@ protected MappedFieldType mockFieldType() {
}

@Override
protected void assertSuggester(CompletionSuggestionBuilder builder, SuggestionContext context) {
protected void assertSuggestionContext(CompletionSuggestionBuilder builder, SuggestionContext context) throws IOException {
assertThat(context, instanceOf(CompletionSuggestionContext.class));
assertThat(context.getSuggester(), instanceOf(CompletionSuggester.class));
CompletionSuggestionContext completionSuggestionCtx = (CompletionSuggestionContext) context;
assertThat(completionSuggestionCtx.getFieldType(), instanceOf(CompletionFieldType.class) );
assertEquals(builder.fuzzyOptions, completionSuggestionCtx.getFuzzyOptions());
Map<String, List<InternalQueryContext>> parsedContextBytes;
parsedContextBytes = CompletionSuggestionBuilder.parseContextBytes(builder.contextBytes, xContentRegistry(),
new ContextMappings(contextMappings));
Map<String, List<InternalQueryContext>> queryContexts = completionSuggestionCtx.getQueryContexts();
assertEquals(parsedContextBytes.keySet(), queryContexts.keySet());
for (String contextName : queryContexts.keySet()) {
assertEquals(parsedContextBytes.get(contextName), queryContexts.get(contextName));
}
assertEquals(builder.regexOptions, completionSuggestionCtx.getRegexOptions());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public void testInvalidParameters() {
}

@Override
protected void assertSuggester(PhraseSuggestionBuilder builder, SuggestionContext context) {
protected void assertSuggestionContext(PhraseSuggestionBuilder builder, SuggestionContext context) {
assertThat(context, instanceOf(PhraseSuggestionContext.class));
assertThat(context.getSuggester(), instanceOf(PhraseSuggester.class));
PhraseSuggestionContext phraseSuggesterCtx = (PhraseSuggestionContext) context;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public void testMalformedJson() {
}

@Override
protected void assertSuggester(TermSuggestionBuilder builder, SuggestionContext context) {
protected void assertSuggestionContext(TermSuggestionBuilder builder, SuggestionContext context) {
assertThat(context, instanceOf(TermSuggestionContext.class));
assertThat(context.getSuggester(), instanceOf(TermSuggester.class));
TermSuggestionContext termSuggesterCtx = (TermSuggestionContext) context;
Expand Down