# Two way substring search

**URL:** <https://discuss.elastic.co/t/two-way-substring-search/331052>\
**Category:** Elasticsearch\
**Created:** [April 28, 2023, 11:17am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052 "2023-04-28T11:17:55Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![Andr](https://avatars.discourse-cdn.com/v4/letter/a/85f322/32.png) [@Andr](https://discuss.elastic.co/u/Andr)\
**Post date:** [April 28, 2023, 11:17am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/1 "2023-04-28T11:17:55Z")

</div>

Hello!

I would like to implement a search which returns a document if a query string is substring of the document field or the document field is substring of the query string.  
For example, I have a document with a text field {"num": "123456"}.  
I would like to get it if search queries are: "123456", "2345", "01234567"  
I wouldn't like to get it if search queries are: "1234456", "23455", "012345667"  
Are there any ways to implement such a behavior?

Thanks!

---

<div class="post-metadata">

**Author:** ![warkolm](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/warkolm/32/39224_2.png) [@warkolm](https://discuss.elastic.co/u/warkolm)\
**Post date:** [April 30, 2023, 11:04pm UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/2 "2023-04-30T23:04:22Z")

</div>

Welcome to our community! 😃

You could try [ngrams](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/analysis-ngram-tokenfilter.html), which are expensive to implement due to the tokenisation.

---

<div class="post-metadata">

**Author:** ![Christian\_Dahlqvist](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/christian_dahlqvist/32/4617_2.png) [@Christian\_Dahlqvist](https://discuss.elastic.co/u/Christian_Dahlqvist)\
**Post date:** [May 1, 2023, 10:04am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/3 "2023-05-01T10:04:07Z")

</div>

As far as I know there is no easy and efficient way to do this. ngrams or wildcard search would find matches one way (when the search string is part of the indexed string). The opposite is trickier and will likely need to be handled separately and require one or more query clauses combined in a boolean clause. You may be able to use one or more ngram query time analysers to break up the query string into substrings and do an exact match for each against the indexed strings, but I think it would quickly become messy. I also suspect this might quickly become expensive for longer strings.

What is the minimum and maximum length of the strings you have indexed and are searching with?

---

<div class="post-metadata">

**Author:** ![Andr](https://avatars.discourse-cdn.com/v4/letter/a/85f322/32.png) [@Andr](https://discuss.elastic.co/u/Andr)\
**Post date:** [May 1, 2023, 10:48am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/4 "2023-05-01T10:48:37Z")

</div>

Thanks! It's a phone number field. So, max lenght should be up to 17 symbols. I've already had ngram (3 gram) tokenization and it works ok when a query string shorter than the field of document (for expample doc is {"num": "123456"} and queries are: "123", "123456"). Mainly this problem due to a country code. Indexed docs and queries could be with or without the code. When I have a query string "+4930901820" I would like to get doc with the field "30901820".

---

<div class="post-metadata">

**Author:** ![Christian\_Dahlqvist](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/christian_dahlqvist/32/4617_2.png) [@Christian\_Dahlqvist](https://discuss.elastic.co/u/Christian_Dahlqvist)\
**Post date:** [May 1, 2023, 10:54am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/5 "2023-05-01T10:54:49Z")

</div>

Context helps as it sounds like you are not just looking for any substrings, but rather an exact match for a substring originating from the back. Is that correct?

---

<div class="post-metadata">

**Author:** ![Andr](https://avatars.discourse-cdn.com/v4/letter/a/85f322/32.png) [@Andr](https://discuss.elastic.co/u/Andr)\
**Post date:** [May 1, 2023, 11:10am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/6 "2023-05-01T11:10:31Z")

</div>

Yes. For query string "+4930901820" I would like to get doc with the field "30901820". So the whole field "30901820" is a substring of the query "+4930901820"

---

<div class="post-metadata">

**Author:** ![Christian\_Dahlqvist](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/christian_dahlqvist/32/4617_2.png) [@Christian\_Dahlqvist](https://discuss.elastic.co/u/Christian_Dahlqvist)\
**Post date:** [May 1, 2023, 11:11am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/7 "2023-05-01T11:11:55Z")

</div>

Does the match need to be anchored at the end or should "3090182" to match as well?

---

<div class="post-metadata">

**Author:** ![Andr](https://avatars.discourse-cdn.com/v4/letter/a/85f322/32.png) [@Andr](https://discuss.elastic.co/u/Andr)\
**Post date:** [May 1, 2023, 11:24am UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/8 "2023-05-01T11:24:44Z")

</div>

Currently, the main goal is to find "30901820" as much perfomant as possible.

---

<div class="post-metadata">

**Author:** ![Christian\_Dahlqvist](https://sea2.discourse-cdn.com/elastic/user_avatar/discuss.elastic.co/christian_dahlqvist/32/4617_2.png) [@Christian\_Dahlqvist](https://discuss.elastic.co/u/Christian_Dahlqvist)\
**Post date:** [May 1, 2023, 5:59pm UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/9 "2023-05-01T17:59:06Z")

</div>

I have not had time to experiment with it and am not sure if/when I will, but wanted to share some initial ideas.

One way of doing this might be to map the main field as keyword and have two [multi-fields](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/multi-fields.html#_multi_fields_with_multiple_analyzers) with custom mappings, one for each type of search (longer search string vs shorter indexed string and shorter search string vs longer indexed string). This means that you most likely would need a boolean query with two should clauses, one for each scenario.

For the scenario where you have a shorter search string and want to match to the same string or something that ends with the full search string, you can probably use a multi-field with [wildcard mapping](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/keyword.html#wildcard-field-type). You could use the keyword field as well with a wildcard query, but as you would have a leading wildcard this would be quite slow.

For the other scenario you might be able to create a custom analyser with a [reverse filter](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/analysis-reverse-tokenfilter.html) followed by an [edge ngram token filter](https://www.elastic.co/guide/en/elasticsearch/reference/8.7/analysis-edgengram-tokenfilter.html). Am not sure which query type would work best with that, so you may need to experiment.

---

<div class="post-metadata">

**Author:** ![Andr](https://avatars.discourse-cdn.com/v4/letter/a/85f322/32.png) [@Andr](https://discuss.elastic.co/u/Andr)\
**Post date:** [May 2, 2023, 12:28pm UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/10 "2023-05-02T12:28:48Z")

</div>

Thank you for ideas! I'll try them

---

<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:** [May 30, 2023, 12:29pm UTC](https://discuss.elastic.co/t/two-way-substring-search/331052/11 "2023-05-30T12:29:31Z")

</div>

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