Search code examples
searchelasticsearchmappingsettingspartial

Elasticsearch | copy_to with partial searching


I have copy_to working properly for exact matches but I am unable to properly set it up with partial matches. Below is my mappings/settings and query with expected and actual results.

settings:

{
   "test": {
      "settings": {
         "index": {
            "analysis": {
               "filter": {
                  "ngram_filter": {
                     "type": "edge_ngram",
                     "min_gram": "1",
                     "max_gram": "15"
                  }
               },
               "analyzer": {
                  "ngram_analyzer": {
                     "filter": [
                        "lowercase",
                        "ngram_filter"
                     ],
                     "type": "custom",
                     "tokenizer": "standard"
                  }
               }
            },
            "number_of_shards": "1",
            "number_of_replicas": "1",
           }
      }
   }
}

mappings:

POST /test/_mapping/name
{
   "name": {
      "properties": {
         "vital": {
            "properties": {
               "first": {
                  "type": "string",
                  "copy_to": "full_name",
                   "term_vector": "yes",
                    "analyzer": "ngram_analyzer",
                    "search_analyzer": "standard"
               },
               "last": {
                  "type": "string",
                  "copy_to": "full_name",
                   "term_vector": "yes",
                    "analyzer": "ngram_analyzer",
                    "search_analyzer": "standard"
               },
               "full_name": {
                  "type": "string",
                   "term_vector": "yes",
                    "analyzer": "ngram_analyzer",
                    "search_analyzer": "standard"
               }
            }
         }
      }
   }
}

POST:

POST /test/name 
{
   "vital": {
      "first": "Tom",
      "last": "Doe"
   }
}

Now when I do the search ...

GET /test/name/_search
{
  "query": {
    "match": {
      "full_name": { 
        "query": "Tom Doe",
        "operator": "and"
      }
    }
  }
}

... I get back the result!! Hurrraaaay, but if I do the search ....

GET /test/name/_search
{
  "query": {
    "match": {
      "full_name": { 
        "query": "Tom Do",
        "operator": "and"
      }
    }
  }
}

... I get back no result :( I would like partial matching to work for full_name as well. As another not I am successfully able to due partial matching on first and last name. It is just full_name that is not working. How would I go about this?


Solution

  • You have one little mistake in your mapping, you need to copy the first and last names into the vital.full_name field, not just full_name, otherwise that will create a new string field named full_name with a standard analyzer at the top-level of your mapping (if you run GET test you'll see that new field in your mapping):

    POST /test/_mapping/name
    {
       "name": {
          "properties": {
             "vital": {
                "properties": {
                   "first": {
                      "type": "string",
                      "copy_to": "vital.full_name",      <--- fix this
                       "term_vector": "yes",
                        "analyzer": "ngram_analyzer",
                        "search_analyzer": "standard"
                   },
                   "last": {
                      "type": "string",
                      "copy_to": "vital.full_name",      <--- fix this
                       "term_vector": "yes",
                        "analyzer": "ngram_analyzer",
                        "search_analyzer": "standard"
                   },
                   "full_name": {
                      "type": "string",
                       "term_vector": "yes",
                        "analyzer": "ngram_analyzer",
                        "search_analyzer": "standard"
                   }
                }
             }
          }
       }
    }
    

    And then fix your queries like this:

    POST /test/name/_search
    {
      "query": {
        "match": {
          "vital.full_name": {          <-- fix this
            "query": "Tom Doe",
            "operator": "and"
          }
        }
      }
    }
    
    POST /test/name/_search
    {
      "query": {
        "match": {
          "vital.full_name": {          <-- fix this
            "query": "Tom Do",
            "operator": "and"
          }
        }
      }
    }
    

    Both will work as you expect.