Mappings: Lock down _field_names field

Now that we have an explicit `enabled` flag, we can lock down
the field type so it is not mungeable.

see #8143
closes #9912
This commit is contained in:
Ryan Ernst 2015-02-26 13:47:53 -08:00
parent 7181bbde26
commit 3b7928d568
8 changed files with 32 additions and 18 deletions

View File

@ -256,6 +256,7 @@ to provide special features. They now have limited configuration options.
* `_index` configuration is limited to enabling the field.
* `_routing` configuration is limited to requiring the field.
* `_boost` has been removed.
* `_field_names` configuration is limited to disabling the field.
=== Codecs

View File

@ -89,6 +89,7 @@ public class FieldNamesFieldMapper extends AbstractFieldMapper<String> implement
}
@Override
@Deprecated
public Builder index(boolean index) {
enabled(index);
return super.index(index);
@ -113,7 +114,9 @@ public class FieldNamesFieldMapper extends AbstractFieldMapper<String> implement
}
FieldNamesFieldMapper.Builder builder = fieldNames();
parseField(builder, builder.name, node, parserContext);
if (parserContext.indexVersionCreated().before(Version.V_2_0_0)) {
parseField(builder, builder.name, node, parserContext);
}
for (Iterator<Map.Entry<String, Object>> iterator = node.entrySet().iterator(); iterator.hasNext();) {
Map.Entry<String, Object> entry = iterator.next();
@ -260,7 +263,7 @@ public class FieldNamesFieldMapper extends AbstractFieldMapper<String> implement
if (includeDefaults || enabledState != Defaults.ENABLED_STATE) {
builder.field("enabled", enabledState.enabled);
}
if (includeDefaults || fieldType().equals(Defaults.FIELD_TYPE) == false) {
if (writePre2xSettings && (includeDefaults || fieldType().equals(Defaults.FIELD_TYPE) == false)) {
super.doXContentBody(builder, includeDefaults, params);
}

View File

@ -28,6 +28,7 @@ import org.elasticsearch.common.lucene.HashedBytesRef;
import org.elasticsearch.common.lucene.search.Queries;
import org.elasticsearch.common.lucene.search.XBooleanFilter;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.mapper.FieldMapper;
import org.elasticsearch.index.mapper.FieldMappers;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.internal.FieldNamesFieldMapper;
@ -82,7 +83,8 @@ public class ExistsFilterParser implements FilterParser {
}
public static Filter newFilter(QueryParseContext parseContext, String fieldPattern, String filterName) {
final FieldMappers fieldNamesMapper = parseContext.mapperService().indexName(FieldNamesFieldMapper.CONTENT_TYPE);
final FieldMappers fieldNamesMappers = parseContext.mapperService().indexName(FieldNamesFieldMapper.NAME);
final FieldNamesFieldMapper fieldNamesMapper = (FieldNamesFieldMapper)fieldNamesMappers.mapper();
MapperService.SmartNameObjectMapper smartNameObjectMapper = parseContext.smartObjectMapper(fieldPattern);
if (smartNameObjectMapper != null && smartNameObjectMapper.hasMapper()) {
@ -104,14 +106,14 @@ public class ExistsFilterParser implements FilterParser {
nonNullFieldMappers = smartNameFieldMappers;
}
Filter filter = null;
if (fieldNamesMapper!= null && fieldNamesMapper.mapper().fieldType().indexOptions() != IndexOptions.NONE) {
if (fieldNamesMapper!= null && fieldNamesMapper.enabled()) {
final String f;
if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
f = smartNameFieldMappers.mapper().names().indexName();
} else {
f = field;
}
filter = fieldNamesMapper.mapper().termFilter(f, parseContext);
filter = fieldNamesMapper.termFilter(f, parseContext);
}
// if _field_names are not indexed, we need to go the slow way
if (filter == null && smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {

View File

@ -95,7 +95,8 @@ public class MissingFilterParser implements FilterParser {
throw new QueryParsingException(parseContext.index(), "missing must have either existence, or null_value, or both set to true");
}
final FieldMappers fieldNamesMapper = parseContext.mapperService().indexName(FieldNamesFieldMapper.NAME);
final FieldMappers fieldNamesMappers = parseContext.mapperService().indexName(FieldNamesFieldMapper.NAME);
final FieldNamesFieldMapper fieldNamesMapper = (FieldNamesFieldMapper)fieldNamesMappers.mapper();
MapperService.SmartNameObjectMapper smartNameObjectMapper = parseContext.smartObjectMapper(fieldPattern);
if (smartNameObjectMapper != null && smartNameObjectMapper.hasMapper()) {
// automatic make the object mapper pattern
@ -124,14 +125,14 @@ public class MissingFilterParser implements FilterParser {
nonNullFieldMappers = smartNameFieldMappers;
}
Filter filter = null;
if (fieldNamesMapper != null && fieldNamesMapper.mapper().fieldType().indexOptions() != IndexOptions.NONE) {
if (fieldNamesMapper != null && fieldNamesMapper.enabled()) {
final String f;
if (smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {
f = smartNameFieldMappers.mapper().names().indexName();
} else {
f = field;
}
filter = fieldNamesMapper.mapper().termFilter(f, parseContext);
filter = fieldNamesMapper.termFilter(f, parseContext);
}
// if _field_names are not indexed, we need to go the slow way
if (filter == null && smartNameFieldMappers != null && smartNameFieldMappers.hasMapper()) {

View File

@ -1144,16 +1144,20 @@ public class GetActionTests extends ElasticsearchIntegrationTest {
@Test
public void testGeneratedStringFieldsStored() throws IOException {
indexSingleDocumentWithStringFieldsGeneratedFromText(true, randomBoolean());
String[] fieldsList = {"_all", "_field_names"};
String[] fieldsList = {"_all"};
String[] alwaysNotStoredFieldsList = {"_field_names"};
// before refresh - document is only in translog
assertGetFieldsNull(indexOrAlias(), "doc", "1", fieldsList);
assertGetFieldsException(indexOrAlias(), "doc", "1", fieldsList);
assertGetFieldsNull(indexOrAlias(), "doc", "1", alwaysNotStoredFieldsList);
refresh();
//after refresh - document is in translog and also indexed
assertGetFieldsAlwaysWorks(indexOrAlias(), "doc", "1", fieldsList);
assertGetFieldsNull(indexOrAlias(), "doc", "1", alwaysNotStoredFieldsList);
flush();
//after flush - document is in not anymore translog - only indexed
assertGetFieldsAlwaysWorks(indexOrAlias(), "doc", "1", fieldsList);
assertGetFieldsNull(indexOrAlias(), "doc", "1", alwaysNotStoredFieldsList);
}
void indexSingleDocumentWithStringFieldsGeneratedFromText(boolean stored, boolean sourceEnabled) {
@ -1167,8 +1171,7 @@ public class GetActionTests extends ElasticsearchIntegrationTest {
" \"mappings\": {\n" +
" \"doc\": {\n" +
" \"_source\" : {\"enabled\" : " + sourceEnabled + "}," +
" \"_all\" : {\"enabled\" : true, \"store\":\"" + storedString + "\" }," +
" \"_field_names\" : {\"store\":\"" + storedString + "\" }" +
" \"_all\" : {\"enabled\" : true, \"store\":\"" + storedString + "\" }" +
" }\n" +
" }\n" +
"}";

View File

@ -19,6 +19,10 @@
package org.elasticsearch.index.mapper.internal;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.ImmutableSettings;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.mapper.DocumentMapper;
import org.elasticsearch.index.mapper.DocumentMapperParser;
@ -114,7 +118,8 @@ public class FieldNamesFieldMapperTests extends ElasticsearchSingleNodeTest {
.startObject("_field_names").field("index", "no").endObject()
.endObject().endObject().string();
DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping);
Settings indexSettings = ImmutableSettings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_4_2.id).build();
DocumentMapper docMapper = createIndex("test", indexSettings).mapperService().documentMapperParser().parse(mapping);
FieldNamesFieldMapper fieldNamesMapper = docMapper.rootMapper(FieldNamesFieldMapper.class);
assertFalse(fieldNamesMapper.enabled());
@ -127,12 +132,13 @@ public class FieldNamesFieldMapperTests extends ElasticsearchSingleNodeTest {
assertNull(doc.rootDoc().get("_field_names"));
}
public void testFieldTypeSettings() throws Exception {
public void testFieldTypeSettingsBackcompat() throws Exception {
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("_field_names").field("store", "yes").endObject()
.endObject().endObject().string();
DocumentMapper docMapper = createIndex("test").mapperService().documentMapperParser().parse(mapping);
Settings indexSettings = ImmutableSettings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.V_1_4_2.id).build();
DocumentMapper docMapper = createIndex("test", indexSettings).mapperService().documentMapperParser().parse(mapping);
FieldNamesFieldMapper fieldNamesMapper = docMapper.rootMapper(FieldNamesFieldMapper.class);
assertTrue(fieldNamesMapper.fieldType().stored());
}

View File

@ -45,9 +45,7 @@ public class ExistsMissingTests extends ElasticsearchIntegrationTest {
.startObject()
.startObject("type")
.startObject(FieldNamesFieldMapper.NAME)
// by setting randomly index to no we also test the pre-1.3 behavior
.field("index", randomFrom("no", "not_analyzed"))
.field("store", randomFrom("no", "yes"))
.field("enabled", randomBoolean())
.endObject()
.startObject("properties")
.startObject("foo")

View File

@ -335,7 +335,7 @@ public abstract class ElasticsearchIntegrationTest extends ElasticsearchTestCase
.field("compress", randomBoolean())
.endObject();
}
if (compatibilityVersion().onOrAfter(Version.V_1_3_0)) {
if (compatibilityVersion().onOrAfter(Version.V_1_3_0) && compatibilityVersion().before(Version.V_2_0_0)) {
// some tests rely on this BWC version behavior that we wanna keep
mappings.startObject(FieldNamesFieldMapper.NAME)
.startObject("fielddata")