How to Get Elasticsearch to Perform an Exact Match Query

Exact match in elastic search query

As stated here: Finding Exact Values, since the field has been analyzed when indexed - you have no way of exact-matching its tokens (":"). Whenever the tokens should be searchable the mapping should be "not_analyzed" and the data needs to be re-indexed.

If you want to be able to easily match only ":feed:" inside the message field you might want to costumize an analyzer which doesn't tokenize ":" so you will be able to query the field with a simple "match" query instead of wild characters.

How to get elasticsearch to perform an exact match query?

If you have your source stored (which is the default) you can use a script filter

It should go something like this:

$ curl -XPUT localhost:9200/index/type/1 -d '{"foo": "bar"}'
$ curl -XPUT localhost:9200/index/type/2 -d '{"foo": "bar baz"}'
$ curl -XPOST localhost:9200/index/type/_search?pretty=true -d '{
"filter": {
"script": {
"script": "_source.foo == \"bar\""
}
}
}'
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 5,
"successful" : 5,
"failed" : 0
},
"hits" : {
"total" : 1,
"max_score" : 1.0,
"hits" : [ {
"_index" : "index",
"_type" : "type",
"_id" : "1",
"_score" : 1.0, "_source" : {"foo": "bar"}
} ]
}
}

EDIT: I think it's worth mentioning that the "not_analyzed" mapping should be the faster approach. But if you want both exact and partial matches for this field, I see two options: use scripts or index the data twice (once analyzed, once not analyzed).

How to implement an exact match in a filter with elasticsearch?

Update : As OP mentioned in the comment that he is using 2.4, I am updating my solution to include the solution which works for it.

ES 2.4 solution

Index creation with required settings and mappings

{
"settings": {
"analysis": {
"analyzer": {
"lckeyword": {
"filter": [
"lowercase"
],
"tokenizer": "keyword"
}
}
}
},
"mappings": {
"so": {
"properties": {
"state": {
"type": "string"
},
"city": {
"type": "string"
},
"colony": {
"type": "string"
},
"state_raw": {
"type": "string",
"analyzer": "lckeyword"
}
}
}
}
}

Search query

{
"query": {
"filtered": {
"query": {
"bool": {
"should": [
{
"match": {
"state": {
"query": "michoacán de ocampo"
}
}
},
{
"match": {
"colony": {
"query": "zamora"
}
}
},
{
"match": {
"city": {
"query": "zamora"
}
}
}
]
}
},
"filter": {
"term": {
"state_raw": "michoacán de ocampo"
}
}
}
}
}

An important thing to note here is creating a custom analyzer(keyword with lowercase filter), so that field on which we are creating filter stored as it is but with small letter, as that is what you are passing in your query. Now above query returns you both your document, this is the postman collection that has index creation, sample docs creation and query which return both docs returned.

ES 7.X solution

The issue is that you are defining your state field as text field and then in your filter, you are using [term][1] query which is not analyzed as explained in official ES doc.

Returns documents that contain an exact term in a provided field.

Hence it would try to find token `Michoacán de Ocampo` in inverted index which isn't present as state field is defined as text and generates 3 tokens `michoacán`, `de` and `ocampo` and ES works on token(search term) to token(inverted index) match. You can check these tokens with [analyze API][2] and can use [explain API][3] to see the tokens generated by ES when the query has results

Fix
---
Define `state` field as a [multi-field][4] and store it as it is(kwyword form) so that you can filter on it.

{
"mappings": {
"properties": {
"state": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
},
"city": {
"type": "text"
},
"colony": {
"type": "text"
}
}
}
}

Now below query would give you both results.

{
"query": {
"bool": {
"must": [
{
"match": {
"state": {
"query": "michoacán de ocampo"
}
}
},
{
"match": {
"colony": {
"query": "zamora"
}
}
},
{
"match": {
"city": {
"query": "zamora"
}
}
}
],
"filter": {
"term": {
"state.raw": "Michoacán de Ocampo" -->notice .raw to search on keyword field.
}
}
}
}
}

EDIT: - https://www.getpostman.com/collections/f4b9ed00d50e2f4bc7f4 is the postman collection link if you want to quickly test it.

Elasticsearch query not giving exact match

Instead of match you have to use term query, as the documentation describe:

The term query finds documents that contain the exact term specified in the inverted index

So you have to change your query as follow:

get items/_search
{
"query" : {
"term" : {
"code.keyword" : "7000-8900"
}
}
}

If you don't get any result there are two possibilities:

  • The term searched is not what you think it really is (for example is not trimmed)
  • The index has no explicit mapping and the automatic mapping didn't recognize the field code as a string.

Note: if the mapping is correct and code is a term field it is possible to use "code". If the mapping was automatic and the mapping recognize it as text you need to use "code.keyword"

How to search query by field with exact match in Elasticsearch

You need to use term query with keyword like this

{
"query" : {
"term" : {
"field.keyword" : "test"
}
}
}

hope this helps.

Elasticsearch - How to list exact match at the top

If you want your exact matches to be on top, you could use a keyword field in your mapping.

{
"settings": {
"analysis": {
"filter": {
"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 3,
"max_gram": 15
}
},
"analyzer": {
"autocomplete": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"autocomplete_filter"
]
}
}
}
},
"mappings": {
"_doc": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword"
}
},
"analyzer": "autocomplete",
"search_analyzer": "standard"
}
}
}
}
}

Then you can have a bool query that uses your keyword in the should query.

{
"query": {
"bool": {
"must": [
{
"match": {
"name": "Adana"
}
}
],
"should": [
{
"term": {
"name.keyword": {
"value": "Adana"
}
}
}
]
}
}
}

That should push the exact match to the top.

Elasticsearch query to find exact match on string field ( Without analyzing )

The mapping influences how your data gets stored and how you can query that field. Your category field (named name)field is configured to be used for auto-complete and not for exact-match search. So you have to change the mapping in order to support exact match-search.

However, you can do so without changing the current field, but simply by adding an additional field by converting your field to a multi-field and adding a field that supports exact-match queries. Mapping snippet:

"name": {
"type": "string",
"analyzer": "autocomplete",
"search_analyzer": "standard",
"fields": {
"raw": {
"type" "string",
"index": "not_analyzed"
}
}
}
}

Note: I noticed that you are on an older version of Elasticsearch (the type "string" got replaced with types "text" (for full-text-search) and "keyword" (for exact match search). I tried to come up with a mapping that works with older versions of Elasticsearch and hope it works. If not you need to provide the version of Elasticsearch you are using.

After fixing the mapping, you need to ensure that the field is also populated. You can do so, by getting everything re-indexed, by executing the following request:

POST <your_index_name>/_update_by_query

In your query you would then need to search as follows (for exact-match):

"match": {
"category.name.raw": "Arts and Culture"
}

For partial searches you would continue using the category.name-field (without suffix raw)

how to do exact match in elastic search?

The problem is because your sysid field is of type text and is thus analyzed at indexing time and tokenized into fcf0dd4e, abbc, ec11, 8943 and 000d3a15f525.

Also when searching using a match query, the searched term is analyzed as well into the following tokens 6a8c283f, 1e75, ec11, 8943, 000d3a15f525.

As you saw, the match works because ec11 matches.

For an exact match, what you need to do is to run your query against a keyword field instead. Maybe you're lucky and your mapping already contains a sysid.keyword sub-field. If not, you need to change your mapping and reindex your data.

Then you'll be able to run your term query like this and get your exact match:

  "term": {
"sysid.keyword":sysid
}


Related Topics



Leave a reply



Submit