Подсветка результатов поиска по полю с фильтром word_delimiter не правильно работает

Добрый день,
Есть текст, в котором символы, цифры и буквы смешиваются в одно слово и с помощью фильтра word_delimiter хотел это разделить, но поломалась подсветка результатов.

Настройки индекса и запросы поиска
DELETE test

PUT test
{
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": "lowercase"
        }
      },
      "filter": {
        "word_delimiter": {
          "type": "word_delimiter_graph",
          "preserve_original": true,
          "catenate_all": true
        }
      },
      "tokenizer": {
        "my_ngram_tokinizer": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 50,
          "token_chars": [
            "letter",
            "digit",
            "symbol",
            "punctuation"
          ]
        }
      },
      "char_filter": {
        "char_mapping": {
          "type": "mapping",
          "mappings": [
            "Ё => Е",
            "ё => е"
          ]
        }
      },
      "analyzer": {
        "my_ngram": {
          "type": "custom",
          "tokenizer": "my_ngram_tokinizer",
          "char_filter": [
            "html_strip",
            "char_mapping"
          ],
          "filter": [
            "lowercase",
            "word_delimiter"
          ]
        },
        "my_search": {
          "tokenizer": "whitespace",
          "char_filter": [
            "html_strip"
          ],
          "filter": [
            "lowercase"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type": "keyword",
          "normalizer": "lowercase",
          "fields": {
            "ngram": {
              "type": "text",
              "analyzer": "my_ngram",
              "search_analyzer": "my_search"
            }
          }
        }
      }
    }
  }
}

PUT test/_doc/1
{
  "title" : "Сделка №25"
}

PUT test/_doc/2
{
  "title" : "Import46 (copy).csv"
}

GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title.ngram": {
              "query": "Сделка №2",
              "operator": "AND"
            }
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}

GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title.ngram": {
              "query": "Import46",
              "operator": "AND"
            }
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}

Подскажите, как починить подсветку ?

Подсветка работает нормально, это анализатор весь перекрученный и выдает ерунду. Если посмотреть, что происходит с токенами:

POST test/_analyze
{
  "text": ["Import46 (copy).csv"],
  "analyzer": "my_ngram"
}

то вы получите кроме всего прочего:

    {
      "token" : "import46",
      "start_offset" : 6,
      "end_offset" : 8,
      "type" : "word",
      "position" : 8,
      "positionLength" : 2
    },

Другими словами, анализатор утверждает, что import46 начинается на позиции 6 и имеет длину в 2 символа, что у вас и подсвечивается.

Вообще, graph фильтры - дело сложное, особенно, если пытаться применять их при индексации вместо поиска да еще и с N-граммами. Я могу объяснить почему это решение работать не будет, но я не могу понять, что вы хотите добиться этим анализатором, поэтому другие решения подсказать не могу.

1 Like

Хочется добиться отделения символов, цифр и букв друг от друга, что бы слова:
"Сделка №25" можно было найти по "Сделка" , "Сделка 25" , "25 Сделка" , "Сделка №25"
"Import46 (copy).csv" по "import 46", "import46", "46 импорт",
Кроме использования word_delimiter_graph я не нашёл способа это сделать

Хочется добиться отделения символов, цифр и букв друг от друга, что бы слова

Это только часть требований, которая не объясняет зачем нужен ngram.

Точно, забыл про ngram написать.
Ngram нам нужен для поиска с автокомплитом. Он у нас используется практически во всех полях содержащих небольшие строки текста. Это названия товаров, названия задач, названия сделок, имена клиентов, номера телефонов, адреса почты, обычно 50-70 символов.

Ngram нам нужен для поиска с автокомплитом

И как это должно работать с word delimiter?

Как пример в случае автокомплита:
"import46" находим по "imp", "impo", "import", "import46", "import 46", "46 imp", "46 import"
"Сделка №25" находим по "сдел", "сделк", "сделка", "сделка 2", "сделка 25", "сделка №2", "сделка №25", "25 сдел", "25 сделка"

А как насчет "сдел 2" и "сдел2" ?

"сдел2" скорее всего пока не нужно, а вот "сдел 2" как я понимаю, это небольшой приятный бонус от использования edge_ngram.
Судя по логам, так ещё не пробовали искать и пока не понятно, нужно ли будет.

Ну если Вы ничего о дополнительных требованиях не умолчали, то как-то так:

DELETE test

