LuceneなどのAPIを使用せずに検索エンジンの単純なインデックス作成機能を構築したいと思います。転置インデックスでは、各単語の基本情報を記録する必要があります。 docID、位置、および頻度。
今、私はいくつかの質問があります:
転置インデックスの作成には、どのようなデータ構造がよく使用されますか?多次元リスト?
インデックスを作成した後、それをファイルに書き込む方法は?ファイルにはどのような形式がありますか?テーブルのように?紙にインデックステーブルを描くのが好きですか?
TinySearchEngine で、転置インデックスと検索の非常に単純な実装を確認できます。
最初の質問では、単純な(メモリ内の)転置インデックスを作成する場合、単純なデータ構造は次のようなハッシュマップです。
val invertedIndex = new collection.mutable.HashMap[String, List[Posting]]
またはJava風:
HashMap<String, List<Posting>> invertedIndex = new HashMap<String, List<Postring>>();
ハッシュは、各用語/単語/トークンを投稿のリストにマップします。 Posting
は、ドキュメント内のWordの出現を表す単なるオブジェクトです。
case class Posting(docId:Int, var termFrequency:Int)
新しいドキュメントのインデックス作成は、トークン化(トークン/単語で区切る)の問題であり、トークンごとに、ハッシュマップの正しいリストに新しい投稿を挿入します。もちろん、その特定のdocIdにその用語の投稿がすでに存在する場合は、termFrequencyを増やします。これを行うには他の方法があります。メモリ内の転置インデックスの場合はこれで問題ありませんが、ディスク上のインデックスの場合は、毎回更新するのではなく、正しいPostings
を使用してtermFrequency
を1回挿入することをお勧めします。
2番目の質問に関しては、通常2つのケースがあります。
(1)(ほぼ)不変のインデックスがあります。すべてのデータに一度インデックスを付け、新しいデータがある場合は、インデックスを再作成するだけです。たとえば、1時間に何度もリアルタイムまたはインデックスを作成する必要はありません。
(2)常に新しいドキュメントが到着するため、新しく到着したドキュメントをできるだけ早く検索する必要があります。
ケース(1)の場合、少なくとも2つのファイルを持つことができます。
1-転置インデックスファイル。用語ごとにすべてのPostings
(docId/termFrequencyペア)が一覧表示されます。ここではプレーンテキストで表されていますが、通常はバイナリデータとして保存されます。
Term1<docId1,termFreq><docId2,termFreq><docId3,termFreq><docId4,termFreq><docId5,termFreq><docId6,termFreq><docId7,termFreq>
Term2<docId3,termFreq><docId5,termFreq><docId9,termFreq><docId10,termFreq><docId11,termFreq>
Term3<docId1,termFreq><docId3,termFreq><docId10,termFreq>
Term4<docId5,termFreq><docId7,termFreq><docId10,termFreq><docId12,termFreq>
...
TermN<docId5,termFreq><docId7,termFreq>
2-オフセットファイル。転置インデックスファイルで転置リストを見つけるために、各用語のオフセットを格納します。ここではオフセットを文字で表していますが、通常はバイナリデータを格納するため、オフセットはバイト単位になります。このファイルは、起動時にメモリにロードできます。用語の転置リストを検索する必要がある場合は、そのオフセットを検索し、ファイルから転置リストを読み取ります。
Term1 -> 0
Term2 -> 126
Term3 -> 222
....
この2つのファイルに加えて、各用語の [〜#〜] idf [〜#〜] および各ドキュメントの標準を格納するファイルを作成できます(通常は作成します)。
ケース(2)の場合、 Lucene (したがって、 Solr および ElasticSearch )がどのようにそれを行うかを簡単に説明しようと思います。
ファイル形式は、上記で説明したものと同じにすることができます。主な違いは、Luceneのようなシステムで新しいドキュメントにインデックスを作成する場合、インデックスを最初から再構築するのではなく、新しいドキュメントのみで新しいドキュメントを作成することです。したがって、何かにインデックスを付ける必要があるたびに、新しい分離されたインデックスでインデックスを作成します。
この「分割された」インデックスでクエリを実行するには、異なる各インデックスに対してクエリを(並行して)実行し、ユーザーに戻る前に結果をマージします。
Luceneはこれを「小さな」インデックスsegments
と呼んでいます。
ここでの明らかな懸念は、多くの小さなセグメントが非常に迅速に取得されることです。これを回避するには、セグメントをマージしてより大きなセグメントを作成するためのポリシーが必要になります。たとえば、N segments
を超える場合は、10 KBs
より小さいすべてのセグメントをマージすることを決定できます。