前世紀の後半の初めに、ソビエトおよびロシアの有名な数学者ウラジミール・イオシフォビッチ・レーベンシュタイン(ちなみに、2ヶ月半前に亡くなりました)は、
編集距離の概念を導入しました。これは、検索エンジンからバイオインフォマティクスまでさまざまな分野でまだ使用されています。 この記事では、MySQLの
ファジー検索にその原理を適用します(MySQLは組み込みのソリューションをまだ提供していないため)。インターネットで見つかったいくつかから最も効率的な(つまり高速)メソッドを計算し、そのような検索のアルゴリズムを構築して実装しますPHP
使用するもの:
ファジー検索アルゴリズム
明らかに、各検索中に入力された単語とデータベース内の辞書からの各単語との間のレーベンシュタイン距離を計算しても意味がありません。 それには多くの時間がかかります。 ちなみに、数年前にハブで
メソッドが説明されていました。各検索で、データベースの辞書全体がPHP配列にドライブされ、音訳され、次に、同様の単語が選択されました。交互に、levenshtein関数、metaphone、similar_text、または2つが同時に使用されました。 予備のクイックフィルタリングとそれに続く見つかったオプションの改良のソリューションは、それ自体を示唆しています。
したがって、ファジー検索アルゴリズムの本質は次のように削減できます。
- 検索クエリのメタフォンを計算します。
- データベース内の辞書内のすべての単語を、レーベンシュタイン距離(またはダメラウ-レーベンシュタイン距離)が2文字未満のmetaphoneで検索します。
- 何も見つからない場合、ユーザーは単語に多くの間違いを犯し、データベースの拷問を停止し、
ユーザーが浴場に行って何も見つからないことを書きます。 - 1つの単語が見つかった場合、それを返します。
- 見つかった場合> 1単語、精製します。データベース内の辞書から見つかった各単語と検索クエリの類似性の割合を見つけます。 類似度の最大パーセンテージを見つけます。 この割合のすべての単語を返します(複数の単語の割合が同じで、最大になる場合)。
各検索では、レーベンシュタイン距離を計算する必要があります。 これを行うには、MySQLのアルゴリズムの最速の実装を見つけます。
DBの準備
すべてのテストで、4年前にVkontakteから引き出された
集落の
ベースが選択されました。 テストでは、220万件以上のエントリが含まれ、部分的に13言語に翻訳された都市テーブルが使用されました。 ロシア語に翻訳された列のみが残っており、多くの未翻訳の名前が含まれていました。 city_id列にもインデックスが作成されました。
次のステップは、各名前のメタフォンの形成でした。 metaphoneは英語のアルファベットの文字を含む行からのみ計算されるため、各名前は事前に変換する必要があります。キリル文字に音訳し、英語のアルファベットの文字に変換した
発音区別文字を含むラテン文字。
したがって、metaphone列を追加するためのPHPコードは次のとおりです。
2,246,813行の場合、メタフォンとその挿入の計算には約38分かかりました。
メタフォンの列にもインデックスが作成されました。 それ以降の検索操作は、その中でのみ発生します。
MySQLのレーベンシュタイン距離実装の選択
前述のように、3つの実装-フラッタリーリクエスト、Rast関数、およびTorres関数の速度がチェックされます。
テストでは、5語(またはスペルミス)、3語はキリル文字、2語はラテン語を使用します。
いや | 正しいつづり | 彼のメタフォン | スペルミス | 彼のメタフォン | レーベンシュタインメタフォン |
1。 | アレクサンドロフスク-サハリンスキー | ALKSNTRFSKSHLNSK | アルとザンダー、vsk-sa g olin zkiy | ALKSNTRFSKS K LNSK | 1 |
2。 | セミカラコルスク | SMKRKRSK | ckのがんについての Semik | SMKRK F SK | 1 |
3。 | シャレンタバン | XRNTSFN | シェレンツ | XRNTS F | 1 |
4。 | ブヌンブーグーラ | BNNBKL | B o n u nb o g uv a | BNNBK F | 1 |
5。 | カンポン・テナキ・カワン | KMPNKTNKKWNK | かんぽんエナキカヴァン | KMP NT NKK F NK | 2 |
最後の言葉では、レーベンシュタインの距離が2文字になるように、意図的にさらにミスが行われました。 各メソッドが正常に機能する場合、最後の単語を検索すると、毎回0行が返されます。
お世辞のリクエスト
最初のオプションは、3つすべての中で最も原始的なものです。ここでは、多数のLIKEステートメントを使用したSQLクエリでシミュレートされるレーベンシュタイン距離原理(挿入、削除、および置換操作)が基礎として採用されます。 行(単語)で構成される場合
文字(文字)、1文字のレーベンシュタイン距離を検索するときのLIKE演算子の数は、
(または
レーベンシュタイン距離<2文字を検索する場合)。
記事の最後に、著者はそのようなSQLクエリのコンパイルを自動化するPHP関数を提供しています。 この機能は、メタフォン検索用に変更されましたが、原則は同じままです。
LIKEは、両方のアンダースコアとパーセント記号の両方の
検索パターンでチェックされました。
ワード番号 | 「_」付きのLIKEの場合はt 、sec | 見つかった | 「%」のあるLIKEの場合、秒 | 見つかった |
1。 | 24.2 | 1 | 24.6 | 1 |
2。 | 14.1 | 1 | 14.8 | 4 |
3。 | 11.9 | 188 | 12.3 | 224 |
4。 | 11.9 | 70 | 12.8 | 87 |
5。 | 18.1 | 0 | 19.6 | 0 |
単語が長いほど、類似のメタフォンを検索するのに時間がかかります(これは明らかです)が、同時に、単語が長いほど、各文字に費やす時間が少なくなります(明らかではありません)。 と仮定する
-同様のメタフォンの検索に費やした合計時間、および
-私たちが探していたメタフォンの文字の総数。 最初の単語が2番目の単語より短い場合(
)したがって、最初の単語の類似したメタフォンの検索に費やされた合計時間は、2番目の単語よりも短くなります(
)その後
検索パターン「_」を「%」に置き換えると、時間の急激な急増も予想されましたが、合計時間は2〜8%の範囲で増加しました。
ラスタ機能
2番目のオプションは、ユーザー定義関数levenshteinで、2つのパラメーター(2つのVARCHAR行(255))を取り、数値INTを返します-レーベンシュタイン距離。 補助関数levenshtein_ratioも提案されています。これは、前の関数で計算されたレーベンシュタイン距離に基づいて、2行の類似度のパーセンテージを返します(PHP関数Similar_textと同様)。 最初の機能のみをテストします。
レーベンシュタイン距離が1文字のすべての単語を検索してみましょう。
SELECT city_id, title_ru FROM _cities WHERE levenshtein("BNNBKF",metaphone)=1
4番目の名前(最短のメタフォンを持つ)の類似のメタフォンを検索すると、検索テンプレート "_"でLIKEを使用して検索した場合と同じ結果が得られましたが、約66分かかりました。
トーレス関数
3番目のオプションは、
Linus TorvaldsがCで作成した
関数の実装です。 この関数は3つのパラメーターを取ります-比較のために2行、整数INTは上限です。 比較のために各行から取得される最大文字数。
私たちのテストからのすべての比phor-20文字の普遍的な上限を設定し、1文字のDamerau-Levenshtein距離を持つすべての単語を見つけようとします。
SELECT city_id, title_ru FROM _cities WHERE damlevlim("BNNBKF",metaphone,20)=1
結果:
ワード番号 | f-torresの場合はt 、sec | 見つかった |
1。 | 11.24 | 1 |
2。 | 9.25 | 1 |
3。 | 9.19 | 173 |
4。 | 8.3 | 86 |
5。 | 11.57 | 0 |
Torres関数は、Flattery要求と比較して、特にRast関数と比較して優れた結果を示しています。 2番目のプラス-Torresは高度なDamerau-Levenshteinアルゴリズムを使用しました。このアルゴリズムでは、転置操作が挿入、削除、および置換操作に追加されました。 Torres関数とFlatteryリクエストの結果を比較します。
ワード番号 | 要求のお世辞、秒 | f-torresの場合はt 、sec | クエリのお世辞、単語が見つかりました | F.トレス、発見された言葉 |
1。 | 24.2 | 11.24 | 1 | 1 |
2。 | 14.1 | 9.25 | 1 | 1 |
3。 | 11.9 | 9.19 | 188 | 173 |
4。 | 11.9 | 8.3 | 70 | 86 |
5。 | 18.1 | 11.57 | 0 | 0 |
返される行の数の違いは、使用されるアルゴリズムの違いによって説明できます(それぞれ、ブラーニーとトーレス関数を照会するためのレーベンシュタインとダメラウ-レーベンシュタイン)。 5つのケースのうち5つのケースで、Torres関数が勝者となりました。そのため、PHPの最終的な実装で使用され、similar_text関数によって結果が絞り込まれます。
PHPの実装
検索クエリが音訳されていない場合、つまり、 同様の単語を受け取った後、元の単語と比較されます。 実験中に、similar_text関数は、同じレーベンシュタイン距離にあるキリル文字とラテン文字の単語に対して異なる結果を返すことがわかりました。 そのため、計算の純度を高めるために、levenshtein PHP関数の使用時に同じ問題を解決するためにluciole75wによって元々提案され
たutf8_to_extended_ascii関数が追加で使用されます。
結果は次のようになります。
まとめ
最も高速なのはDamerau-Levenshtein distanceの実装で、CでLinus Torvaldsが作成し、ユーザー定義関数としてMySQL用にDiego Torresが採用しました。 第二に、わずかな時間差で、多数のLIKEステートメントを持つSQLクエリの形式で、レーベンシュタイン距離の原始的な模倣があります、著者-Gordon Lesti。 3番目に、Jason RastによるMySQLのユーザー定義関数ははるかに遅れていました。
結論として、本番環境でのMySQLでのレーベンシュタイン距離計算の使用は、比較する行が短く、その行と比較される単語のあるテーブルが小さい場合にのみ必要であることを付け加えます。 そうでない場合、大きな辞書を持つテーブルの場合に考えられる解決策は、たとえば最初の文字、または単語またはそのメタフォンの長さによって、いくつかのテーブルに分割することです。
ほとんどの場合、Webのファジー検索アルゴリズムの適用分野は、既存の辞書の名前と名前を検索することです。ユーザーは入力ミスをしたり、少なくとも1文字間違える可能性が高いです。 いずれにせよ、スクリプトが提供するオプションがスクリーンショットの対象にならないことを予測してみてください。
PSレーベンシュタイン距離を計算するための他のカスタムMySQL関数に関する
別の記事 。
PPSのコメントでは、
YourChiefは、生産には
レーベンシュタインオートマトンを使用する方がはるかに効率的であると示唆しています。