Automatically upgrade analyzed strings with an analyzer to `text`. #17861

I had stayed away from it until now since the `analyzer` property is supported
on analyzed strings but not on analyzed strings. So th list of supported
properties for the upgrade has been splitted so that we would still fail the
upgrade if setting an analyzer on a not-analyzed string field.

See https://discuss.elastic.co/t/es5-0a1-the-string-type-is-removed-in-5-0-you-should-now-use-either-a-text-or-keyword-field-instead-for-field/47305/7
This commit is contained in:
Adrien Grand 2016-04-19 17:55:10 +02:00
parent 6921712847
commit f245fc1ccd
2 changed files with 37 additions and 7 deletions

View File

@ -70,11 +70,16 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
// If a string field is created on 5.x and all parameters are in this list then we
// will automatically upgrade to a text/keyword field. Otherwise we will just fail
// saying that string fields are not supported anymore.
private static final Set<String> SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE = new HashSet<>(Arrays.asList(
private static final Set<String> SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE_TO_KEYWORD = new HashSet<>(Arrays.asList(
"type",
// most common parameters, for which the upgrade is straightforward
// common keyword parameters, for which the upgrade is straightforward
"index", "store", "doc_values", "omit_norms", "norms", "fields", "copy_to",
"fielddata", "ignore_above"));
private static final Set<String> SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE_TO_TEXT = new HashSet<>(Arrays.asList(
"type",
// common text parameters, for which the upgrade is straightforward
"index", "store", "doc_values", "omit_norms", "norms", "fields", "copy_to",
"fielddata", "analyzer", "search_analyzer", "search_quote_analyzer"));
public static class Defaults {
public static double FIELDDATA_MIN_FREQUENCY = 0;
@ -198,12 +203,16 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
@Override
public Mapper.Builder parse(String fieldName, Map<String, Object> node, ParserContext parserContext) throws MapperParsingException {
if (parserContext.indexVersionCreated().onOrAfter(Version.V_5_0_0_alpha1)) {
final Object index = node.get("index");
final boolean keyword = index != null && "analyzed".equals(index) == false;
// Automatically upgrade simple mappings for ease of upgrade, otherwise fail
if (SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE.containsAll(node.keySet())) {
Set<String> autoUpgradeParameters = keyword
? SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE_TO_KEYWORD
: SUPPORTED_PARAMETERS_FOR_AUTO_UPGRADE_TO_TEXT;
if (autoUpgradeParameters.containsAll(node.keySet())) {
deprecationLogger.deprecated("The [string] field is deprecated, please use [text] or [keyword] instead on [{}]",
fieldName);
final Object index = node.remove("index");
final boolean keyword = index != null && "analyzed".equals(index) == false;
{
// upgrade the index setting
node.put("index", "no".equals(index) == false);
@ -254,7 +263,7 @@ public class StringFieldMapper extends FieldMapper implements AllFieldMapper.Inc
throw new IllegalArgumentException("The [string] type is removed in 5.0. You should now use either a [text] "
+ "or [keyword] field instead for field [" + fieldName + "]");
}
StringFieldMapper.Builder builder = new StringFieldMapper.Builder(fieldName);
// hack for the fact that string can't just accept true/false for
// the index property and still accepts no/not_analyzed/analyzed

View File

@ -107,7 +107,8 @@ public class StringMappingUpgradeTests extends ESSingleNodeTestCase {
IndexService indexService = createIndex("test");
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "string").field("analyzer", "keyword").endObject().endObject()
.startObject("properties").startObject("field").field("type", "string")
.field("index", "not_analyzed").field("analyzer", "keyword").endObject().endObject()
.endObject().endObject().string();
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
() -> parser.parse("type", new CompressedXContent(mapping)));
@ -164,6 +165,23 @@ public class StringMappingUpgradeTests extends ESSingleNodeTestCase {
assertEquals(200, ((KeywordFieldMapper) field).ignoreAbove());
}
public void testUpgradeAnalyzer() throws IOException {
IndexService indexService = createIndex("test");
DocumentMapperParser parser = indexService.mapperService().documentMapperParser();
String mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
.startObject("properties").startObject("field").field("type", "string")
.field("analyzer", "standard")
.field("search_analyzer", "whitespace")
.field("search_quote_analyzer", "keyword").endObject().endObject()
.endObject().endObject().string();
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping));
FieldMapper field = mapper.mappers().getMapper("field");
assertThat(field, instanceOf(TextFieldMapper.class));
assertEquals("standard", field.fieldType().indexAnalyzer().name());
assertEquals("whitespace", field.fieldType().searchAnalyzer().name());
assertEquals("keyword", field.fieldType().searchQuoteAnalyzer().name());
}
public void testUpgradeRandomMapping() throws IOException {
final int iters = 20;
for (int i = 0; i < iters; ++i) {
@ -200,6 +218,9 @@ public class StringMappingUpgradeTests extends ESSingleNodeTestCase {
if (keyword && randomBoolean()) {
mapping.field("doc_values", randomBoolean());
}
if (keyword == false && randomBoolean()) {
mapping.field("analyzer", "keyword");
}
if (randomBoolean()) {
hasNorms = randomBoolean();
if (randomBoolean()) {