Add parameter update and conflict tests to MapperTestCase (#62828) (#62902)

This commit adds a mechanism to MapperTestCase that allows implementing
test classes to check that their parameters can be updated, or throw conflict
errors as advertised. Child classes override the registerParameters method
and tell the passed-in UpdateChecker class about their parameters. Simple
conflicts can be checked, using the existing minimal mappings as a base to
compare against, or alternatively a particular initial mapping can be provided
to check edge cases (eg, norms can be updated from true to false, but not
vice versa). Updates are registered with a predicate that checks that the update
has in fact been applied to the resulting FieldMapper.

Fixes #61631
This commit is contained in:
Alan Woodward 2020-09-24 20:38:12 +01:00 committed by GitHub
parent 4b9ddb48b6
commit e28750b001
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
36 changed files with 574 additions and 132 deletions

View File

@ -282,6 +282,14 @@ public class ScaledFloatFieldMapper extends ParametrizedFieldMapper {
this.coerceByDefault = builder.coerce.getDefaultValue().value();
}
boolean coerce() {
return coerce.value();
}
boolean ignoreMalformed() {
return ignoreMalformed.value();
}
@Override
public ScaledFloatFieldType fieldType() {
return (ScaledFloatFieldType) super.fieldType();

View File

@ -33,7 +33,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.plugins.Plugin;
import org.hamcrest.Matchers;
import org.junit.Before;
import java.io.IOException;
import java.util.Arrays;
@ -54,12 +53,9 @@ public class RankFeatureFieldMapperTests extends FieldMapperTestCase2<RankFeatur
return org.elasticsearch.common.collect.Set.of("analyzer", "similarity", "store", "doc_values", "index");
}
@Before
public void setup() {
addModifier("positive_score_impact", false, (a, b) -> {
a.positiveScoreImpact(true);
b.positiveScoreImpact(false);
});
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("positive_score_impact", b -> b.field("positive_score_impact", false));
}
@Override

View File

@ -59,6 +59,11 @@ public class RankFeaturesFieldMapperTests extends FieldMapperTestCase2<RankFeatu
b.field("type", "rank_features");
}
@Override
protected void registerParameters(ParameterChecker checker) {
// no parameters to configure
}
@Override
protected boolean supportsMeta() {
return false;

View File

@ -56,6 +56,25 @@ public class ScaledFloatFieldMapperTests extends MapperTestCase {
b.field("type", "scaled_float").field("scaling_factor", 10.0);
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck(
"scaling_factor",
fieldMapping(this::minimalMapping),
fieldMapping(b -> {
b.field("type", "scaled_float");
b.field("scaling_factor", 5.0);
}));
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("null_value", b -> b.field("null_value", 1));
checker.registerUpdateCheck(b -> b.field("coerce", false),
m -> assertFalse(((ScaledFloatFieldMapper) m).coerce()));
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true),
m -> assertTrue(((ScaledFloatFieldMapper) m).ignoreMalformed()));
}
public void testExistsQueryDocValuesDisabled() throws IOException {
MapperService mapperService = createMapperService(fieldMapping(b -> {
minimalMapping(b);

View File

@ -34,8 +34,6 @@ import org.apache.lucene.search.MultiPhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SynonymQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.similarities.BM25Similarity;
import org.apache.lucene.search.similarities.BooleanSimilarity;
import org.apache.lucene.search.spans.FieldMaskingSpanQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
@ -55,9 +53,7 @@ import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MultiMatchQueryBuilder;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.similarity.SimilarityProvider;
import org.elasticsearch.plugins.Plugin;
import org.junit.Before;
import java.io.IOException;
import java.util.ArrayList;
@ -80,20 +76,53 @@ import static org.hamcrest.core.IsInstanceOf.instanceOf;
public class SearchAsYouTypeFieldMapperTests extends FieldMapperTestCase2<SearchAsYouTypeFieldMapper.Builder> {
@Override
protected void writeFieldValue(XContentBuilder builder) throws IOException {
builder.value("new york city");
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("max_shingle_size", b -> b.field("max_shingle_size", 4));
checker.registerConflictCheck("similarity", b -> b.field("similarity", "boolean"));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("analyzer", b -> b.field("analyzer", "keyword"));
checker.registerConflictCheck("index_options", b -> b.field("index_options", "docs"));
checker.registerConflictCheck("term_vector", b -> b.field("term_vector", "yes"));
// norms can be set from true to false, but not vice versa
checker.registerConflictCheck("norms",
fieldMapping(b -> {
b.field("type", "text");
b.field("norms", false);
}),
fieldMapping(b -> {
b.field("type", "text");
b.field("norms", true);
}));
checker.registerUpdateCheck(
b -> {
b.field("type", "search_as_you_type");
b.field("norms", true);
},
b -> {
b.field("type", "search_as_you_type");
b.field("norms", false);
},
m -> assertFalse(m.fieldType().getTextSearchInfo().hasNorms())
);
checker.registerUpdateCheck(b -> {
b.field("analyzer", "default");
b.field("search_analyzer", "keyword");
},
m -> assertEquals("keyword", m.fieldType().getTextSearchInfo().getSearchAnalyzer().name()));
checker.registerUpdateCheck(b -> {
b.field("analyzer", "default");
b.field("search_analyzer", "keyword");
b.field("search_quote_analyzer", "keyword");
},
m -> assertEquals("keyword", m.fieldType().getTextSearchInfo().getSearchQuoteAnalyzer().name()));
}
@Before
public void addModifiers() {
addModifier("max_shingle_size", false, (a, b) -> {
a.maxShingleSize(3);
b.maxShingleSize(2);
});
addModifier("similarity", false, (a, b) -> {
a.similarity(new SimilarityProvider("BM25", new BM25Similarity()));
b.similarity(new SimilarityProvider("boolean", new BooleanSimilarity()));
});
protected void writeFieldValue(XContentBuilder builder) throws IOException {
builder.value("new york city");
}
@Override

View File

@ -578,51 +578,51 @@ public class ICUCollationKeywordFieldMapper extends FieldMapper {
conflicts.add("mapper [" + name() + "] has different [collator]");
}
if (!Objects.equals(rules, icuMergeWith.rules)) {
conflicts.add("Cannot update rules setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [rules] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(language, icuMergeWith.language)) {
conflicts.add("Cannot update language setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [language] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(country, icuMergeWith.country)) {
conflicts.add("Cannot update country setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [country] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(variant, icuMergeWith.variant)) {
conflicts.add("Cannot update variant setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [variant] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(strength, icuMergeWith.strength)) {
conflicts.add("Cannot update strength setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [strength] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(decomposition, icuMergeWith.decomposition)) {
conflicts.add("Cannot update decomposition setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [decomposition] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(alternate, icuMergeWith.alternate)) {
conflicts.add("Cannot update alternate setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [alternate] for [" + CONTENT_TYPE + "]");
}
if (caseLevel != icuMergeWith.caseLevel) {
conflicts.add("Cannot update case_level setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [case_level] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(caseFirst, icuMergeWith.caseFirst)) {
conflicts.add("Cannot update case_first setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [case_first] for [" + CONTENT_TYPE + "]");
}
if (numeric != icuMergeWith.numeric) {
conflicts.add("Cannot update numeric setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [numeric] for [" + CONTENT_TYPE + "]");
}
if (!Objects.equals(variableTop, icuMergeWith.variableTop)) {
conflicts.add("Cannot update variable_top setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [variable_top] for [" + CONTENT_TYPE + "]");
}
if (hiraganaQuaternaryMode != icuMergeWith.hiraganaQuaternaryMode) {
conflicts.add("Cannot update hiragana_quaternary_mode setting for [" + CONTENT_TYPE + "]");
conflicts.add("Cannot update parameter [hiragana_quaternary_mode] for [" + CONTENT_TYPE + "]");
}
this.ignoreAbove = icuMergeWith.ignoreAbove;

View File

@ -34,7 +34,6 @@ import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.plugin.analysis.icu.AnalysisICUPlugin;
import org.elasticsearch.plugins.Plugin;
import org.junit.Before;
import java.io.IOException;
import java.util.Arrays;
@ -63,31 +62,16 @@ public class ICUCollationKeywordFieldMapperTests extends FieldMapperTestCase2<IC
return org.elasticsearch.common.collect.Set.of("analyzer", "similarity");
}
@Before
public void setup() {
addModifier("strength", false, (a, b) -> {
a.strength("primary");
b.strength("secondary");
});
addModifier("decomposition", false, (a, b) -> {
a.decomposition("no");
b.decomposition("canonical");
});
addModifier("alternate", false, (a, b) -> {
a.alternate("shifted");
b.alternate("non-ignorable");
});
addBooleanModifier("case_level", false, ICUCollationKeywordFieldMapper.Builder::caseLevel);
addModifier("case_first", false, (a, b) -> {
a.caseFirst("upper");
a.caseFirst("lower");
});
addBooleanModifier("numeric", false, ICUCollationKeywordFieldMapper.Builder::numeric);
addModifier("variable_top", false, (a, b) -> {
a.variableTop(";");
b.variableTop(":");
});
addBooleanModifier("hiragana_quaternary_mode", false, ICUCollationKeywordFieldMapper.Builder::hiraganaQuaternaryMode);
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("strength", b -> b.field("strength", "secondary"));
checker.registerConflictCheck("decomposition", b -> b.field("decomposition", "canonical"));
checker.registerConflictCheck("alternate", b -> b.field("alternate", "non-ignorable"));
checker.registerConflictCheck("case_level", b -> b.field("case_level", true));
checker.registerConflictCheck("case_first", b -> b.field("case_first", "lower"));
checker.registerConflictCheck("numeric", b -> b.field("numeric", true));
checker.registerConflictCheck("variable_top", b -> b.field("variable_top", ":"));
checker.registerConflictCheck("hiragana_quaternary_mode", b -> b.field("hiragana_quaternary_mode", true));
}
@Override

View File

@ -59,6 +59,11 @@ public class Murmur3FieldMapperTests extends FieldMapperTestCase2<Murmur3FieldMa
b.field("type", "murmur3");
}
@Override
protected void registerParameters(ParameterChecker checker) {
// no parameters to configure
}
public void testDefaults() throws Exception {
DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping));
ParsedDocument parsedDoc = mapper.parse(source(b -> b.field("field", "value")));

View File

@ -134,6 +134,7 @@ public abstract class AbstractPointGeometryFieldMapper<Parsed, Processed> extend
@Override
protected void mergeOptions(FieldMapper other, List<String> conflicts) {
super.mergeOptions(other, conflicts);
AbstractPointGeometryFieldMapper gpfm = (AbstractPointGeometryFieldMapper)other;
// TODO make this un-updateable
if (gpfm.nullValue != null) {

View File

@ -188,6 +188,7 @@ public abstract class AbstractShapeGeometryFieldMapper<Parsed, Processed> extend
@Override
protected final void mergeOptions(FieldMapper other, List<String> conflicts) {
super.mergeOptions(other, conflicts);
AbstractShapeGeometryFieldMapper gsfm = (AbstractShapeGeometryFieldMapper)other;
if (gsfm.coerce.explicit()) {
this.coerce = gsfm.coerce;

View File

@ -137,7 +137,7 @@ public class CompletionFieldMapper extends ParametrizedFieldMapper {
b.startArray(n);
c.toXContent(b, ToXContent.EMPTY_PARAMS);
b.endArray();
}, ContextMappings::toString);
}, Objects::toString);
private final Parameter<Integer> maxInputLength = Parameter.intParam("max_input_length", true,
m -> toType(m).maxInputLength, Defaults.DEFAULT_MAX_INPUT_LENGTH)
.addDeprecatedName("max_input_len")
@ -351,6 +351,10 @@ public class CompletionFieldMapper extends ParametrizedFieldMapper {
return true;
}
int getMaxInputLength() {
return maxInputLength;
}
/**
* Parses and indexes inputs
*

View File

@ -400,7 +400,7 @@ public abstract class FieldMapper extends Mapper implements Cloneable {
conflicts.add("mapper [" + name() + "] has different [norms] values, cannot change from disable to enabled");
}
if (fieldType.storeTermVectors() != other.storeTermVectors()) {
conflicts.add("mapper [" + name() + "] has different [store_term_vector] values");
conflicts.add("mapper [" + name() + "] has different [term_vector] values");
}
if (fieldType.storeTermVectorOffsets() != other.storeTermVectorOffsets()) {
conflicts.add("mapper [" + name() + "] has different [store_term_vector_offsets] values");

View File

@ -345,6 +345,10 @@ public class IpFieldMapper extends ParametrizedFieldMapper {
this.indexCreatedVersion = builder.indexCreatedVersion;
}
boolean ignoreMalformed() {
return ignoreMalformed;
}
@Override
public IpFieldType fieldType() {
return (IpFieldType) super.fieldType();

View File

@ -92,7 +92,8 @@ public final class KeywordFieldMapper extends ParametrizedFieldMapper {
private final Parameter<String> indexOptions
= Parameter.restrictedStringParam("index_options", false, m -> toType(m).indexOptions, "docs", "freqs");
private final Parameter<Boolean> hasNorms
= Parameter.boolParam("norms", false, m -> toType(m).fieldType.omitNorms() == false, false);
= Parameter.boolParam("norms", true, m -> toType(m).fieldType.omitNorms() == false, false)
.setMergeValidator((o, n) -> o == n || (o && n == false)); // norms can be updated from 'true' to 'false' but not vv
private final Parameter<SimilarityProvider> similarity = new Parameter<>("similarity", false, () -> null,
(n, c, o) -> TypeParsers.resolveSimilarity(c, n, o), m -> toType(m).similarity)
.setSerializer((b, f, v) -> b.field(f, v == null ? null : v.name()), v -> v == null ? null : v.name())

View File

@ -1014,6 +1014,14 @@ public class NumberFieldMapper extends ParametrizedFieldMapper {
this.coerceByDefault = builder.coerce.getDefaultValue().value();
}
boolean coerce() {
return coerce.value();
}
boolean ignoreMalformed() {
return ignoreMalformed.value();
}
@Override
public NumberFieldType fieldType() {
return (NumberFieldType) super.fieldType();

View File

@ -144,7 +144,6 @@ public abstract class ParametrizedFieldMapper extends FieldMapper {
private final Supplier<T> defaultValue;
private final TriFunction<String, ParserContext, Object, T> parser;
private final Function<FieldMapper, T> initializer;
private final boolean updateable;
private boolean acceptsNull = false;
private Consumer<T> validator = null;
private Serializer<T> serializer = XContentBuilder::field;
@ -169,7 +168,6 @@ public abstract class ParametrizedFieldMapper extends FieldMapper {
this.value = null;
this.parser = parser;
this.initializer = initializer;
this.updateable = updateable;
this.mergeValidator = (previous, toMerge) -> updateable || Objects.equals(previous, toMerge);
}

View File

@ -274,6 +274,10 @@ public class RangeFieldMapper extends ParametrizedFieldMapper {
this.coerceByDefault = builder.coerce.getDefaultValue().value();
}
boolean coerce() {
return coerce.value();
}
@Override
public ParametrizedFieldMapper.Builder getMergeBuilder() {
return new Builder(simpleName(), type, coerceByDefault).init(this);

View File

@ -47,6 +47,12 @@ public class BinaryFieldMapperTests extends MapperTestCase {
b.field("type", "binary");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", true));
checker.registerConflictCheck("store", b -> b.field("store", true));
}
public void testExistsQueryDocValuesEnabled() throws IOException {
MapperService mapperService = createMapperService(fieldMapping(b -> {
minimalMapping(b);

View File

@ -58,6 +58,14 @@ public class BooleanFieldMapperTests extends MapperTestCase {
assertWarnings("Parameter [boost] on field [field] is deprecated and will be removed in 8.0");
}
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("null_value", b -> b.field("null_value", true));
checker.registerUpdateCheck(b -> b.field("boost", 2.0), m -> assertEquals(m.fieldType().boost(), 2.0, 0));
}
public void testExistsQueryDocValuesDisabled() throws IOException {
MapperService mapperService = createMapperService(fieldMapping(b -> {
minimalMapping(b);

View File

@ -87,6 +87,31 @@ public class CompletionFieldMapperTests extends MapperTestCase {
b.field("max_input_length", 50);
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("analyzer", b -> b.field("analyzer", "standard"));
checker.registerConflictCheck("preserve_separators", b -> b.field("preserve_separators", false));
checker.registerConflictCheck("preserve_position_increments", b -> b.field("preserve_position_increments", false));
checker.registerConflictCheck("contexts", b -> {
b.startArray("contexts");
{
b.startObject();
b.field("name", "place_type");
b.field("type", "category");
b.field("path", "cat");
b.endObject();
}
b.endArray();
});
checker.registerUpdateCheck(b -> b.field("search_analyzer", "standard"),
m -> assertEquals("standard", m.fieldType().getTextSearchInfo().getSearchAnalyzer().name()));
checker.registerUpdateCheck(b -> b.field("max_input_length", 30), m -> {
CompletionFieldMapper cfm = (CompletionFieldMapper) m;
assertEquals(30, cfm.getMaxInputLength());
});
}
@Override
protected IndexAnalyzers createIndexAnalyzers(IndexSettings indexSettings) {
Map<String, NamedAnalyzer> analyzers = new HashMap<>();

View File

@ -54,6 +54,19 @@ public class DateFieldMapperTests extends MapperTestCase {
b.field("type", "date");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("format", b -> b.field("format", "yyyy-MM-dd"));
checker.registerConflictCheck("locale", b -> b.field("locale", "es"));
checker.registerConflictCheck("null_value", b -> b.field("null_value", "34500000"));
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true),
m -> assertTrue(((DateFieldMapper)m).getIgnoreMalformed()));
checker.registerUpdateCheck(b -> b.field("boost", 2.0), m -> assertEquals(m.fieldType().boost(), 2.0, 0));
}
public void testExistsQueryDocValuesDisabled() throws IOException {
MapperService mapperService = createMapperService(fieldMapping(b -> {
minimalMapping(b);

View File

@ -23,6 +23,7 @@ import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetadata;
import org.elasticsearch.common.collect.List;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.geo.GeoUtils;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
@ -57,6 +58,23 @@ public class GeoPointFieldMapperTests extends FieldMapperTestCase2<GeoPointField
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true), m -> {
GeoPointFieldMapper gpfm = (GeoPointFieldMapper) m;
assertTrue(gpfm.ignoreMalformed.value());
});
checker.registerUpdateCheck(b -> b.field("ignore_z_value", false), m -> {
GeoPointFieldMapper gpfm = (GeoPointFieldMapper) m;
assertFalse(gpfm.ignoreZValue.value());
});
GeoPoint point = GeoUtils.parseFromString("41.12,-71.34");
// TODO this should not be updateable!
checker.registerUpdateCheck(b -> b.field("null_value", "41.12,-71.34"), m -> {
GeoPointFieldMapper gpfm = (GeoPointFieldMapper) m;
assertEquals(gpfm.nullValue, point);
});
}
protected void writeFieldValue(XContentBuilder builder) throws IOException {
builder.value(stringEncode(1.3, 1.2));
}

View File

@ -54,6 +54,26 @@ public class GeoShapeFieldMapperTests extends FieldMapperTestCase2<GeoShapeField
return new GeoShapeFieldMapper.Builder("geoshape");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerUpdateCheck(b -> b.field("orientation", "right"), m -> {
GeoShapeFieldMapper gsfm = (GeoShapeFieldMapper) m;
assertEquals(ShapeBuilder.Orientation.RIGHT, gsfm.orientation());
});
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true), m -> {
GeoShapeFieldMapper gpfm = (GeoShapeFieldMapper) m;
assertTrue(gpfm.ignoreMalformed.value());
});
checker.registerUpdateCheck(b -> b.field("ignore_z_value", false), m -> {
GeoShapeFieldMapper gpfm = (GeoShapeFieldMapper) m;
assertFalse(gpfm.ignoreZValue.value());
});
checker.registerUpdateCheck(b -> b.field("coerce", true), m -> {
GeoShapeFieldMapper gpfm = (GeoShapeFieldMapper) m;
assertTrue(gpfm.coerce.value());
});
}
@Before
public void addModifiers() {
addModifier("orientation", true, (a, b) -> {

View File

@ -51,6 +51,16 @@ public class IpFieldMapperTests extends MapperTestCase {
b.field("type", "ip");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("null_value", b -> b.field("null_value", "::1"));
checker.registerUpdateCheck(b -> b.field("ignore_malformed", false),
m -> assertFalse(((IpFieldMapper) m).ignoreMalformed()));
}
public void testExistsQueryDocValuesDisabled() throws IOException {
MapperService mapperService = createMapperService(fieldMapping(b -> {
minimalMapping(b);

View File

@ -164,6 +164,39 @@ public class KeywordFieldMapperTests extends MapperTestCase {
assertWarnings("Parameter [boost] on field [field] is deprecated and will be removed in 8.0");
}
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("index_options", b -> b.field("index_options", "freqs"));
checker.registerConflictCheck("null_value", b -> b.field("null_value", "foo"));
checker.registerConflictCheck("similarity", b -> b.field("similarity", "boolean"));
checker.registerConflictCheck("normalizer", b -> b.field("normalizer", "lowercase"));
checker.registerUpdateCheck(b -> b.field("eager_global_ordinals", true),
m -> assertTrue(m.fieldType().eagerGlobalOrdinals()));
checker.registerUpdateCheck(b -> b.field("ignore_above", 256),
m -> assertEquals(256, ((KeywordFieldMapper)m).ignoreAbove()));
checker.registerUpdateCheck(b -> b.field("split_queries_on_whitespace", true),
m -> assertEquals("_whitespace", m.fieldType().getTextSearchInfo().getSearchAnalyzer().name()));
// norms can be set from true to false, but not vice versa
checker.registerConflictCheck("norms", b -> b.field("norms", true));
checker.registerUpdateCheck(
b -> {
b.field("type", "keyword");
b.field("norms", true);
},
b -> {
b.field("type", "keyword");
b.field("norms", false);
},
m -> assertFalse(m.fieldType().getTextSearchInfo().hasNorms())
);
checker.registerUpdateCheck(b -> b.field("boost", 2.0), m -> assertEquals(m.fieldType().boost(), 2.0, 0));
}
public void testDefaults() throws Exception {
XContentBuilder mapping = fieldMapping(this::minimalMapping);
DocumentMapper mapper = createDocumentMapper(mapping);

View File

@ -40,7 +40,6 @@ import org.elasticsearch.geometry.Point;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.test.TestGeoShapeFieldMapperPlugin;
import org.junit.Before;
import java.io.IOException;
import java.util.Collection;
@ -56,6 +55,7 @@ import static org.hamcrest.Matchers.not;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SuppressWarnings("deprecation")
public class LegacyGeoShapeFieldMapperTests extends FieldMapperTestCase2<LegacyGeoShapeFieldMapper.Builder> {
@Override
@ -73,32 +73,42 @@ public class LegacyGeoShapeFieldMapperTests extends FieldMapperTestCase2<LegacyG
return org.elasticsearch.common.collect.Set.of("analyzer", "similarity", "doc_values", "store");
}
@Before
public void addModifiers() {
addModifier("tree", false, (a, b) -> {
a.deprecatedParameters.tree = "geohash";
b.deprecatedParameters.tree = "quadtree";
@Override
protected void minimalMapping(XContentBuilder b) throws IOException {
b.field("type", "geo_shape").field("strategy", "recursive");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("strategy",
fieldMapping(this::minimalMapping),
fieldMapping(b -> {
b.field("type", "geo_shape");
b.field("strategy", "term");
}));
checker.registerConflictCheck("tree", b -> b.field("tree", "geohash"));
checker.registerConflictCheck("tree_levels", b -> b.field("tree_levels", 5));
checker.registerConflictCheck("precision", b -> b.field("precision", 10));
checker.registerUpdateCheck(b -> b.field("orientation", "right"), m -> {
LegacyGeoShapeFieldMapper gsfm = (LegacyGeoShapeFieldMapper) m;
assertEquals(ShapeBuilder.Orientation.RIGHT, gsfm.orientation());
});
addModifier("strategy", false, (a, b) -> {
a.deprecatedParameters.strategy = SpatialStrategy.TERM;
b.deprecatedParameters.strategy = SpatialStrategy.RECURSIVE;
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true), m -> {
LegacyGeoShapeFieldMapper gpfm = (LegacyGeoShapeFieldMapper) m;
assertTrue(gpfm.ignoreMalformed.value());
});
addModifier("tree_levels", false, (a, b) -> {
a.deprecatedParameters.treeLevels = 2;
b.deprecatedParameters.treeLevels = 3;
checker.registerUpdateCheck(b -> b.field("ignore_z_value", false), m -> {
LegacyGeoShapeFieldMapper gpfm = (LegacyGeoShapeFieldMapper) m;
assertFalse(gpfm.ignoreZValue.value());
});
addModifier("precision", false, (a, b) -> {
a.deprecatedParameters.precision = "10";
b.deprecatedParameters.precision = "20";
});
addModifier("distance_error_pct", true, (a, b) -> {
a.deprecatedParameters.distanceErrorPct = 0.5;
b.deprecatedParameters.distanceErrorPct = 0.6;
});
addModifier("orientation", true, (a, b) -> {
a.orientation = ShapeBuilder.Orientation.RIGHT;
b.orientation = ShapeBuilder.Orientation.LEFT;
checker.registerUpdateCheck(b -> b.field("coerce", true), m -> {
LegacyGeoShapeFieldMapper gpfm = (LegacyGeoShapeFieldMapper) m;
assertTrue(gpfm.coerce.value());
});
// TODO - distance_error_pct ends up being subsumed into a calculated value, how to test
checker.registerUpdateCheck(b -> b.field("distance_error_pct", 0.8), m -> {});
}
@Override
@ -106,11 +116,6 @@ public class LegacyGeoShapeFieldMapperTests extends FieldMapperTestCase2<LegacyG
return List.of(new TestGeoShapeFieldMapperPlugin());
}
@Override
protected void minimalMapping(XContentBuilder b) throws IOException {
b.field("type", "geo_shape").field("strategy", "recursive");
}
@Override
protected boolean supportsMeta() {
return false;
@ -601,13 +606,18 @@ public class LegacyGeoShapeFieldMapperTests extends FieldMapperTestCase2<LegacyG
}
@Override
protected void assertSerializationWarnings() {
protected void assertParseMaximalWarnings() {
assertWarnings("Field parameter [strategy] is deprecated and will be removed in a future version.",
"Field parameter [tree] is deprecated and will be removed in a future version.",
"Field parameter [tree_levels] is deprecated and will be removed in a future version.",
"Field parameter [precision] is deprecated and will be removed in a future version.",
"Field parameter [distance_error_pct] is deprecated and will be removed in a future version."
);
);
}
@Override
protected void assertSerializationWarnings() {
assertParseMinimalWarnings();
}
public void testGeoShapeArrayParsing() throws Exception {

View File

@ -59,6 +59,17 @@ public class NumberFieldMapperTests extends AbstractNumericFieldMapperTestCase {
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("null_value", b -> b.field("null_value", 1));
checker.registerUpdateCheck(b -> b.field("coerce", false),
m -> assertFalse(((NumberFieldMapper) m).coerce()));
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true),
m -> assertTrue(((NumberFieldMapper) m).ignoreMalformed()));
}
protected void writeFieldValue(XContentBuilder builder) throws IOException {
builder.value(123);
}

View File

@ -99,6 +99,15 @@ public class RangeFieldMapperTests extends AbstractNumericFieldMapperTestCase {
assertWarnings("Parameter [boost] on field [field] is deprecated and will be removed in 8.0");
}
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerConflictCheck("doc_values", b -> b.field("doc_values", false));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerUpdateCheck(b -> b.field("coerce", false),
m -> assertFalse(((RangeFieldMapper)m).coerce()));
checker.registerUpdateCheck(b -> b.field("boost", 2.0), m -> assertEquals(m.fieldType().boost(), 2.0, 0));
}
private Object getFrom(String type) {
if (type.equals("date_range")) {
return FROM_DATE;

View File

@ -45,8 +45,6 @@ import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SynonymQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.similarities.BM25Similarity;
import org.apache.lucene.search.similarities.BooleanSimilarity;
import org.apache.lucene.search.spans.FieldMaskingSpanQuery;
import org.apache.lucene.search.spans.SpanNearQuery;
import org.apache.lucene.search.spans.SpanOrQuery;
@ -73,8 +71,6 @@ import org.elasticsearch.index.query.MatchPhrasePrefixQueryBuilder;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.index.search.MatchQuery;
import org.elasticsearch.index.similarity.SimilarityProvider;
import org.junit.Before;
import java.io.IOException;
import java.util.Arrays;
@ -132,36 +128,76 @@ public class TextFieldMapperTests extends FieldMapperTestCase2<TextFieldMapper.B
.searchAnalyzer(new NamedAnalyzer("standard", AnalyzerScope.INDEX, new StandardAnalyzer()));
}
@Before
public void addModifiers() {
addBooleanModifier("fielddata", true, TextFieldMapper.Builder::fielddata);
addModifier("fielddata_frequency_filter.min", true, (a, b) -> {
a.fielddataFrequencyFilter(1, 10, 10);
a.fielddataFrequencyFilter(2, 10, 10);
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerUpdateCheck(b -> b.field("fielddata", true), m -> {
TextFieldType ft = (TextFieldType) m.fieldType();
assertTrue(ft.fielddata());
});
addModifier("fielddata_frequency_filter.max", true, (a, b) -> {
a.fielddataFrequencyFilter(1, 10, 10);
a.fielddataFrequencyFilter(1, 12, 10);
});
addModifier("fielddata_frequency_filter.min_segment_size", true, (a, b) -> {
a.fielddataFrequencyFilter(1, 10, 10);
a.fielddataFrequencyFilter(1, 10, 11);
});
addModifier("index_phrases", false, (a, b) -> {
a.indexPhrases(true);
b.indexPhrases(false);
});
addModifier("index_prefixes", false, (a, b) -> {
a.indexPrefixes(2, 4);
});
addModifier("index_options", false, (a, b) -> {
a.indexOptions(IndexOptions.DOCS_AND_FREQS);
b.indexOptions(IndexOptions.DOCS_AND_FREQS_AND_POSITIONS);
});
addModifier("similarity", false, (a, b) -> {
a.similarity(new SimilarityProvider("BM25", new BM25Similarity()));
b.similarity(new SimilarityProvider("boolean", new BooleanSimilarity()));
checker.registerUpdateCheck(b -> {
b.field("fielddata", true);
b.startObject("fielddata_frequency_filter");
{
b.field("min", 10);
b.field("max", 20);
b.field("min_segment_size", 100);
}
b.endObject();
}, m -> {
TextFieldType ft = (TextFieldType) m.fieldType();
assertEquals(10, ft.fielddataMinFrequency(), 0);
assertEquals(20, ft.fielddataMaxFrequency(), 0);
assertEquals(100, ft.fielddataMinSegmentSize());
});
checker.registerUpdateCheck(b -> b.field("eager_global_ordinals", "true"),
m -> assertTrue(m.fieldType().eagerGlobalOrdinals()));
checker.registerUpdateCheck(b -> {
b.field("analyzer", "default");
b.field("search_analyzer", "keyword");
},
m -> assertEquals("keyword", m.fieldType().getTextSearchInfo().getSearchAnalyzer().name()));
checker.registerUpdateCheck(b -> {
b.field("analyzer", "default");
b.field("search_analyzer", "keyword");
b.field("search_quote_analyzer", "keyword");
},
m -> assertEquals("keyword", m.fieldType().getTextSearchInfo().getSearchQuoteAnalyzer().name()));
checker.registerConflictCheck("index", b -> b.field("index", false));
checker.registerConflictCheck("store", b -> b.field("store", true));
checker.registerConflictCheck("index_phrases", b -> b.field("index_phrases", true));
checker.registerConflictCheck("index_prefixes", b -> b.startObject("index_prefixes").endObject());
checker.registerConflictCheck("index_options", b -> b.field("index_options", "docs"));
checker.registerConflictCheck("similarity", b -> b.field("similarity", "boolean"));
checker.registerConflictCheck("analyzer", b -> b.field("analyzer", "keyword"));
checker.registerConflictCheck("term_vector", b -> b.field("term_vector", "yes"));
// TODO position_increment_gap should not be updateable!
//checker.registerConflictCheck("position_increment_gap", b -> b.field("position_increment_gap", 10));
// norms can be set from true to false, but not vice versa
checker.registerConflictCheck("norms",
fieldMapping(b -> {
b.field("type", "text");
b.field("norms", false);
}),
fieldMapping(b -> {
b.field("type", "text");
b.field("norms", true);
}));
checker.registerUpdateCheck(
b -> {
b.field("type", "text");
b.field("norms", true);
},
b -> {
b.field("type", "text");
b.field("norms", false);
},
m -> assertFalse(m.fieldType().getTextSearchInfo().hasNorms())
);
}
@Override

View File

@ -30,6 +30,7 @@ import org.apache.lucene.search.NormsFieldExistsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.util.SetOnce;
import org.elasticsearch.common.CheckedConsumer;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.xcontent.ToXContent;
@ -45,9 +46,13 @@ import org.elasticsearch.search.lookup.SearchLookup;
import org.elasticsearch.search.lookup.SourceLookup;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import static org.hamcrest.Matchers.anyOf;
@ -100,7 +105,7 @@ public abstract class MapperTestCase extends MapperServiceTestCase {
protected void assertExistsQuery(MappedFieldType fieldType, Query query, ParseContext.Document fields) {
if (fieldType.hasDocValues()) {
assertThat(query, instanceOf(DocValuesFieldExistsQuery.class));
DocValuesFieldExistsQuery fieldExistsQuery = (DocValuesFieldExistsQuery)query;
DocValuesFieldExistsQuery fieldExistsQuery = (DocValuesFieldExistsQuery) query;
assertEquals("field", fieldExistsQuery.getField());
assertDocValuesField(fields, "field");
assertNoFieldNamesField(fields);
@ -283,7 +288,9 @@ public abstract class MapperTestCase extends MapperServiceTestCase {
throws IOException {
BiFunction<MappedFieldType, Supplier<SearchLookup>, IndexFieldData<?>> fieldDataLookup = (mft, lookupSource) -> mft
.fielddataBuilder("test", () -> { throw new UnsupportedOperationException(); })
.fielddataBuilder("test", () -> {
throw new UnsupportedOperationException();
})
.build(new IndexFieldDataCache.None(), new NoneCircuitBreakerService(), mapperService);
SetOnce<List<?>> result = new SetOnce<>();
withLuceneIndex(mapperService, iw -> {
@ -299,4 +306,127 @@ public abstract class MapperTestCase extends MapperServiceTestCase {
});
return result.get();
}
private class UpdateCheck {
final XContentBuilder init;
final XContentBuilder update;
final Consumer<FieldMapper> check;
private UpdateCheck(CheckedConsumer<XContentBuilder, IOException> update,
Consumer<FieldMapper> check) throws IOException {
this.init = fieldMapping(MapperTestCase.this::minimalMapping);
this.update = fieldMapping(b -> {
minimalMapping(b);
update.accept(b);
});
this.check = check;
}
private UpdateCheck(CheckedConsumer<XContentBuilder, IOException> init,
CheckedConsumer<XContentBuilder, IOException> update,
Consumer<FieldMapper> check) throws IOException {
this.init = fieldMapping(init);
this.update = fieldMapping(update);
this.check = check;
}
}
private static class ConflictCheck {
final XContentBuilder init;
final XContentBuilder update;
private ConflictCheck(XContentBuilder init, XContentBuilder update) {
this.init = init;
this.update = update;
}
}
public class ParameterChecker {
List<UpdateCheck> updateChecks = new ArrayList<>();
Map<String, ConflictCheck> conflictChecks = new HashMap<>();
/**
* Register a check that a parameter can be updated, using the minimal mapping as a base
*
* @param update a field builder applied on top of the minimal mapping
* @param check a check that the updated parameter has been applied to the FieldMapper
*/
public void registerUpdateCheck(CheckedConsumer<XContentBuilder, IOException> update,
Consumer<FieldMapper> check) throws IOException {
updateChecks.add(new UpdateCheck(update, check));
}
/**
* Register a check that a parameter can be updated
*
* @param init the initial mapping
* @param update the updated mapping
* @param check a check that the updated parameter has been applied to the FieldMapper
*/
public void registerUpdateCheck(CheckedConsumer<XContentBuilder, IOException> init,
CheckedConsumer<XContentBuilder, IOException> update,
Consumer<FieldMapper> check) throws IOException {
updateChecks.add(new UpdateCheck(init, update, check));
}
/**
* Register a check that a parameter update will cause a conflict, using the minimal mapping as a base
*
* @param param the parameter name, expected to appear in the error message
* @param update a field builder applied on top of the minimal mapping
*/
public void registerConflictCheck(String param, CheckedConsumer<XContentBuilder, IOException> update) throws IOException {
conflictChecks.put(param, new ConflictCheck(
fieldMapping(MapperTestCase.this::minimalMapping),
fieldMapping(b -> {
minimalMapping(b);
update.accept(b);
})
));
}
/**
* Register a check that a parameter update will cause a conflict
*
* @param param the parameter name, expected to appear in the error message
* @param init the initial mapping
* @param update the updated mapping
*/
public void registerConflictCheck(String param, XContentBuilder init, XContentBuilder update) {
conflictChecks.put(param, new ConflictCheck(init, update));
}
}
protected abstract void registerParameters(ParameterChecker checker) throws IOException;
public void testUpdates() throws IOException {
ParameterChecker checker = new ParameterChecker();
registerParameters(checker);
for (UpdateCheck updateCheck : checker.updateChecks) {
MapperService mapperService = createMapperService(updateCheck.init);
merge(mapperService, updateCheck.update);
FieldMapper mapper = (FieldMapper) mapperService.documentMapper().mappers().getMapper("field");
updateCheck.check.accept(mapper);
// do it again to ensure that we don't get conflicts the second time
merge(mapperService, updateCheck.update);
mapper = (FieldMapper) mapperService.documentMapper().mappers().getMapper("field");
updateCheck.check.accept(mapper);
}
for (String param : checker.conflictChecks.keySet()) {
MapperService mapperService = createMapperService(checker.conflictChecks.get(param).init);
// merging the same change is fine
merge(mapperService, checker.conflictChecks.get(param).init);
// merging the conflicting update should throw an exception
Exception e = expectThrows(IllegalArgumentException.class,
"No conflict when updating parameter [" + param + "]",
() -> merge(mapperService, checker.conflictChecks.get(param).update));
assertThat(e.getMessage(), anyOf(
containsString("Cannot update parameter [" + param + "]"),
containsString("different [" + param + "]")));
}
assertParseMaximalWarnings();
}
}

View File

@ -148,6 +148,10 @@ public class HistogramFieldMapper extends FieldMapper {
this.ignoreMalformed = ignoreMalformed;
}
boolean ignoreMalformed() {
return ignoreMalformed.value();
}
@Override
protected void mergeOptions(FieldMapper other, List<String> conflicts) {
HistogramFieldMapper gpfmMergeWith = (HistogramFieldMapper) other;

View File

@ -51,6 +51,12 @@ public class HistogramFieldMapperTests extends FieldMapperTestCase2<HistogramFie
b.field("type", "histogram");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerUpdateCheck(b -> b.field("ignore_malformed", true),
m -> assertTrue(((HistogramFieldMapper)m).ignoreMalformed()));
}
public void testParseValue() throws Exception {
DocumentMapper mapper = createDocumentMapper(fieldMapping(this::minimalMapping));
ParsedDocument doc = mapper.parse(

View File

@ -25,6 +25,7 @@ import org.elasticsearch.index.mapper.ValueFetcher;
import org.elasticsearch.plugins.Plugin;
import org.elasticsearch.search.lookup.SourceLookup;
import org.elasticsearch.xpack.constantkeyword.ConstantKeywordMapperPlugin;
import org.elasticsearch.xpack.constantkeyword.mapper.ConstantKeywordFieldMapper.ConstantKeywordFieldType;
import java.io.IOException;
import java.util.Collection;
@ -120,8 +121,8 @@ public class ConstantKeywordFieldMapperTests extends MapperTestCase {
b.field("type", "constant_keyword");
b.field("value", 74);
}));
ConstantKeywordFieldMapper.ConstantKeywordFieldType ft
= (ConstantKeywordFieldMapper.ConstantKeywordFieldType) mapperService.fieldType("field");
ConstantKeywordFieldType ft
= (ConstantKeywordFieldType) mapperService.fieldType("field");
assertEquals("74", ft.value());
}
@ -145,6 +146,23 @@ public class ConstantKeywordFieldMapperTests extends MapperTestCase {
b.field("type", "constant_keyword");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
checker.registerUpdateCheck(b -> b.field("value", "foo"), m -> {
ConstantKeywordFieldType ft = (ConstantKeywordFieldType) m.fieldType();
assertEquals("foo", ft.value());
});
checker.registerConflictCheck("value",
fieldMapping(b -> {
b.field("type", "constant_keyword");
b.field("value", "foo");
}),
fieldMapping(b -> {
b.field("type", "constant_keyword");
b.field("value", "bar");
}));
}
public void testFetchValue() throws Exception {
MapperService mapperService = createMapperService(fieldMapping(b -> b.field("type", "constant_keyword")));
FieldMapper fieldMapper = (FieldMapper) mapperService.documentMapper().mappers().getMapper("field");

View File

@ -50,6 +50,11 @@ public class VersionStringFieldMapperTests extends MapperTestCase {
builder.value("1.2.3");
}
@Override
protected void registerParameters(ParameterChecker checker) throws IOException {
// no configurable parameters
}
public void testDefaults() throws Exception {
XContentBuilder mapping = fieldMapping(this::minimalMapping);
DocumentMapper mapper = createDocumentMapper(mapping);

View File

@ -60,6 +60,14 @@ public final class RuntimeFieldMapper extends ParametrizedFieldMapper {
this.scriptCompiler = scriptCompiler;
}
String runtimeType() {
return runtimeType;
}
Script script() {
return script;
}
@Override
public ParametrizedFieldMapper.Builder getMergeBuilder() {
return new RuntimeFieldMapper.Builder(simpleName(), scriptCompiler).init(this);

View File

@ -76,6 +76,11 @@ public class RuntimeFieldMapperTests extends MapperTestCase {
b.startObject("script").field("source", "dummy_source").field("lang", "test").endObject();
}
@Override
protected void registerParameters(ParameterChecker checker) {
// TODO need to be able to pass a completely new config rather than updating minimal mapping
}
public void testRuntimeTypeIsRequired() throws Exception {
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()