4

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クエリを実行するときに「音に似た」一致が何であるかを見つける方法はありますか?

つまり、誰かが「Joan」を検索した場合、代わりに「John」を強調表示したいと思います。

4

2 に答える 2

8

SOUNDS LIKEあなたが思うように動作しないことに注意してください。ワイルドカードLIKEをサポートしていないため、MySQL と同等ではありません。%

これは、"John" を検索しても "John David" が検索されないことを意味します。これが単なるフォールバックである場合は許容されるかもしれませんが、理想的ではありません。

したがって、ここに別の提案があります (改善が必要な場合があります)。最初soundex()に PHP 関数を使用して、探しているキーワードの 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 と検索語の間のレーベンシュタイン距離を計算し、それによって並べ替えることができます。

次に、SQL インジェクションのバグを回避するために、MySQL でパラメーター化されたクエリを確認する必要があります。

于 2009-11-23T02:38:42.877 に答える
6

SOUND LIKE 条件は、両方の単語の SOUNDEX キーを比較するだけであり、PHP の soundex() 関数を使用して同じキーを生成できます。

したがって、一致する行が見つかり、強調表示する単語を見つける必要がある場合は、名と姓の両方をフェッチしてから、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);
    }
}

これは、最初のスニペットのよりターゲットを絞ったコードほど効率的ではありません。

于 2009-11-23T01:44:50.253 に答える