Bool query with whole and partial words matching

I want to index contacts containing a first name and last name and be able to search against them by typing either whole word or just the beginning of word. The whole words should be rank higher.
My mapping looks like this:

    PUT example
    {
      "settings": {
        "analysis": {
          "filter": {
            "edge_ngram_filter": {
              "type": "edge_ngram",
              "min_gram": 1,
              "max_gram": 50
            }
          },
          "analyzer": {
            "edge_ngram_analyzer": {
              "type": "custom",
              "tokenizer": "standard",
              "filter": [
                "lowercase",
                "edge_ngram_filter"
              ]
            }
          }
        }
      },
      "mappings": {
        "contact": {
          "properties": {
            "FirstName": {
              "type": "string",
              "analyzer": "edge_ngram_analyzer",
              "fields": {
                "raw": {
                  "type": "string",
                  "analyzer": "standard"
                }
              }
            },
            "LastName": {
              "type": "string",
              "analyzer": "edge_ngram_analyzer",
              "fields": {
                "raw": {
                  "type": "string",
                  "analyzer": "standard"
                }
              }
            }
          }
        }
      }
    }

And my query like this:

POST example/contact/_search
{
   "query": {
      "bool": {
         "should": [
            {
               "multi_match": {
                  "query": "kim w",
                  "fields": [
                     "*.raw"
                  ]
               }
            },
            {
               "multi_match": {
                  "type": "cross_fields",
                  "query": "kim w",
                  "fields": [
                     "FirstName",
                     "LastName"
                  ]
               }
            }
         ]
      }
   }
}

It does not work quite good because for a query "k w" it returns "kim west" and "william kennedy" which is ok.
But for the query "kim w" it returns "krista porter" which is not good.
So I need a query which, when two words are entered, must match both on them on two fields.

[deleted because it did not work]

You need to specify a query analyser that is different to your index analyser. E.g a "standard" query analyser.

Sorry, the correct term is "search analyser":

https://www.elastic.co/guide/en/elasticsearch/reference/current/search-analyzer.html

You can specify the search analyser at index time (as described in the link above), or you can specify it at query time as described here:

https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-match-query.html#query-dsl-match-query-phrase

Thanks for suggestion,
here is my working solution:
I changed should to must clause and used AND operator in edge-ngram part so every term in the query must match. I also use standard analyzer so the query Joh match John but not Jimmy.
The raw fields part of the query is still in should clause so it increases full words score.

POST example/contact/_search
{
   "query": {
      "bool": {
         "must": [
            {
               "multi_match": {
                  "type": "cross_fields",
                  "query": "W Kim",
                  "fields": [
                     "FirstName",
                     "LastName"
                  ],
                  "operator": "and",
                  "analyzer": "standard"
               }
            }
         ],
         "should": [
            {
               "multi_match": {
                  "query": "W Kim",
                  "fields": [
                     "*.raw"
                  ],
                  "type": "cross_fields"
               }
            }
         ]
      }
   }
}
1 Like