Как сделать полнотекстовый поиск с несколькими столбцами mysql, в котором сопоставляются частичные слова

В настоящее время у меня есть одно поле поиска для поиска по нескольким столбцам с помощью этого кода:

$searchArray = explode(" ", $searchVal);
$query="SELECT * FROM users WHERE ";
$i=0;
foreach ($searchArray as $word) {
    if ($i != 0) $query .= " OR ";
    $query .= " MATCH (`first_name`, `last_name`, `email`) AGAINST ('".$word."*'  IN BOOLEAN MODE)";
    $i++;
}

Предположим, что у меня эти две строки в таблице:

id | last_name | first_name | email
1  | Smith     | John       | [email protected]
2  | Smith     | Bob        | [email protected]

Если я наберу "John S", будет показан только первый результат, который является желаемым.

Если я наберу "Джон Смит", будет показан только первый результат, который является желаемым.

Если я нахожу "Smith J", оба результата показывают, что Bob не соответствует.

Если я наберу "Смит Джон", оба результата показывают, хотя Боб не соответствует.

Наконец, если я нахожу "Jo S", результаты не возвращаются, несмотря на частичное совпадение на "Jo" и "S".

Может ли кто-нибудь помочь мне исправить мой запрос, чтобы иметь дело с желаемой функциональностью заказа, не являющейся важным и частичным согласованием результатов? Если он может быть отсортирован по лучшим совпадениям (то есть самая длинная часть слова, начиная с первой буквы, а не в середине, в наибольшем числе столбцов), это также будет огромной помощью.

UPDATE:

Просто хотел опубликовать окончательный код, который работал на основе решения. Моя петля, создающая несколько операторов сопоставления, была неправильной, как и моя ft_min_word_len.

Мой код:

$searchArray = explode(" ", $searchVal);
$query="SELECT * FROM users WHERE  MATCH (`first_name`, `last_name`, `email`) AGAINST ('";
$i=0;
foreach ($searchArray as $word) {
    $query .= "+".$word."* ";
}
$query .= "' IN BOOLEAN MODE)";

Ответ 1

В логическом режиме, требуя присутствия строк (вместо того, чтобы просто подсчитывать больше), выполняется с помощью +. сопоставление префикса выполняется с завершением *. Это похоже на то, что вы хотите, поэтому выполните поиск:

+John* +S*
+John* +Smith*
+Smith* +J*
+Jo* +S*

Обратите внимание, что индексы Full Text не могут помочь вам найти слово "где угодно". поэтому что-то вроде *mith* связано с ошибкой: они должны совпадать с символом 1 в индексе.

Если вы также хотите заказать их по значениям соответствия и, например, нужно John Smith до Johnny Smithson, вы бы сделали это:

 SELECT * FROM user 
 WHERE MATCH(..fields..) AGAINST ('match' IN BOOLEAN MODE)
 ORDER BY MATCH(..fields..) AGAINST ('match' IN BOOLEAN MODE) DESC;

Который вы увидите, не даст вам нигде, если вы не добавите все словa >= ft_min_word_len снова отдельно:

+John* +S* John
+John* +Smith* John Smith
+Smith* +J* Smith
+Jo* +S*

Для последнего, оба являются < по умолчанию 4 символа, поэтому мы не можем добавлять параметры сортировки для этого в mysql по умолчанию, но вы можете установить ft_min_world_len по-разному.

Ответ 2

IN BOOLEAN MODE вы можете использовать + -modifier, чтобы заставить AND или - -модификатор принудительно NOT. Никакой оператор, ваше дело, не обязательно.

И вам нужно проверить минимальную длину слова в вашей конфигурации mysql, чтобы индексные слова FULLTEXT INDEX были меньше определенной длины.

Мне пришлось установить

ft_min_word_len = 2

в my.cnf и пришлось перестроить индекс, чтобы сделать это эффективным. По умолчанию оно равно 3.

Чтобы узнать свой min_word_len чек (и увеличить) этот вопрос

Ответ 3

См. http://dev.mysql.com/doc/refman/5.5/en//fulltext-boolean.html

Вы можете поместить оператор "+", "-" или "нет" перед словом, чтобы он искал "И содержит это слово", "НЕ содержит это слово", и ни один оператор не "ИЛИ содержит это слово",

Если я наберу "John S", будет показан только первый результат, который является желаемым.

Там только один Джон, так что это работает, S меньше минимальной длины слова и отбрасывается

Если я наберу "Джон Смит", будет показан только первый результат, который является желаемым.

Там только один Джон, так что это работает

Если я нахожу "Smith J", оба результата показывают, что Bob не соответствует.

J ниже минимальной длины слова, поэтому его единственный совпадающий куз, который является двумя строками

Если я наберу "Смит Джон", оба результата показывают, хотя Боб не соответствует.

Поскольку вы находитесь в BOOLEAN MODE, MySQL интерпретирует это как Смит ИЛИ Джон... Смит соответствует обоим.

Наконец, если я нахожу "Jo S", результаты не возвращаются, несмотря на частичное совпадение на "Jo" и "S".

Jo и S ниже минимальной длины слова - я считаю, что MySQL рассматривает это как поиск ничего

Вам нужно добавить "+" перед вашими поисковыми параметрами, чтобы превратить их в поиск AND... +Smith +John