# Custom score for fuzzy matching based on Levenshtein distance score

**URL:** <https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544>\
**Category:** Elasticsearch\
**Created:** [March 26, 2018, 5:43am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544 "2018-03-26T05:43:37Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![maarab](https://avatars.discourse-cdn.com/v4/letter/m/9fc29f/32.png) [@maarab](https://discuss.elastic.co/u/maarab)\
**Post date:** [March 26, 2018, 5:43am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/1 "2018-03-26T05:43:37Z")

</div>

I have a requirement where there needs to be custom scoring on name field based on the Levenshtein distance and not the score returned by elastic search  
e.g  
Smith vs Smith = 100%,  
Smith vs Smithh = 90%,  
Smith vs SSmithhh = 60%

Has someone done this? How?  
As an idea, I think native scripting would be required using some elasticsearch property that gives the distance score between two terms but I'm not sure how it can be done.

---

<div class="post-metadata">

**Author:** ![abdon](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/abdon/32/9195_2.png) [@abdon](https://discuss.elastic.co/u/abdon)\
**Post date:** [March 28, 2018, 8:59am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/2 "2018-03-28T08:59:45Z")

</div>

The [match query accepts a `fuzziness` parameter](https://www.elastic.co/guide/en/elasticsearch/guide/current/fuzzy-match-query.html) which allows you to do fuzzy matching based on the Damerau-Levenshtein edit distance (see the explanation for the fuzziness parameter in our docs [here](https://www.elastic.co/guide/en/elasticsearch/guide/current/fuzziness.html)).

Now, two things to keep in mind with `fuzziness`: the maximum edit distance that Elasticsearch supports is 2, so `Smith` and `SSmithhh` will never be a match. And, the default scoring is not quite how you want it to be.

However, if a maximum edit distance of 2 is enough for your use case, then you can use a combination of `bool` and `constant_score` queries to get to the scoring you want. The idea is that we look for all documents that match with an edit distance of 2, and assign a score of 80 to those documents. Then, if those documents also match with an edit distance of 1, increase the score by 10 points. And if those documents also match with an edit distance of 0 - assign another 10 points to get to a perfect score of 100.

So, let's say these are your documents:

```auto
PUT levtest/_doc/_bulk
{ "index" : { "_id": 1 } }
{ "name": "Smith" }
{ "index" : { "_id": 2 } }
{ "name": "Smithh" }
{ "index" : { "_id": 3 } }
{ "name": "SSmithh" }

```

Then your query could look like this:

```auto
GET levtest/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "constant_score": {
            "filter": {
              "match": {
                "name": {
                  "query": "Smith",
                  "fuzziness": 2
                }
              }
            },
            "boost": 80
          }
        },
        {
          "constant_score": {
            "filter": {
              "match": {
                "name": {
                  "query": "Smith",
                  "fuzziness": 1
                }
              }
            },
            "boost": 10
          }
        },
        {
          "constant_score": {
            "filter": {
              "match": {
                "name": {
                  "query": "Smith"
                }
              }
            },
            "boost": 10
          }
        }
      ]
    }
  }
}

```

Returning:

```auto
    "hits": [
      {
        "_index": "levtest",
        "_type": "_doc",
        "_id": "1",
        "_score": 100,
        "_source": {
          "name": "Smith"
        }
      },
      {
        "_index": "levtest",
        "_type": "_doc",
        "_id": "2",
        "_score": 90,
        "_source": {
          "name": "Smithh"
        }
      },
      {
        "_index": "levtest",
        "_type": "_doc",
        "_id": "3",
        "_score": 80,
        "_source": {
          "name": "SSmithh"
        }
      }
    ]

```

A warning though: queries with a `"fuzziness": 2` are computationally expensive.

---

<div class="post-metadata">

**Author:** ![maarab](https://avatars.discourse-cdn.com/v4/letter/m/9fc29f/32.png) [@maarab](https://discuss.elastic.co/u/maarab)\
**Post date:** [March 28, 2018, 10:14am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/3 "2018-03-28T10:14:38Z")

</div>

Thank you for your reply. It does solve my problem partially. To elaborate a bit on my question, when I do a edit distance similarity between two strings in SQL(oracle) I get a certain score e.g.  
select utl\_match.edit\_distance\_similarity('Smith','Smith') from dual;  
Score: 100

select utl\_match.edit\_distance\_similarity('Smith','Smithh') from dual;  
Score: 84

select utl\_match.edit\_distance\_similarity('Smith','SSmithhh') from dual;  
Score: 63

Is this something doable in elasticsearch?

---

<div class="post-metadata">

**Author:** ![abdon](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/abdon/32/9195_2.png) [@abdon](https://discuss.elastic.co/u/abdon)\
**Post date:** [March 28, 2018, 2:29pm UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/4 "2018-03-28T14:29:04Z")

</div>

There is no out of the box functionality that gives you a similarity score like that, as far as I know.

You'd have to implement this yourself as a script that you could use as a [script\_score function in the function score query](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-function-score-query.html#function-script-score) (which is likely going to be very slow). A better way could be to [implement this as a Java plugin](https://www.elastic.co/guide/en/elasticsearch/plugins/6.2/plugin-authors.html).

Either way, you'll be looking at writing some code, unless someone else on this forum has a better idea.

---

<div class="post-metadata">

**Author:** ![maarab](https://avatars.discourse-cdn.com/v4/letter/m/9fc29f/32.png) [@maarab](https://discuss.elastic.co/u/maarab)\
**Post date:** [March 29, 2018, 4:51am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/5 "2018-03-29T04:51:21Z")

</div>

Java Plugin does seem to be the best approach to go at it. However I was not able to find enough documentation anywhere to get me started, especially when going with gradle. I'd really appreciate if you could point me to something that will help me build the skeleton of the plugin or something already there that can be modified (what I already looked at seems to be a bit confusing and seems incomplete in terms of information). Rest would be just implementation that would return the score as per the requirement.

---

<div class="post-metadata">

**Author:** ![abdon](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/abdon/32/9195_2.png) [@abdon](https://discuss.elastic.co/u/abdon)\
**Post date:** [March 29, 2018, 7:25am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/6 "2018-03-29T07:25:47Z")

</div>

There are some blogs out there on creating Elasticsearch plugins that may be helpful. If you're running into specific problems, it's probably better to create a new post on this forum to give your question some visibility.

---

<div class="post-metadata">

**Author:** ![system](https://us1.discourse-cdn.com/elastic/original/3X/1/a/1ac57faf039f6b580b3f104ef42a2a89e41014de.png) [@system](https://discuss.elastic.co/u/system)\
**Post date:** [April 26, 2018, 7:25am UTC](https://discuss.elastic.co/t/custom-score-for-fuzzy-matching-based-on-levenshtein-distance-score/125544/7 "2018-04-26T07:25:47Z")

</div>

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.
