Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Enforce Completion Context Limit #38675

Merged
merged 8 commits into from
Feb 18, 2019
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 2 additions & 0 deletions docs/reference/search/suggesters/context-suggest.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ the field mapping.
NOTE: It is mandatory to provide a context when indexing and querying
a context enabled completion field.

NOTE: The maximum allowed number of completion field context mappings is 10.

The following defines types, each with two context mappings for a completion
field:

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
*/
package org.elasticsearch.index.mapper;

import org.apache.logging.log4j.LogManager;
import org.apache.lucene.codecs.PostingsFormat;
import org.apache.lucene.index.IndexableField;
import org.apache.lucene.index.Term;
Expand All @@ -31,8 +32,10 @@
import org.apache.lucene.search.suggest.document.RegexCompletionQuery;
import org.apache.lucene.search.suggest.document.SuggestField;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.common.util.set.Sets;
Expand Down Expand Up @@ -85,6 +88,11 @@
public class CompletionFieldMapper extends FieldMapper implements ArrayValueMapperParser {
public static final String CONTENT_TYPE = "completion";

/**
* Maximum allowed number of completion contexts in a mapping.
*/
static final int COMPLETION_CONTEXTS_LIMIT = 10;

public static class Defaults {
public static final MappedFieldType FIELD_TYPE = new CompletionFieldType();
static {
Expand Down Expand Up @@ -354,6 +362,8 @@ public static class Builder extends FieldMapper.Builder<Builder, CompletionField
private boolean preserveSeparators = Defaults.DEFAULT_PRESERVE_SEPARATORS;
private boolean preservePositionIncrements = Defaults.DEFAULT_POSITION_INCREMENTS;

private static final DeprecationLogger deprecationLogger = new DeprecationLogger(LogManager.getLogger(Builder.class));

/**
* @param name of the completion field to build
*/
Expand Down Expand Up @@ -397,6 +407,7 @@ public Builder preservePositionIncrements(boolean preservePositionIncrements) {

@Override
public CompletionFieldMapper build(BuilderContext context) {
checkCompletionContextsLimit(context);
setupFieldType(context);
CompletionFieldType completionFieldType = (CompletionFieldType) this.fieldType;
completionFieldType.setContextMappings(contextMappings);
Expand All @@ -405,6 +416,20 @@ public CompletionFieldMapper build(BuilderContext context) {
return new CompletionFieldMapper(name, this.fieldType, context.indexSettings(),
multiFieldsBuilder.build(this, context), copyTo, maxInputLength);
}

private void checkCompletionContextsLimit(BuilderContext context) {
if (this.contextMappings != null && this.contextMappings.size() > COMPLETION_CONTEXTS_LIMIT) {
if (context.indexCreatedVersion().onOrAfter(Version.V_8_0_0)) {
throw new IllegalArgumentException(
"Limit of completion field contexts [" + COMPLETION_CONTEXTS_LIMIT + "] has been exceeded");
} else {
deprecationLogger.deprecated("You have defined more than [" + COMPLETION_CONTEXTS_LIMIT + "] completion contexts" +
" in the mapping for index [" + context.indexSettings().get(IndexMetaData.SETTING_INDEX_PROVIDED_NAME) + "]. " +
"The maximum allowed number of completion contexts in a mapping will be limited to " +
"[" + COMPLETION_CONTEXTS_LIMIT + "] starting in version [8.0].");
}
}
}
}

private int maxInputLength;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -908,6 +908,28 @@ public void testEmptyName() throws IOException {
assertThat(e.getMessage(), containsString("name cannot be empty string"));
}

public void testLimitOfContextMappings() throws Throwable {
final String index = "test";
XContentBuilder mappingBuilder = XContentFactory.jsonBuilder().startObject().startObject("properties")
.startObject("suggest").field("type", "completion").startArray("contexts");
for (int i = 0; i < CompletionFieldMapper.COMPLETION_CONTEXTS_LIMIT + 1; i++) {
mappingBuilder.startObject();
mappingBuilder.field("name", Integer.toString(i));
mappingBuilder.field("type", "category");
mappingBuilder.endObject();
}

mappingBuilder.endArray().endObject().endObject().endObject();
String mappings = Strings.toString(mappingBuilder);

IllegalArgumentException e = expectThrows(IllegalArgumentException.class, () -> {
createIndex(index).mapperService().documentMapperParser().parse("type1", new CompressedXContent(mappings));
});
assertTrue(e.getMessage(),
e.getMessage().contains("Limit of completion field contexts [" +
CompletionFieldMapper.COMPLETION_CONTEXTS_LIMIT + "] has been exceeded"));
}

private Matcher<IndexableField> suggestField(String value) {
return Matchers.allOf(hasProperty(IndexableField::stringValue, equalTo(value)),
Matchers.instanceOf(SuggestField.class));
Expand Down