41

I put 2 documents in Elasticsearch :

curl -XPUT "http://localhost:9200/vehicles/vehicle/1" -d'
{
    "model": "Classe A"
}'

curl -XPUT "http://localhost:9200/vehicles/vehicle/2" -d'
{
    "model": "Classe B"
}'

Why is this query returns the 2 documents :

curl -XPOST "http://localhost:9200/vehicles/_search" -d'
{
  "query": {
    "query_string": {
      "query": "model:\"Classe A\""
    }
  }
}'

And this one, only the second document :

curl -XPOST "http://localhost:9200/vehicles/_search" -d'
{
  "query": {
    "query_string": {
      "query": "model:\"Classe B\""
    }
  }
}'

I want elastic search to match on the exact phrase I pass to the query parameter, WITH the whitespace, how can I do that ?

Molochdaa
  • 2,158
  • 1
  • 17
  • 23

5 Answers5

40

What you need to look at is the analyzer you're using. If you don't specify one Elasticsearch will use the Standard Analyzer. It is great for the majority of cases with plain text input, but doesn't work for the use case you mention.

What the standard analyzer will do is split the words in your string and then converts them to lowercase.

If you want to match the whole string "Classe A" and distinguish this from "Classe B", you can use the Keyword Analyzer. This will keep the entire field as one string.

Then you can use the match query which will return the results you expect.

Create the mapping:

PUT vehicles
{
  "mappings": {
    "vehicle": {
      "properties": {
        "model": {
          "type": "string",
          "analyzer": "keyword"
        }
      }
    }
  }
}

Perform the query:

POST vehicles/_search
{
  "query": {
    "match": {
      "model": "Classe A"
    }
  }
}

If you wanted to use the query_string query, then you could set the operator to AND

POST vehicles/vehicle/_search
{
  "query": {
    "query_string": {
      "query": "Classe B",
      "default_operator": "AND"
    }
  }
}
Akshay
  • 3,361
  • 1
  • 21
  • 19
32

Additionally, you can use query_string and escape the quotes will also return an exact phrase:

POST _search
{
    "query": {
      "query_string": {
        "query": "\"Classe A\""
     }
}
Eric
  • 369
  • 4
  • 5
  • 1
    Works great in 2020. Yet another option ES docs suggest is [Simple Query String](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-simple-query-string-query.html). – m01010011 Nov 13 '20 at 00:15
  • will it works too with lowercase? like `classe a`? – Guillaume Cisco Mar 23 '23 at 10:10
10

use match phrase query as mentioned below

GET /company/employee/_search   
{     
    "query" : {      
        "match_phrase" : {      
            "about" : "rock climbing"      
        }      
    }      
}
Nikhil Kumar K
  • 1,089
  • 10
  • 13
  • 6
    this query also returns "super rock climbing", "rock climbing and java", any phrase that contains "rock climbing" – yaroslavTir Mar 07 '19 at 10:41
  • 2
    @yaroslavTir queries don't return strings, they return documents that match queries. In this case, all those documents matched the phrase query "rock climbing" as they contained that substring. – aaa90210 Mar 22 '19 at 05:11
5

Seems like in the latest versions of ES you can just use .keyword

POST vehicles/_search
{
  "query": {
    "term": {
      "model.keyword": "Classe A"
    }
  }
}

It will match exactly the string "Classe A"

Dynamic fields determined by ES as text will have a subfield 'keyword', very useful for this cases: https://www.elastic.co/guide/en/elasticsearch/reference/current/dynamic-field-mapping.html

Richard
  • 719
  • 10
  • 11
2

Another nice solution would be using match and minimum_should_match(providing the percentage of the words you want to match). It can be 100% and will return the results containing at least the given text;

It is important that this approach is NOT considering the order of the words.

"query":{
  "bool":{
     "should":[
        {
           "match":{
              "my_text":{
                 "query":"I want to buy a new new car",
                 "minimum_should_match":"90%"
              }
           }
        }
     ]
  }
}
Radu Linu
  • 1,143
  • 13
  • 29