Как реализовать систему поиска автозамены / альтернативного правописания с полнотекстовым логическим режимом PHP и MySQL для MVP

NB:

  • Этот вопрос поднимался много раз, но, прежде чем голосовать, пожалуйста, найдите время, чтобы прочитать следующее. Если этот вопрос повторяется, это может означать, что для такой повторяющейся ситуации, как эта, нет однозначного или четкого приемлемого ответа. Если вы понизили голос, объясните, почему.

  • Я мог бы задать этот вопрос dba.stackexchange.com, но мой вопрос касается примера с кодом.

  • Не думайте, что у меня более 10 лет опыта (или у других людей, имеющих такой же вопрос). Я начал программировать 2 года назад, поэтому, пожалуйста, будьте терпимы.

  • Я мог бы использовать словарь, как Pspell, Заклинание или же Hunspell но этот случай не распространяется должным образом на названия компаний или городов. Более того, я не хочу запрашивать в БД все предложенные исправления (особенно при запуске заголовка каждые 300 мс) (больше вопросов об этих словарях)

  • Я мог бы использовать дополнительную поисковую систему, такую ​​как Elasticsearch или же сфинкс но у меня нет финансовых или человеческих ресурсов, выделенных для этого MVP. Как предложено в этом ответе, Полный текст MySQL должен быть достаточно и намного менее сложным.

Доступные технологии:

MySQL 5.7 InnoDB с логическим режимом полнотекстового индекса на желаемых полях, PHP 7.0 с php-fpm, VPS с Centos 7, corejs-typeahead

Цель:

Я хочу вернуть из MySQL результаты поиска пользователя, будь то правильный поиск или поиск с ошибкой.

Пример распространенных проблем:

ДЕФИС

  • слова с дефисами «-» раздражает поиск при частичном поиске.