PUT test
{
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": "lowercase"
        }
      },
      "char_filter": {
        "char_mapping": {
          "type": "mapping",
          "mappings": [
            "Ё => Е",
            "ё => е"
          ]
        }
      },
      "filter": {
        "my_ngram_filter": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 50
        },
        "my_word_delimiter_index": {
          "type": "word_delimiter",
          "preserve_original": false
        } ,
        "my_word_delimiter_search": {
          "type": "word_delimiter_graph",
          "preserve_original": false
        }
      },
      "analyzer": {
        "my_index": {
          "type": "custom",
          "char_filter": [
            "html_strip",
            "char_mapping"
          ],
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "my_word_delimiter_index",
            "my_ngram_filter"
          ]
        },
        "my_search": {
          "type": "custom",
          "char_filter": [
            "char_mapping"
          ],
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "my_word_delimiter_search"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type": "keyword",
          "normalizer": "lowercase",
          "fields": {
            "ngram": {
              "type": "text",
              "analyzer": "my_index",
              "search_analyzer": "my_search"
            }
          }
        }
      }
    }
  }
}

PUT test/_doc/1
{
  "title" : "Сделка №25"
}

PUT test/_doc/2
{
  "title" : "Import46 (copy).csv"
}

GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title.ngram": {
              "query": "Сделка №2",
              "operator": "AND"
            }
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}

GET test/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "title.ngram": {
              "query": "Import46",
              "operator": "AND"
            }
          }
        }
      ]
    }
  },
  "highlight": {
    "fields": {
      "*": {}
    }
  }
}
1 Like

Прошу прощения, я забыл упомянуть, что ещё подсвечивать нужно только то, что ввели, например ввели "сдел" подсветило "<em>Сдел</em>ка №25", а сейчас подсвечивает всё слово "<em>Сделка</em>".

Насколько я понимаю, это из за токенайзера "whitespace". Попробовал заменить его на токенайзер edge_ngram, убрав "my_ngram_filter", но при добавлении документа с "Import46 (copy).csv" выскакивает ошибка:

startOffset must be non-negative, and endOffset must be >= startOffset, and offsets must not go backwards startOffset=0,endOffset=6,lastStartOffset=6 for field 'title.ngram'

Прошу прощения, я забыл упомянуть,

Ну вот, началось :smile: Подсветка происходит по позициям. Поэтому для того, чтобы подсвечивать часть термина, эта часть должна иметь свою собственную позицию. Этого можно добиться, поместив используя ngram токенизатор, но поскольку поиск по фразам тоже использует позиции, он поломается. Так что, если нужно искать по фразам, то можно искать как показано выше, а подсвечивать по полю индексированному вот так:

PUT test
{
  "settings": {
    "number_of_shards": 1,
    "analysis": {
      "normalizer": {
        "lowercase": {
          "type": "custom",
          "filter": "lowercase"
        }
      },
      "char_filter": {
        "digit_after": {
          "type": "pattern_replace",
          "pattern": "(\\D)(\\d)",
          "replacement": "$1 $2"
        },
        "digit_before": {
          "type": "pattern_replace",
          "pattern": "(\\d)(\\D)",
          "replacement": "$1 $2"
        },
        "char_mapping": {
          "type": "mapping",
          "mappings": [
            "Ё => Е",
            "ё => е"
          ]
        }
      },
      "tokenizer": {
        "my_ngram_tokenizer": {
          "type": "edge_ngram",
          "min_gram": 1,
          "max_gram": 50,
          "token_chars": [
            "letter",
            "digit"
          ]
        }
      },
      "filter": {
        "my_word_delimiter_search": {
          "type": "word_delimiter_graph",
          "preserve_original": false
        }
      },
      "analyzer": {
        "my_index": {
          "type": "custom",
          "char_filter": [
            "html_strip",
            "char_mapping",
            "digit_before",
            "digit_after"
          ],
          "tokenizer": "my_ngram_tokenizer",
          "filter": [
            "lowercase"
          ]
        },
        "my_search": {
          "type": "custom",
          "char_filter": [
            "char_mapping"
          ],
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "my_word_delimiter_search"
          ]
        }
      }
    }
  },
  "mappings": {
    "_doc": {
      "properties": {
        "title": {
          "type": "keyword",
          "normalizer": "lowercase",
          "fields": {
            "ngram": {
              "type": "text",
              "analyzer": "my_index",
              "search_analyzer": "my_search"
            }
          }
        }
      }
    }
  }
}
1 Like

Спасибо! Работает, буду тестировать :slight_smile:

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