Case insensitive exact match search in Elasticsearch
I had a requirement where I needed to do exact match search in ElasticSearch. After searching a bit I landed to this page of ElasticSearch documentation. I was thrilled that I got the solution quickly (thanking god and ElasticSearch team in my mind). Hence I started testing by creating an index using request below:
1
2
3
4
5
6
7
8
9
10
11
12
13
PUT localhost:9200/products/
{
"mappings": {
"product": {
"properties": {
"productName": {
"type": "string",
"index": "not_analyzed"
}
}
}
}
}
Then I added some data using request below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
POST localhost:9200/products/
{
"productName": "Green Ball"
}
POST localhost:9200/products/
{
"productName": "Red Ball"
}
POST localhost:9200/products/
{
"productName": "Yellow Ball"
}
Now it was time for testing and hence I fired following request:
1
2
3
4
5
6
7
8
9
10
11
12
POST localhost:9200/products/product/_search
{
"query":{
"constant_score":{
"filter":{
"term":{
"productName":"green ball"
}
}
}
}
}
And I was presented with following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"took":0,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"skipped":0,
"failed":0
},
"hits":{
"total":0,
"max_score":null,
"hits":[]
}
}
I was surprised (and frustrated at the same time) as the data was there in ElasticSearch, still it was not returning the data. Soon enough I found that it was because the search was case sensitive and searching with “Green Ball” returned result. This was unacceptable in my use case as I needed case insensitive search. After searching a little bit more, I found that I have to create an analyzer with lowercase
filter and use this to analyze the field on which I want to perform search. Hence I deleted the index and recreated it as below:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
DELETE localhost:9200/products/
PUT localhost:9200/products/
{
"settings":{
"index":{
"analysis":{
"analyzer":{
"analyzer_case_insensitive":{
"tokenizer":"keyword",
"filter":"lowercase"
}
}
}
}
},
"mappings":{
"product":{
"properties":{
"productName":{
"type":"string",
"analyzer":"analyzer_case_insensitive"
}
}
}
}
}
I needed to switch to match
query as term
query would return result with only lower case search text. Below is the modified query:
1
2
3
4
5
6
7
8
POST localhost:9200/products/product/_search
{
"query":{
"match":{
"productName":"greeN ball"
}
}
}
And this resulted in following:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"took":0,
"timed_out":false,
"_shards":{
"total":5,
"successful":5,
"skipped":0,
"failed":0
},
"hits":{
"total":1,
"max_score":0.6931472,
"hits":[
{
"_index":"products",
"_type":"product",
"_id":"AV57L6vzqKOq3BnmUAzd",
"_score":0.6931472,
"_source":{
"productName":"Green Ball"
}
}
]
}
}
I was happy with the result as it served my purpose. Let me know in comments if there is a better way to achieve this.