要約すると、 ElasticSearchをお勧めしますが、問題を分解して、実装方法について説明しましょう。
これにはいくつかの部分があります。
- ドキュメントからテキストを抽出してインデックスに登録できるようにする
- このテキストを全文検索として利用できるようにする
- ドキュメントの強調表示されたスニペットを返す
- ドキュメント内のどこでそれらのスニペットがページングを可能にすることがわかっているかを知る
- 完全なドキュメントを返す
ElasticSearchが提供できるもの:
- ElasticSearch(Solrなど)はTikaを使用して、さまざまなドキュメント形式からテキストとメタデータを抽出します
- 明らかに、強力な全文検索を提供します。特定のフィールド(たとえば、コンテンツよりも重要なタイトル)、ngramなどの関連性をステミング、ブーストして、適切な言語で各ドキュメントを分析するように構成できます。つまり、標準のLuceneのものです。
- 検索結果ごとに強調表示されたスニペットを返すことができます
- それらのスニペットがドキュメントのどこにあるかはわかりません
- 元のドキュメントを添付ファイルとして保存することも、抽出したテキストを保存して返すこともできます。ただし、ページではなく、ドキュメント全体が返されます。
ドキュメント全体を添付ファイルとしてElasticSearchに送信するだけで、全文検索が可能になります。しかし、こだわりのポイントは上記の(4)と(5)です。つまり、ドキュメント内のどこにいるかを知り、ドキュメントの一部を返すことです。
where-am-Iの目的にはおそらく個々のページを保存するだけで十分ですが(段落レベルまで同様に下げることもできます)、検索キーワードが表示された場合でも、検索結果にドキュメントが返されるようにグループ化する必要があります。別のページに。
最初にインデックス作成の部分:ElasticSearchにドキュメントを保存します:
- Tika(または使い慣れたもの)を使用して、各ドキュメントからテキストを抽出します。プレーンテキストまたはHTMLのままにして、フォーマットを保持します。(XMLは忘れてください、必要ありません)。
- また、各ドキュメントのメタデータを抽出します:タイトル、作成者、章、言語、日付など
- 元のドキュメントをファイルシステムに保存し、後で提供できるようにパスを記録します
- ElasticSearchで、すべてのメタデータと、場合によってはチャプターのリストを含む「doc」ドキュメントにインデックスを付けます
各ページを「ページ」ドキュメントとしてインデックス付けします。これには、次のものが含まれます。
- 「doc」ドキュメントのIDを含む親フィールド(以下の「親子関係」を参照)
- テキスト
- ページ番号
- 多分章のタイトルや番号
- 検索可能にしたいメタデータ
今検索のため。これをどのように行うかは、結果をどのように表示するかによって異なります。ページごと、またはドキュメントごとにグループ化されます。
ページごとの結果は簡単です。このクエリは、一致するページのリスト(各ページは完全に返されます)と、ページから強調表示されたスニペットのリストを返します。
curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d '
{
"query" : {
"text" : {
"text" : "interesting keywords"
}
},
"highlight" : {
"fields" : {
"text" : {}
}
}
}
'
「doc」でグループ化された結果をテキストのハイライトとともに表示するのは少し注意が必要です。単一のクエリで実行することはできませんが、クライアント側で少しグループ化することでそこに到達できます。1つのアプローチは次のとおりです。
ステップ1:top-children-queryを実行して、子( "page")がクエリに最も一致する親( "doc")を見つけます。
curl -XGET 'http://127.0.0.1:9200/my_index/doc/_search?pretty=1' -d '
{
"query" : {
"top_children" : {
"query" : {
"text" : {
"text" : "interesting keywords"
}
},
"score" : "sum",
"type" : "page",
"factor" : "5"
}
}
}
ステップ2:上記のクエリから「ドキュメント」IDを収集し、新しいクエリを発行して、一致する「ページ」ドキュメントからスニペットを取得します。
curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d '
{
"query" : {
"filtered" : {
"query" : {
"text" : {
"text" : "interesting keywords"
}
},
"filter" : {
"terms" : {
"doc_id" : [ 1,2,3],
}
}
}
},
"highlight" : {
"fields" : {
"text" : {}
}
}
}
'
ステップ3:アプリで、上記のクエリの結果をドキュメントごとにグループ化し、表示します。
2番目のクエリの検索結果を使用すると、表示できるページの全文がすでに表示されています。次のページに移動するには、次のページを検索するだけです。
curl -XGET 'http://127.0.0.1:9200/my_index/page/_search?pretty=1' -d '
{
"query" : {
"constant_score" : {
"filter" : {
"and" : [
{
"term" : {
"doc_id" : 1
}
},
{
"term" : {
"page" : 2
}
}
]
}
}
},
"size" : 1
}
'
または、「ページ」ドキュメントに$doc_id _ $page_num
(123_2など)で構成されるIDを指定すると、そのページを取得できます。
curl -XGET 'http://127.0.0.1:9200/my_index/page/123_2
親子関係:
通常、ES(およびほとんどのNoSQLソリューション)では、各ドキュメント/オブジェクトは独立しています-実際の関係はありません。ElasticSearchは、「ドキュメント」と「ページ」の間に親子関係を確立することにより、子ドキュメント(つまり「ページ」)が親ドキュメント(「ドキュメント」)と同じシャードに保存されるようにします。
これにより、「ページ」のコンテンツに基づいて最も一致する「ドキュメント」を見つけるtop-children-queryを実行できます。