Skip to content

Commit

Permalink
Enforce Completion Context Limit (#38675)
Browse files Browse the repository at this point in the history
This change adds a limit to the number of completion contexts that a completion field can define.

Closes #32741
  • Loading branch information
TommyWind authored and jimczi committed Feb 18, 2019
1 parent d9f924c commit c1ab821
Show file tree
Hide file tree
Showing 5 changed files with 72 additions and 1 deletion.
16 changes: 16 additions & 0 deletions docs/reference/migration/migrate_8_0.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,20 @@ coming[8.0.0]

* <<breaking_80_mappings_changes>>

[float]
=== Indices created before 7.0

Elasticsearch 8.0 can read indices created in version 7.0 or above. An
Elasticsearch 8.0 node will not start in the presence of indices created in a
version of Elasticsearch before 7.0.

[IMPORTANT]
.Reindex indices from Elasticsearch 6.x or before
=========================================
Indices created in Elasticsearch 6.x or before will need to be reindexed with
Elasticsearch 7.x in order to be readable by Elasticsearch 8.x.
=========================================

include::migrate_8_0/mappings.asciidoc[]
8 changes: 7 additions & 1 deletion docs/reference/migration/migrate_8_0/mappings.asciidoc
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@

The `nGram` and `edgeNGram` token filter names that have been deprecated since
version 6.4 have been removed. Both token filters should be used by their
alternative names `ngram` and `edge_ngram` instead.
alternative names `ngram` and `edge_ngram` instead.

[float]
==== Limiting the number of completion contexts

The number of completion contexts within a single completion field
has been limited to 10.
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

0 comments on commit c1ab821

Please sign in to comment.