Mapping a field type as Keyword results in 'java.lang.IllegalStateException: Mixing up field types' error

I'm pulling some basic data into ElasticSearch via LogStash from an ER database (data on support tickets). I'm still learning about this so configuring mappings for the first time. I have fields which I'm sure should be used as "keywords" (such as "Product" which contain text data on fields we'll need to search and filter by). However the data only gets pulled in successfully if I use the "text" data type (which I don't believe is what I should be using here).

Here is the template I'm using which I use curl to upload, and also use curl to check it gets applied:

{
    "template" : "logstash-asdf*",
	"mappings" : {
		"asdf" : {
			"properties" : {
				<<<other fields I'm not listing here since irrelevant>>>
				"product_family":{"type":"keyword"}
			}
		}
	}
}

In ElasticSearch I get the following error for every record which LogStash attempts to pass to ElasticSearch (I've removed some fields and data between <<< >>> in the below snippet, I've also trimmed some of the error stack of messages with ...... because I was hitting the character limit to post this):

[[logstash-asdf-2017.03.07][2]] containing [index {[logstash-asdf-2017.03.07][logs][156251819], source[{<<<removed listing of other fields>>>,"product_family":"SampleProductName","@timestamp":"2017-03-07T08:33:05.000Z","@version":"1","ticket_id":156251819,}]}]
org.elasticsearch.index.mapper.MapperParsingException: failed to parse
        at org.elasticsearch.index.mapper.DocumentParser.wrapInMapperParsingException(DocumentParser.java:176) ~[elasticsearch-5.4.0.jar:5.4.0]
        at org.elasticsearch.index.mapper.DocumentParser.parseDocument(DocumentParser.java:69) ~[elasticsearch-5.4.0.jar:5.4.0]
        at org.elasticsearch.index.mapper.DocumentMapper.parse(DocumentMapper.java:277) ~[elasticsearch-5.4.0.jar:5.4.0]
        at org.elasticsearch.index.shard.IndexShard.prepareIndex(IndexShard.java:536) ~[elasticsearch-5.4.0.jar:5.4.0]
        at org.elasticsearch.index.shard.IndexShard.prepareIndexOnPrimary(IndexShard.java:513) ~[elasticsearch-5.4.0.jar:5.4.0]
        ..........
Caused by: java.lang.IllegalStateException: Mixing up field types: class org.elasticsearch.index.mapper.TextFieldMapper$TextFieldType != class org.elasticsearch.index.mapper.KeywordFieldMapper$KeywordFieldType on field product_family
        at org.elasticsearch.index.mapper.FieldMapper.updateFieldType(FieldMapper.java:366) ~[elasticsearch-5.4.0.jar:5.4.0]
        at org.elasticsearch.index.mapper.FieldMapper.updateFieldType(FieldMapper.java:49) ~[elasticsearch-5.4.0.jar:5.4.0]
        ..........

I can't seem to find many pointers which seem relevant to my problem when I search on this error, and the "keyword" datatype seems relatively straightforward I am not sure if there are some additional properties I need to configure along with this. Any guidance appreciated, happy to share any further details to assist I've just tried to share the basics which I think are relevant here. Thanks for any help in advance.

Edit: Also here is the output when I query the templates to check the mapping is correctly applied (again I've redacted the irrelevant fields which aren't related to this issue):

# curl -XGET http://server:9200/_template/asdf*
{"asdf_template_1":{"order":0,"template":"logstash-asdf*","settings":{},"mappings":{"asdf":{"properties":{<<<removed some fields>>>,"product_family":{"type":"keyword"}}}},"aliases":{}}}

Edit2: Looking at the error messages, it looks like it is complaining about trying to apply the type as "keyword" when it already thinks it is "text" but I am not sure how/why it is getting it as text. Could this be something to do with dynamic type mapping which happens by default and do I need to do something with that? I've looked at all the templates and can't find any conflicts but I'm not familiar enough with how the mapping works to know what to check next.

I still can't work this out if anyone here can offer any kind of guidance at all would be much appreciated. :frowning:

If I go through the logs for a particular failed index attempt I see the following in case this is helpful at all:

// This is ElasticSearch getting the request from LogStash to index the event
[2017-06-22T12:10:01,293][INFO ][o.e.c.m.MetaDataCreateIndexService] [KQrW4py] [logstash-bems-2017.06.05] creating index, cause [auto(bulk api)], templates [bems_template_1, logstash], shards [5]/[1], mappings [_default_, bems]

// It fails with error: "java.lang.IllegalStateException: Mixing up field types: class org.elasticsearch.index.mapper.TextFieldMapper$TextFieldType != class org.elasticsearch.index.mapper.KeywordFieldMapper$KeywordFieldType on field product_family" (the value from the database is a string, with a template I set this to type "keyword")
[2017-06-22T12:10:01,733][DEBUG][o.e.a.b.TransportShardBulkAction] [KQrW4py] [logstash-bems-2017.06.05][2] failed to execute bulk item (index) BulkShardRequest [[logstash-bems-2017.06.05][2]] containing [index {[logstash-bems-2017.06.05][logs][176586722], source[{"product_family":"asdf","closed_date":"2017-06-07T23:54:18","product":"asdf","component":"asdf","@timestamp":"2017-06-05T09:58:20.000Z","case_title":"asdf","@version":"1","engagement_id":176586722,"case_number":"1234","create_date":"2017-06-05T09:58:20","last_updated_date":"2017-06-07T23:54:18","business_unit":"asdf"}]}]
org.elasticsearch.index.mapper.MapperParsingException: failed to parse
...
Caused by: java.lang.IllegalStateException: Mixing up field types: class org.elasticsearch.index.mapper.TextFieldMapper$TextFieldType != class org.elasticsearch.index.mapper.KeywordFieldMapper$KeywordFieldType on field product_family


// In the request I lookde at above it mentions "templates [bems_template_1, logstash]" so I ran the following to take a closer look...
Run following command:
# curl -XGET http://server:9200/_template/*
// This is the template which I explictly added to map the data types, specifically focusing on "product_family" here trying to test "keyword" data type which fails.
"bems_template_1":{
	"order":0,
	"template":"logstash-bems*",
	"settings":{},
	"mappings":{
		"bems":{"properties":
			{"engagament_id":{"type":"integer"},
			"product_family":{"type":"keyword"},
			"closed_date":{"type":"date"},
			"product":{"type":"text"},
			"component":{"type":"text"},
			"case_title":{"type":"text"},
			"case_number":{"type":"text"},
			"create_date":{"type":"date"},
			"last_updated_date":{"type":"date"},
			"business_unit":{"type":"text"}}
		}
	},
	"aliases":{}
},

// It also seems to mention this "logstash" template (I'm not quite sure where this came from if it was dynamically added or something else):
"logstash":{
	"order":0,"version":50001,
	"template":"logstash-*",
	"settings":{"index":{"refresh_interval":"5s"}},
	"mappings":{
		"_default_":{
			"dynamic_templates":[
				{"message_field":{
					"path_match":"message",
					"mapping":{"norms":false,"type":"text"},
					"match_mapping_type":"string"}
				},
				{"string_fields":{
					"mapping":{"norms":false,"type":"text","fields":{"keyword":{"type":"keyword"}}},
					"match_mapping_type":"string",
					"match":"*"
				}}
			],
			"_all":{"norms":false,"enabled":true},
			"properties":{
				"@timestamp":{"include_in_all":false,"type":"date"},
				"geoip":{"dynamic":true,"properties":{"ip":{"type":"ip"},"latitude":{"type":"half_float"},"location":{"type":"geo_point"},"longitude":{"type":"half_float"}}},
				"@version":{"include_in_all":false,"type":"keyword"}
			}
		}
	},
	"aliases":{}
},

// In the request I looked at above it also mentions "mappings [_default_, bems]" so from the below curl I extracted the mapping for "logstash-bems-2017.06.05"
// Let's see if we can work out what this part means in the original request: "mappings [_default_, bems]"

# curl -XGET http://server:9200/_mapping/*

"logstash-bems-2017.06.05":
{
	"mappings":
	{
		"bems":
		{
			"_all":{"enabled":true,"norms":false},
			"dynamic_templates":
			[
				{
					"message_field":
					{
						"path_match":"message",
						"match_mapping_type":"string",
						"mapping":{"norms":false,"type":"text"}
					}
				},
				{
					"string_fields":
					{
						"match":"*",
						"match_mapping_type":"string",
						"mapping":{"fields":{"keyword":{"type":"keyword"}},"norms":false,"type":"text"}
					}
				}
			],
			"properties":
			{
				"@timestamp":{"type":"date","include_in_all":false},
				"@version":{"type":"keyword","include_in_all":false},
				"business_unit":{"type":"text"},
				"case_number":{"type":"text"},
				"case_title":{"type":"text"},
				"closed_date":{"type":"date"},
				"component":{"type":"text"},
				"create_date":{"type":"date"},
				"engagament_id":{"type":"integer"},
				"geoip":{"dynamic":"true","properties":{"ip":{"type":"ip"},"latitude":{"type":"half_float"},"location":{"type":"geo_point"},"longitude":{"type":"half_float"}}},
				"last_updated_date":{"type":"date"},
				"product":{"type":"text"},
				"product_family":{"type":"keyword"}
			}
		},
			
		"_default_":
		{
			"_all":{"enabled":true,"norms":false},
			"dynamic_templates":[
				{"message_field":{"path_match":"message","match_mapping_type":"string","mapping":{"norms":false,"type":"text"}}},
				{"string_fields":{"match":"*","match_mapping_type":"string","mapping":{"fields":{"keyword":{"type":"keyword"}},"norms":false,"type":"text"}}}
			],
			"properties":
			{
				"@timestamp":{"type":"date","include_in_all":false},
				"@version":{"type":"keyword","include_in_all":false},
				"geoip":{"dynamic":"true","properties":{"ip":{"type":"ip"},"latitude":{"type":"half_float"},"location":{"type":"geo_point"},"longitude":{"type":"half_float"}}}
			}
		}
	}
}

I also was wondering if anything with the dynamic templates being enabled by default was conflicting here, so I tried the following as a test to try and rule this out by using the following template then deleting and re-importing the data through LogStash but the results in the logs were the same.

{
    "template" : "logstash-bems*",
	"version" : 1,
	"mappings" : {
		"bems" : {
			"dynamic": "strict",
			"properties" : {
				"case_number":{"type":"text"},
				"engagament_id":{"type":"integer"},
				"case_title":{"type":"text"},
				"create_date":{"type":"date"},
				"closed_date":{"type":"date"},
				"business_unit":{"type":"text"},
				"product_family":{"type":"keyword"},
				"product":{"type":"text"},
				"component":{"type":"text"},
				"last_updated_date":{"type":"date"}
			}
		}
	}
}

I did the following very simple test to try and successfully map a keyword field and it worked, so now I can see the difference between working and non-working:

// Create basic test template in file test.template:
{
    "template" : "test*",
	"mappings" : {
		"test" : {
			"dynamic": "strict",
			"properties" : {
				"keyword_value" : {"type" : "keyword"}
			}
		}
	}
}

// Add the template into ElasticSearch:
# curl -XPUT http://server:9200/_template/test1 -H "Content-Type: application/json" -d @test.template

// Create basic test index in file test.post:
{
	"keyword_value" : "keywordstring"
}

// Post the test index to ElasticSearch:
# curl -XPOST http://server:9200/test-1/test -H "Content-Type: application/json" -d @test.post

// From ElasticSearch logs I see the following not followed by any errors:
[2017-06-22T16:18:29,768][INFO ][o.e.c.m.MetaDataCreateIndexService] [KQrW4py] [test-1] creating index, cause [auto(bulk api)], templates [test1], shards [5]/[1], mappings [test]

So in my simple test it only maps to a single template [test1] and a single mapping [test] where in my failed example above it's mapping to templates [bems_template_1, logstash] and mappings [default, bems].

I'm not sure where the "logstash" and "_default_" came into play, is this lingering from dynamically created templates and conflicting or something? This is the only difference I can see between working and non-working so I think I'm close here but if anyone can point me in the right direction would be appreciated, thanks.

I ended up resolving this by working up from my simple working example above until I got the failure. In the end it was another silly mistake but thought I would update here in case someone else runs into something similar because I couldn't find much relevant on the original error. :frowning:

As I'm new to ElasticSearch, I had overlooked the very basic concept of "type". Documents stored in an index are also classified by type, so you can have multiple types but since my use case is simple I was only using one type so overlooked this configuration. So you could have:
Index1 -> Type1 -> DocumentA
Index1 -> Type2 -> DocumentB

I had no "type" configuration in my LogStash configuration file under ElasticSearch output plugin, so it assigns a default type of "logs". When I was creating my mapping template I didn't realise that the type under "mappings" needs to match. So basically my template wasn't being assigned to my documents as expected (instead it was using dynamic templates) causing the problem.

Specifically this is the two changes which fixed the problem:

# In my Logstash configuration I added the following to assign my documents to a specified type "my_type"
output {
	elasticsearch {
		document_type => "my_type"
	}
}

# In my ElasticSearch template mapping I used this same type under the "mappings":
{
	"template" : "logstash-*"	<-- This means this template applies to any indexes matching "logstash*"
	"mappings" : {
		"my_type": {	<-- This means this mapping applies to documents classified as type "my_type"
			"properties" : {
				"product_family" : {"type" : "keyword" }	< -- Then a list of my data type mappings etc
			}
		}
	}
}

Once I realised this and correctly had my documents filed by a particular type, and the template mapping matching this type, all the data type mappings were correctly assigned. A bit embarrassed taking this long to end up getting to the bottom of this seemingly simple issue but nice to have this resolved. I hope my bumbling troubleshooting and my lesson here might be useful to someone in the future! :slight_smile:

1 Like

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.