MySQL полнотекстовый поиск с символом @создает ошибку "синтаксическая ошибка", неожиданная "@", ожидающая $end "

Следующий запрос приводит к ошибке из-за символа @(по символу). Запрос будет работать нормально, когда он будет удален. Я попытался избежать символа @, но не повезло.

SELECT * FROM clients WHERE MATCH (form) AGAINST ('[email protected]' IN BOOLEAN MODE);

Произошла ошибка:

#1064 - syntax error, unexpected '@', expecting $end

Обратите внимание, что я тестирую эти запросы в области консоли phpMyAdmin SQL, поэтому это не проблема с ошибкой escape-кода с моим другим программированием.

Сервер MySQL - версия 5.6.17.

Любые идеи? Спасибо.

Ответ 1

Это связано с индексами INNODB FULLTEXT.

Вводится как комбинация:

  • Полнотекстовый поиск InnoDB не поддерживает использование нескольких операторов в одном слове поиска

  • @distance Этот оператор работает только с таблицами InnoDB. Он проверяет, начинаются ли два или несколько слов на определенном расстоянии друг от друга, измеряются словами.

http://dev.mysql.com/doc/refman/5.6/en/fulltext-boolean.html

# Running a test search for MATCH('+test{$ascii}test' IN BOOLEAN MODE) on all ASCII chars returns errors on:
40 (
41 )
64 @

MYSQL, по-видимому, обрабатывает эти символы как разрывы слов, и я не нашел способа избежать и включить их в фактический запрос, поэтому мое решение является разделом на символе и включает их как группу, например. "test @bar" == (+ test + bar)

# As a further test, running a search for MATCH('+{$ascii}' IN BOOLEAN MODE) returns errors for:
40 (
41 )
42 *
43 +
45 -
60 <
62 >
64 @
126 ~

Как ожидается, из документов MYSQL в качестве специальных символов модификатора BOOLEAN

# As a testcase (Requires MYSQL 5.6+): 
CREATE TABLE `fulltext_innodb` (
`id` int(11) NOT NULL AUTO_INCREMENT,
 `text` text COLLATE utf8_unicode_ci,
 PRIMARY KEY (`id`),
 FULLTEXT KEY `text` (`text`)
) ENGINE=InnoDB
INSERT INTO `fulltext_innodb` (`id`, `text`) VALUES (1, '[email protected]');

SELECT * FROM `fulltext_innodb` WHERE MATCH (`text`) AGAINST( '[email protected]’ IN BOOLEAN MODE )

#1064 - syntax error, unexpected '@', expecting $end

Ответ 2

Кажется, что невозможно заменить термин "@" каким-либо другим символом. Удаление "@" и добавление строки после него к поиску - лучший обходной путь, который я нашел до сих пор. Это означает, что

$mail = '[email protected]';
$mail = str_replace("@", " +", $mail); //replace the @ that causes the problem
$query = "SELECT * FROM clients WHERE MATCH (form) AGAINST ('$mail' IN BOOLEAN MODE)'; //query with replaced mail address

должен принести нужный результат.

Другим способом было бы справиться с этим, как в этом посте, который содержит аналогичную проблему с другим хорошим решением.

Ответ 3

Не прямой ответ, но если кто-то ищет код PHP для обработки токенизации строки пользовательского ввода для полнотекстового поиска, можно использовать следующий код:

/**
 * Method to take an input string and tokenize it into an array of words for Full Text Searching (FTS).
 *
 * This method is used when an input string can be made up of multiple words (let say, separated by space characters),
 * and we need to use different Boolean operators on each of the words. The tokenizing process is similar to extraction
 * of words by FTS parser in MySQL. The operators used for matching in Boolean condition are removed from the input $phrase.
 * These characters as of latest version of MySQL (8+) are: +-><()~*:""&|@  (@ is specific for InnoDB)
 * We can also execute the following query to get updated list: show variables like 'ft_boolean_syntax';
 * Afterwards, the modified string is split into individual words considering either space, comma, and, period (.) characters.
 * Details at: https://dev.mysql.com/doc/refman/8.0/en/fulltext-natural-language.html
 *
 * @param string $phrase Input statement/phrase consisting of words
 * @return array Tokenized words
 * @author Madhur, 2019
 */
function tokenizeStringIntoFTSWords(string $phrase) : array {
    $phrase_mod = trim(preg_replace('/[><()~*:"&|@+-]/', ' ', trim($phrase)));
    $words_arr = preg_split('/[\s,.]/', $phrase_mod, null, PREG_SPLIT_NO_EMPTY);

    // filter out the fulltext stop words and words whose length is less than 3.
    $fts_words = array();
    $fulltext_stop_words = array(
        'about','are','com','for','from','how','that','this','was','what',
        'when','where','who','will','with','und','the','www'
    );
    foreach($words_arr as $word) {
        // By default MySQL FULLTEXT index does not store words whose length is less than 3.
        // Check innodb_ft_min_token_size Ref: https://dev.mysql.com/doc/refman/8.0/en/innodb-parameters.html#sysvar_innodb_ft_min_token_size
        // So we need to ignore words whose length is less than 3.
        if(strlen($word) < 3) continue;

        // Ignore the fulltext stop words, whose length is greater than 3 or equal to 3.
        // Ref: https://dev.mysql.com/doc/refman/8.0/en/fulltext-stopwords.html
        if (in_array($word, $fulltext_stop_words)) continue;

        $fts_words[] = $word;
    }

    return $fts_words;
}

Выше код будет обрабатывать стоп-слова, минимальную длину слова, а также операторы логического режима. Так, например, если пользователь вводит: [email protected] with in Javascript, он вернет массив (Search, bar, Javascript). После этого полнотекстовый запрос может быть написан с использованием этого массива.