Skip to content

Incorrect behaviour of fuzzing parameters with completion suggester #107707

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
pigeongcc opened this issue Apr 22, 2024 · 7 comments
Closed

Incorrect behaviour of fuzzing parameters with completion suggester #107707

pigeongcc opened this issue Apr 22, 2024 · 7 comments
Labels
>bug priority:normal A label for assessing bug priority to be used by ES engineers :Search Relevance/Analysis How text is split into tokens Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch

Comments

@pigeongcc
Copy link

Elasticsearch Version

8.6.2, 8.8.2

Installed Plugins

jmorphy2 (https://github.com/anti-social/jmorphy2), LTR plugin (https://elasticsearch-learning-to-rank.readthedocs.io/en/latest/)

Java Version

openjdk version "19.0.2" 2023-01-17 // OpenJDK Runtime Environment (build 19.0.2+7-44) // OpenJDK 64-Bit Server VM (build 19.0.2+7-44, mixed mode, sharing)

OS Version

Linux es-search-v2-es-data-1 5.4.0-167-generic #184-Ubuntu SMP Tue Oct 31 09:21:49 UTC 2023 x86_64 x86_64 x86_64 GNU/Linux

Problem Description

I use fuzzing in my quries to completion suggester. It seems like min_length and fuzziness parameters of the query can't agree between each other.

The parameters are described in the docs

Example 1: "min_length": 5, "fuzziness": "AUTO:5,8"

query: conte-
expected: query length is 6, we're expecting 1 correction (dash to be removed)
result: contemporary is suggested, everything's ok ✅

query: contempo--
expected: query length is 10, we're expecting 2 corrections
result: nothing is suggested, incorrect behaviour ❌

Example 2: "min_length": 1, "fuzziness": "AUTO:2,5"

query: co-
expected: query length is 3, we're expecting 1 correction
result: contemporary and cheese are suggested, we have 2 corrections in the cheese case ❌

query: contempo--
expected: query length is 10, we're expecting 2 corrections
result: contemporary is suggested, everything's ok ✅

The bug is reproducible with ES 8.6.2 and 8.8.2.

Steps to Reproduce

Complete Python code to reproduce the bug. Copy and run.

The code is split into .ipynb cells.

import elasticsearch
import json
import pandas as pd
import os

from tqdm import tqdm
from typing import List
from elasticsearch import Elasticsearch, helpers


es = Elasticsearch(
    hosts="127.0.0.1:9200",
    http_auth=({login}, {password}),
    timeout=100
)

es.indices.get_alias("*")
INDEX_NAME = 'text-suggestions-bug-check-1'
ALIAS_NAME = 'text-suggestions-bug-check'

# es.indices.delete(index=INDEX_NAME)
# es.indices.delete(index=ALIAS_NAME)
# Analyzers and mappings
SETTINGS = {
    "settings": {
        "index": {
            "number_of_replicas": 1,
            "number_of_shards": 3,
            "refresh_interval": "-1"
        },
        "analysis": {
            "filter": {
                "synonym_filter": {
                    "type": "synonym",
                    "synonyms": [
                    ]
                },
                "autocomplete_filter": {
                    "type": "edge_ngram",
                    "min_gram": 1,
                    "max_gram": 20
                },
            },
            "tokenizer": {
                "cg": {
                    "type": "char_group",
                    "tokenize_on_chars": [
                        "whitespace",
                        ".",
                        ",",
                        ":",
                        ";",
                        "/"
                    ]
                }
            },
            "analyzer": {
                "autocomplete_analyzer": {
                    "type": "custom",
                    "tokenizer": "cg",
                    "filter": [
                        "lowercase",
                        "synonym_filter",
                        "remove_duplicates",
                        "autocomplete_filter"
                    ]
                },
                "autocomplete_search_analyzer": {
                    "type": "custom",
                    "tokenizer": "cg",
                    "filter": [
                        "lowercase",
                        "synonym_filter"
                    ]
                }
            }
        }
    },
    
    "mappings": {
        "properties": {
            "query": {
                "type": "completion",
                "analyzer": "autocomplete_analyzer",
                "search_analyzer": "autocomplete_search_analyzer"
            },
        }
    }
}

# index creation
es.indices.create(index=INDEX_NAME, body=SETTINGS)
elasticsearch.client.indices.IndicesClient(es).put_alias(INDEX_NAME, ALIAS_NAME)
# index filling
def fill_index(es, suggests: dict, index_name: str = INDEX_NAME):
    actions = []
    for text, weight in tqdm(suggests.items()):
        actions.append({
            "_index": index_name,
            "query": {
                "input": text,
                "weight": weight
            }
        })
    helpers.bulk(es, actions, refresh=True)

suggests = {
    'contemporary': 1,
    'cheese': 1,
}

fill_index(es, suggests)
# Elasticsearch query
def suggest_on_prefix(
        es: Elasticsearch,
        prefix: str,
        min_length: int,
        fuzziness: str,
        index_name: str = INDEX_NAME,
        size: int = 4,
        verbose: bool = True
):
    response = es.search(
        index=index_name,
        body={
            "suggest": {
                "text-suggest": {
                "prefix": prefix,
                "completion": {
                        "field": "query",
                        "fuzzy": {
                            "fuzziness": fuzziness,
                            "min_length": min_length,
                            "unicode_aware": True
                        },
                        "size": size
                        }
                    }
                }
            }
    )
    
    text_suggestions = response["suggest"]["text-suggest"][0]["options"]
    
    if verbose:
        print(f"prefix: '{prefix}'\n")
        print(f"Score\tSuggest")
        for option in text_suggestions:
            print(f"{option['_score']}\t{option['text']}")
        print()
    
    return text_suggestions
# example 1
min_length = 5
fuzziness = 'AUTO:5,8'

prefix = 'conte-'
_ = suggest_on_prefix(es, prefix, min_length, fuzziness) # ok

prefix = 'contempo--'
_ = suggest_on_prefix(es, prefix, min_length, fuzziness) # incorrect
# example 2
min_length = 1
fuzziness = 'AUTO:2,5'

prefix = 'co-'
_ = suggest_on_prefix(es, prefix, min_length, fuzziness) # incorrect

prefix = 'contempo--'
_ = suggest_on_prefix(es, prefix, min_length, fuzziness) # ok

Logs (if relevant)

No response

@pigeongcc pigeongcc added >bug needs:triage Requires assignment of a team area label labels Apr 22, 2024
@saikatsarkar056 saikatsarkar056 added the :Search/Search Search-related issues that do not fall into other categories label Apr 25, 2024
@elasticsearchmachine elasticsearchmachine added Team:Search Meta label for search team and removed needs:triage Requires assignment of a team area label labels Apr 25, 2024
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-search (Team:Search)

@pigeongcc
Copy link
Author

Hello there! Anyone can help with the issue? It's highly important for our application. Thanks

@benwtrent benwtrent added :Search Relevance/Analysis How text is split into tokens priority:normal A label for assessing bug priority to be used by ES engineers and removed :Search/Search Search-related issues that do not fall into other categories labels Jul 9, 2024
@javanna javanna added Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch and removed Team:Search Meta label for search team labels Jul 12, 2024
@elasticsearchmachine
Copy link
Collaborator

Pinging @elastic/es-search-relevance (Team:Search Relevance)

@pigeongcc
Copy link
Author

Hello. Anyone can help with the issue? It's highly important for our application. Thank you

@pigeongcc
Copy link
Author

Hello there! Anyone can help with the issue? It's highly important for our application 🙏🏻

@jimczi
Copy link
Contributor

jimczi commented Oct 15, 2024

"autocomplete_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 20
},

Remove the edge_ngram filter as it’s unnecessary and causing the "issue". The completion suggester already operates on prefixes, so indexing ngrams like c, co, cont, etc., isn't required. Since queries on the completion field are prefix-based, c will already match contemporary without needing to index every partial term.
After removing the edge_ngram, a reindex is needed but then all the examples you provided should work fine.

@pigeongcc
Copy link
Author

thank you! it solved the problem. I appreciate your support

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
>bug priority:normal A label for assessing bug priority to be used by ES engineers :Search Relevance/Analysis How text is split into tokens Team:Search Relevance Meta label for the Search Relevance team in Elasticsearch
Projects
None yet
Development

No branches or pull requests

6 participants