Потенциальное решение:

  • Мне пришлось бы обернуть поисковый запрос в «», чтобы найти фразу (см. [Введите описание ссылки здесь] [примеры из man]. Тем не менее, он не найдет компанию с именем ‘»le dé-k-lé» «из-за в ft_min_word_len=3 AND «de» и «le» — это слова-заглушки (слишком часто встречаются во многих языках)

  • Я мог бы, но я не буду вдаваться в следующие решения, потому что я недостаточно квалифицирован или это неуместно. Как предложено в руководстве MySQL для Изменить исходный код MySQL или же Изменить файл набора символов или же Добавить новое сопоставление. Например, если я хочу использовать оператор минус (-) для фильтрации некоторых слов в будущем, это больше не будет возможно.

АПОСТРОФ / ЕДИНАЯ ЦИТАТА

  • Слова с апострофом часто ищутся без апострофов (особенно на мобильных телефонах). Например, «A’trego» будет вводиться как «atrego». Это определенно будет пропущено полнотекстовым индексом, так как «A’trego» считается двумя словами «a» и «trego»

Двойные письма пропущены

  • слова с двойными буквами часто пропускаются или ошибаются пользователем. Например, «Cerrutti» может быть с ошибкой «Cerutti» или «Cerruti» и т. Д.

Потенциальное решение:

  • Я мог бы использовать SOUNDEX (), но он в основном предназначен для английского языка
  • Я мог бы использовать функция Левенштейна но это было бы медленно для больших наборов данных (например, таблица со всеми европейскими городами). Кажется, что он должен сделать полное сканирование, в сочетании с typehhead, это определенно не тот путь. Хотя некоторые предложения интересны Вот а также Вот

ЭКЗОНИМЫ И ПЛЮРАЛЬНЫЕ ФОРМЫ

  • Экзонимы могут быть сложны в поиске (с точки зрения пользователя). Например, итальянский город Флоренция по-немецки называется Флоренц, по-французски Флоренция и т. Д. Люди часто переключаются с экзонима на местное имя, когда они находятся в самом городе. Экзонимы не будут обрабатываться должным образом предыдущими алгоритмами. Кроме того, не очень удобно иметь название города без его экзонимов. Это не хорошо ни для i18n.

Потенциальное решение:

  • Самодельный словарь с использованием Pspell или другие подобные библиотеки будут возвращать строку, которая хранится и индексируется в MySQL.

диакритические
— как и в случае с экзонимами, это может быть трудно для пользователя. То же самое для i18n. Например, попробуйте найти ресторан в Лодзи в Польше, используя обычную клавиатуру. Польский и английский человек определенно не будет подходить к этой строке одинаково.

Потенциальное решение:
— Потенциальное решение уже управляется во внешнем интерфейсе отображением, используемым библиотекой corejs-typeahead. Остальное очищается с помощью PHP $strCleaned = iconv('UTF-8', 'utf-8//TRANSLIT', $str);

СОКРАЩЕНИЯ & СОКРАЩЕНИЯ
— Сокращения используются взаимозаменяемо для названий компаний и особенно для голубых фишек. Например, LVMH, HP, GM, GE, BMW. То же самое касается городов. Невозможность вернуть компанию или город при поиске с помощью аббревиатур является большой ошибкой с точки зрения пользовательского опыта.

Потенциальное решение:
— Первый, ft_min_word_len следует уменьшить до двух символов.
— Во-вторых, список стоп-слов должен быть реализован
— В-третьих полнотекстовый индекс перестроен.
— Я не вижу другой устойчивой альтернативы

Этот список не является исчерпывающим ни в вопросах, ни в возможных решениях.
Я буду рад завершить его, если потребуется.

МОЕ РЕШЕНИЕ

Мое решение вдохновлено и экстраполировано из ответ здесь

По сути, перед каждым поиском пользовательский ввод должен быть лишен символов, таких как апостроф, дефис; упрощено удаление похожих последовательных букв.

Эти очищенные альтернативные слова будут сохранены в столбце с индексом полнотекстового индекса.

Это решение довольно простое и адекватно отвечает моим требованиям. Но мой короткий опыт подсказывает, что я должен быть осторожен, поскольку он определенно страдает недостатками (которые я еще не определил).

Ниже приведена упрощенная версия моего кода.

PHP

// Get input from the typeahead searched word
$query = (!empty($_GET['q'])) ? strtolower($_GET['q']) : null;

// end the script if empty query
if (!isset($query)) {
die('Invalid query.');
}

// Clean and Strip input
$query = trim($query);
$query = str_replace("'","",$query);
$query = str_replace("-","",$query);
$query = preg_replace('{(.)\1+}','$1',$query);

// filter/sanitize query
if (!preg_match("/^([0-9 '@&\-\.\pL])+$/ui", $input[$field]) !== false) {exit;}
$query = mysqli_real_escape_string($conn, $query); // I will switch to PDO prepared statement soon as mysqli_real_escape_string do not offer enough protection

MySQL Query

SELECT DISTINCT
company.company_name,
MATCH (company_name, company_alternative) AGAINST ('$query*' IN BOOLEAN MODE) AS relevance

FROM company

WHERE
MATCH (company_name, company_alternative) AGAINST ('$query*' IN BOOLEAN MODE)
AND relevance > 1

ORDER BY
CASE
WHEN company_name = '$query' THEN 0
WHEN company_name LIKE '$query%' THEN 1
WHEN company_name LIKE '%$query' THEN 2
ELSE 3
END

LIMIT 20

MySQL Table

Напоминаю, что я получил полнотекстовый индекс из двух столбцов (company_name, company_alternative)

**company_name**    |   **company_alternative**
l'Attrego           |   lattrego latrego attrego atrego
le Dé-K-Lé          |   dekle dekale decale
General Electric    |   GE

ЯЗЫКИ моего решения, которое я определил

  • Альтернативные слова не будут содержать распространенных орфографических ошибок, пока я не добавлю их вручную в alternative_name колонка или процесс машинного обучения. Таким образом, сложный в управлении и не масштабируемый (этот недостаток может быть устранен без особых проблем с машинным обучением, так как я уже собираю все поисковые запросы).
  • Я должен управлять динамическим и сложным списком стоп-слов
  • Я должен восстановить индексы из-за снижения ft_min_word_len до 2

Итак, мой вопрос,
Как реализовать систему поиска автозамены / альтернативного правописания с полнотекстовым логическим режимом PHP и MySQL для MVP?, можно перефразировать,

  • Является ли мое решение наименее масштабируемым?

  • Вы видите недостатки, которых я не вижу?

  • Как я могу улучшить этот подход, если он разумный?

2

Решение

Задача ещё не решена.

Другие решения

Других решений пока нет …