web-dev-qa-db-ja.com

大規模なデータセットにオートコンプリートを実装する方法

私が作成しているWebサイトにGoogleの提案のようなものを実装しようとしていますが、非常に大きなデータセットで実行する方法に興味があります。確かに1000個のアイテムがある場合は、アイテムをキャッシュしてループするだけです。しかし、100万個のアイテムがある場合、どうすればよいですか?さらに、アイテムが1つの単語ではないと仮定します。特に、Pandora.comには本当に感銘を受けました。たとえば、「wet」を検索すると、「Wet Sand」が返されますが、Toad The Wet Sprocketも返されます。そして、それらのオートコンプリートは高速です。私の最初のアイデアは、項目を最初の2文字でグループ化することでしたので、次のようになります。

Dictionary<string,List<string>>

ここで、キーは最初の2文字です。それは問題ありませんが、Pandoraと同様のことを行い、文字列の中央に一致する結果をユーザーに表示したい場合はどうすればよいでしょうか。私の考えでは、「WE」バケットではなく「TO」バケット内にあるため、WetはToad the Wet Sprocketと決して一致しません。したがって、おそらくcould文字列を分割し、 "Toad the Wet Sprocket"を "TO"、 "WE"、および "SP"バケットに入れます(単語 "THE"を取り除く)が、あなたはそれぞれ数単語を言わなければならないかもしれない百万のエントリについて話している、それはあなたがすぐに大量のメモリを使い始めるように思える。 OK、それは長い質問でした。考え?

44
aquinas

リストにインクリメンタル検索を実装する方法 で指摘したように、大きなテキストのパターンを検索するには、 Trie または Patricia trie のような構造を使用する必要があります。

そして、いくつかのテキストの途中でパターンを発見するために、1つの簡単な解決策があります。それが最も効率的な解決策かどうかはわかりませんが、通常は次のようにします。

Trieに新しいテキストを挿入するときは、テキストを挿入し、最初の文字を削除してから、もう一度挿入し、2番目の文字を削除して、もう一度挿入します。以下、テキスト全体が消費されるまで続けます。次に、ルートから1回検索するだけで、挿入されたすべてのテキストのすべての部分文字列を検出できます。その結果の構造は Suffix Tree と呼ばれ、利用可能な多くの最適化があります。

そして、それは本当に信じられないほど高速です。 n文字の特定のシーケンスを含むすべてのテキストを検索するには、最大でn個のノードを検査し、すべてのノードの子のリストで検索を実行する必要があります。子ノードコレクションの実装(配列、リスト、バイナリツリー、スキップリスト)によっては、大文字と小文字を区別しないラテン文字のみを想定して、わずか5つの検索手順で必要な子ノードを識別できる場合があります。補間ソートは、ルートの近くに通常見られるような、多くの子供を持つ大きなアルファベットやノードに役立つ場合があります。

28

これを自分で実装しようとしないでください(好奇心が強い場合を除きます)。 LuceneやEndecaのようなものを使用してください-時間と髪を節約します。

9
Jim Arnold

アルゴリズムには関係ありませんが、非同期要求を発行する前にユーザーが入力を停止したことを確認するために、カイプレスの後に200ミリ秒以上の遅延(ラグ)があることを確認してください。そうすることで、サーバーへの冗長なhttp要求を減らすことができます。

6
cherouvim

私は trie の行に沿って何かを使用し、各リーフノードの値を、リーフノードで表されるWordを含む可能性のリストにする必要があります。可能性の高い順に並べ替えたり、ユーザーが検索ボックスに入力した別の単語に基づいて動的に並べ替えたりフィルタリングしたりすることができます。これは非常に迅速かつ妥当な量のRAMで実行されます。

2
rmeador

トライを必要とせず、文字列の中央から取得したい場合は、通常、編集距離関数(レーベンシュタイン距離)を実行して、2つの文字列がどの程度一致しているかを示す数値を取得します。これは特に効率的なアルゴリズムではありませんが、比較的短いため、単語などについてはそれほど問題になりません。たとえば8000文字の文字列で比較を実行している場合は、おそらく数秒かかります。私はほとんどの言語が実装を持っていることを知っています、またはインターネットでそれのためのコード/疑似コードをかなり簡単に見つけることができます。

1
Ian Ooi

サーバー側にアイテムを保持し(データセットが実際に大きく複雑な場合は、おそらくDBに)、json/xmlを使用して結果を返すクライアントのブラウザーからAJAX呼び出しを送信します。これは、ユーザーの入力に応じて、またはタイマーを使用して実行できます。

1
Assaf Lavie

私はこのシナリオ用に AutoCompleteAPI を構築しました。

サインアップしてプライベートインデックスを取得し、ドキュメントをアップロードします。

ドキュメント「New York」でcurlを使用したアップロードの例:

curl -X PUT -H "Content-Type: application/json" -H "Authorization: [YourSecretKey]" -d '{
"key": "New York",
"input": "New York"
}' "http://suggest.autocompleteapi.com/[YourAccountKey]/[FieldName]"

すべてのドキュメントのインデックスを作成した後、オートコンプリートの候補を取得するには、次を使用します。

http://suggest.autocompleteapi.com/[YourAccountKey]/[FieldName]?prefix=new

クライアントのオートコンプリートライブラリを使用して、これらの結果をユーザーに表示できます。

0
Sean