PHP/MySQL:Выделите результаты запроса “ЗВУЧИТ КАК”

StackOverflow https://stackoverflow.com/questions/1778252

Вопрос

Быстрый вопрос по MYSQL / PHP.Я использую "не такой строгий" поисковый запрос в качестве запасного варианта, если при обычном поисковом запросе не найдено результатов, в соответствии с:

foreach($find_array as $word) { 
  clauses[] = "(firstname SOUNDS LIKE '$word%' OR lastname SOUNDS LIKE '$word%')";
}
if (!empty($clauses)) $filter='('.implode(' AND ', $clauses).')';
$query = "SELECT * FROM table WHERE $filter";

Теперь я использую PHP для выделения результатов, например:

foreach ($find_array as $term_to_highlight){
    foreach ($result as $key => $result_string){
        $result[$key]=highlight_stuff($result_string, $term_to_highlight);
    }
}

Но этот метод терпит неудачу, когда я не знаю, что выделить.Есть ли какой-нибудь способ узнать, каково соответствие "похожему звуку" при выполнении этого запроса mysql?

То есть, если кто-то ищет "Джоан", я хочу, чтобы вместо этого было выделено "Джон".

Это было полезно?

Решение

Условие SOUND LIKE просто сравнивает ключ SOUNDEX обоих слов, и вы можете использовать функцию PHP soundex() для генерации одного и того же ключа.

Итак, если вы нашли подходящую строку и вам нужно было выяснить, какое слово выделить, вы можете получить как firstname, так и lastname, а затем использовать PHP, чтобы найти, какое из них соответствует, и выделить только это слово.

Я создал этот код просто для того, чтобы опробовать это. (Пришлось проверить мою теорию xD)

<?php
// A space seperated string of keywords, presumably from a search box somewhere.
$search_string = 'John Doe';

// Create a data array to contain the keywords and their matches.
// Keywords are grouped by their soundex keys.
$data = array();
foreach(explode(' ', $search_string) as $_word) {
    $data[soundex($_word)]['keywords'][] = $_word;
}

// Execute a query to find all rows matching the soundex keys for the words.
$soundex_list = "'". implode("','", array_keys($data)) ."'";
$sql = "SELECT id, firstname, lastname
        FROM   sounds_like
        WHERE  SOUNDEX(firstname) IN({$soundex_list})
        OR     SOUNDEX(lastname)  IN({$soundex_list})";
$sql_result = $dbLink->query($sql);

// Add the matches to their respective soundex key in the data array.
// This checks which word matched, the first or last name, and tags
// that word as the match so it can be highlighted later.
if($sql_result) {
    while($_row = $sql_result->fetch_assoc()) {
        foreach($data as $_soundex => &$_elem) {
            if(soundex($_row['firstname']) == $_soundex) {
                $_row['matches'] = 'firstname';
                $_elem['matches'][] = $_row;
            }
            else if(soundex($_row['lastname']) == $_soundex) {
                $_row['matches'] = 'lastname';
                $_elem['matches'][] = $_row;
            }
        }
    }
}

// Print the results as a simple text list.
header('content-type: text/plain');
echo "-- Possible results --\n";

foreach($data as $_group) {
    // Print the keywords for this group's soundex key.
    $keyword_list = "'". implode("', '", $_group['keywords']) ."'";
    echo "For keywords: {$keyword_list}\n";

    // Print all the matches for this group, if any.
    if(isset($_group['matches']) && count($_group['matches']) > 0) {
        foreach($_group['matches'] as $_match) {
            // Highlight the matching word by encapsulatin it in dashes.
            if($_match['matches'] == 'firstname') {
                $_match['firstname'] = "-{$_match['firstname']}-";
            }
            else {
                $_match['lastname'] = "-{$_match['lastname']}-";
            }

            echo " #{$_match['id']}: {$_match['firstname']} {$_match['lastname']}\n";
        }
    }
    else {
        echo " No matches.\n";
    }
}
?>

Более обобщенная функция для извлечения соответствующего слова soundex из строк может выглядеть следующим образом:

<?php
/**
 * Attempts to find the first word in the $heystack that is a soundex
 * match for the $needle.
 */
function find_soundex_match($heystack, $needle) {
    $words = explode(' ', $heystack);
    $needle_soundex = soundex($needle);
    foreach($words as $_word) {
        if(soundex($_word) == $needle_soundex) {
            return $_word;
        }
    }
    return false;
}
?>

Который, если я правильно понимаю, мог бы быть использован в вашем ранее опубликованном коде как:

foreach ($find_array as $term_to_highlight){
    foreach ($result as $key => $result_string){
        $match_to_highlight = find_soundex_match($result_string, $term_to_highlight);
        $result[$key]=highlight_stuff($result_string, $match_to_highlight);
    }
}

Это было бы не так эффективно, как более целевой код в первом фрагменте.

Другие советы

Обратите внимание , что SOUNDS LIKE работает не так, как вы думаете.Это не эквивалентно LIKE в MySQL, поскольку он не поддерживает % подстановочный знак.

Это означает, что ваш запрос не найдет "Джон Дэвид" при поиске "John".Это может быть приемлемо, если это всего лишь ваш запасной вариант, но это не идеально.

Итак, вот другое предложение (которое, возможно, нуждается в улучшении);сначала используйте PHPs soundex() функция для поиска soundex по ключевому слову, которое вы ищете.

$soundex = soundex($word);
$soundexPrefix = substr($soundex, 0, 2); // first two characters of soundex
$sql = "SELECT lastname, firstname ".
    "FROM table WHERE SOUNDEX(lastname) LIKE '$soundexPrefix%' ".
    "OR SOUNDEX(firstname) LIKE '$soundexPrefix%'";

Теперь у вас будет список имен и фамилий, которые имеют смутное сходство в звучании (это может быть много записей, и вы можете захотеть увеличить длину префикса soundex, который вы используете для поиска).Затем вы можете вычислить расстояние Левенштейна между soundex каждого слова и вашим поисковым запросом и отсортировать по нему.

Во-вторых, вы должны посмотреть на параметризованные запросы в MySQL, чтобы избежать ошибок SQL-инъекции.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top