ハイライト

ハイライターを使用すると、検索結果の1つまたは複数のフィールドからハイライトされたスニペットを取得でき、ユーザーにクエリの一致がどこにあるかを示すことができます。ハイライトをリクエストすると、レスポンスには、ハイライトされたフィールドとハイライトされたフラグメントを含む追加の highlight 要素が各検索ヒットに含まれます。

ハイライターは、ハイライトする用語を抽出する際にクエリのブール論理を反映しません。したがって、複雑なブールクエリ(例:ネストされたブールクエリ、minimum_should_match を使用するクエリなど)では、クエリの一致に対応しない文書の一部がハイライトされる場合があります。

ハイライトにはフィールドの実際のコンテンツが必要です。フィールドが保存されていない場合(マッピングが storetrue に設定していない場合)、実際の _source がロードされ、関連するフィールドが _source から抽出されます。

たとえば、デフォルトのハイライターを使用して各検索ヒットの content フィールドのハイライトを取得するには、highlight オブジェクトをリクエストボディに含めて content フィールドを指定します。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "content": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "fields": {
  9. "content": {}
  10. }
  11. },
  12. )
  13. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. content: 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. fields: {
  10. content: {}
  11. }
  12. }
  13. }
  14. )
  15. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. content: "kimchy",
  5. },
  6. },
  7. highlight: {
  8. fields: {
  9. content: {},
  10. },
  11. },
  12. });
  13. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query": {
  4. "match": { "content": "kimchy" }
  5. },
  6. "highlight": {
  7. "fields": {
  8. "content": {}
  9. }
  10. }
  11. }

Elasticsearch は、unifiedplainfvh(ファストベクターハイライター)の 3 つのハイライターをサポートしています。各フィールドに使用するハイライター type を指定できます。

統一ハイライター

unified ハイライターは、Lucene 統一ハイライターを使用します。このハイライターは、テキストを文に分割し、BM25 アルゴリズムを使用して、個々の文をコーパス内の文書のようにスコア付けします。また、正確なフレーズおよびマルチターム(ファジー、プレフィックス、正規表現)ハイライトもサポートしています。unified ハイライターは、複数のフィールドからの一致を 1 つの結果に結合できます(matched_fields を参照)。これがデフォルトのハイライターです。

プレーンハイライター

plain ハイライターは、標準の Lucene ハイライターを使用します。これは、フレーズクエリにおける単語の重要性や単語の位置基準を理解することに関して、クエリの一致ロジックを反映しようとします。

plain ハイライターは、単一フィールド内の単純なクエリ一致をハイライトするのに最適です。クエリロジックを正確に反映するために、メモリ内に小さなインデックスを作成し、Lucene のクエリ実行プランナーを介して元のクエリ基準を再実行して、現在の文書の低レベルの一致情報にアクセスします。これは、ハイライトする必要があるすべてのフィールドとすべての文書に対して繰り返されます。複雑なクエリで多くの文書の多くのフィールドをハイライトしたい場合は、unified ハイライターを postings または term_vector フィールドで使用することをお勧めします。

ファストベクターハイライター

fvh ハイライターは、Lucene ファストベクターハイライターを使用します。このハイライターは、マッピングで term_vectorwith_positions_offsets に設定されているフィールドで使用できます。ファストベクターハイライターは、

  • boundary_scanner でカスタマイズできます。
  • インデックスのサイズを増やす term_vectorwith_positions_offsets に設定する必要があります。
  • 複数のフィールドからの一致を 1 つの結果に結合できます。matched_fields を参照してください。
  • 異なる位置での一致に異なる重みを割り当てることができ、フレーズ一致が用語一致の上にソートされるようにすることができます。これは、フレーズ一致を用語一致よりも強調するブースティングクエリをハイライトする際に便利です。

fvh ハイライターは、スパンクエリをサポートしていません。スパンクエリのサポートが必要な場合は、unified ハイライターなどの代替ハイライターを試してください。

オフセット戦略

クエリされている用語から意味のある検索スニペットを作成するには、ハイライターは元のテキスト内の各単語の開始および終了文字オフセットを知る必要があります。これらのオフセットは、次の方法で取得できます。

  • 投稿リスト。マッピングで index_optionsoffsets に設定されている場合、unified ハイライターはこの情報を使用して、テキストを再分析することなく文書をハイライトします。元のクエリを投稿に直接再実行し、一致するオフセットをインデックスから抽出し、収集をハイライトされた文書に制限します。これは、大きなフィールドを持つ場合に重要です。なぜなら、ハイライトするテキストを再分析する必要がないからです。また、term_vectors を使用するよりもディスクスペースが少なくて済みます。
  • 用語ベクター。マッピングで term_vector 情報が term_vectorwith_positions_offsets に設定することによって提供される場合、unified ハイライターは自動的に term_vector を使用してフィールドをハイライトします。これは特に大きなフィールド(> 1MB)や、prefixwildcard のようなマルチタームクエリをハイライトする際に高速です。なぜなら、各文書の用語の辞書にアクセスできるからです。fvh ハイライターは常に用語ベクターを使用します。
  • プレーンハイライト。これは、他の選択肢がない場合に unified によって使用されます。これは、メモリ内に小さなインデックスを作成し、Lucene のクエリ実行プランナーを介して元のクエリ基準を再実行して、現在の文書の低レベルの一致情報にアクセスします。これは、ハイライトする必要があるすべてのフィールドとすべての文書に対して繰り返されます。plain ハイライターは常にプレーンハイライトを使用します。

大きなテキストのプレーンハイライトには、かなりの時間とメモリが必要になる場合があります。これに対して保護するために、分析される最大文字数は 1000000 に制限されています。このデフォルトの制限は、特定のインデックスに対して index.highlight.max_analyzed_offset 設定で変更できます。

ハイライト設定

ハイライト設定は、グローバルレベルで設定でき、フィールドレベルでオーバーライドできます。

  • boundary_chars
  • 各境界文字を含む文字列。デフォルトは .,!? \t\n です。
  • boundary_max_scan
  • 境界文字をスキャンする距離。デフォルトは 20 です。

  • boundary_scanner
  • ハイライトされたフラグメントを分割する方法を指定します:charssentence、または wordunified および fvh ハイライターに対してのみ有効です。unified ハイライターのデフォルトは sentence です。fvh ハイライターのデフォルトは chars です。
    • chars
    • boundary_chars で指定された文字をハイライトの境界として使用します。boundary_max_scan 設定は、境界文字をスキャンする距離を制御します。fvh ハイライターに対してのみ有効です。
    • sentence
    • Java の BreakIterator によって決定された次の文の境界でハイライトされたフラグメントを分割します。boundary_scanner_locale で使用するロケールを指定できます。
      unified ハイライターと一緒に使用されると、sentence スキャナーは、fragment_size よりも大きい文を fragment_size の隣の最初の単語の境界で分割します。fragment_size を 0 に設定すると、文を分割しなくなります。
    • word
    • Java の BreakIterator によって決定された次の単語の境界でハイライトされたフラグメントを分割します。boundary_scanner_locale で使用するロケールを指定できます。
  • boundary_scanner_locale
  • 文と単語の境界を検索するために使用されるロケールを制御します。このパラメータは、"en-US""fr-FR""ja-JP" のような言語タグの形式を取ります。詳細は、ロケール言語タグ ドキュメントを参照してください。デフォルト値は Locale.ROOT です。
  • encoder
  • スニペットを HTML エンコードする必要があるかどうかを示します:default(エンコードなし)または html(スニペットテキストを HTML エスケープし、ハイライトタグを挿入します)
  • fields
  • ハイライトを取得するフィールドを指定します。ワイルドカードを使用してフィールドを指定できます。たとえば、comment_* を指定して、comment_ で始まるすべての textmatch_only_text、および keyword フィールドのハイライトを取得できます。
    テキスト、match_only_text、およびキーワードフィールドのみが、ワイルドカードを使用する場合にハイライトされます。カスタムマッパーを使用していても、フィールドでハイライトする必要がある場合は、そのフィールド名を明示的に指定する必要があります。
  • fragmenter
  • ハイライトスニペット内のテキストを分割する方法を指定します:simple または spanplain ハイライターに対してのみ有効です。デフォルトは span です。
  • force_source
  • 非推奨。このパラメータは効果がありません。
    • simple
    • テキストを同じサイズのフラグメントに分割します。
    • span
    • テキストを同じサイズのフラグメントに分割しますが、ハイライトされた用語の間でテキストを分割しないようにします。これは、フレーズをクエリする際に役立ちます。デフォルト。
  • fragment_offset
  • ハイライトを開始したいマージンを制御します。fvh ハイライターを使用する場合にのみ有効です。
  • fragment_size
  • 文字数でのハイライトフラグメントのサイズ。デフォルトは 100 です。
  • highlight_query
  • 検索クエリ以外のクエリの一致をハイライトします。これは、リスコアクエリを使用する場合に特に便利です。なぜなら、デフォルトではハイライトによって考慮されないからです。
    Elasticsearch は、highlight_query が検索クエリを含むかどうかを検証しないため、正当なクエリ結果がハイライトされないように定義することが可能です。一般的には、highlight_query の一部として検索クエリを含めるべきです。
  • matched_fields
  • 複数のフィールドの一致を組み合わせて、単一のフィールドをハイライトします。これは、同じ文字列を異なる方法で分析するマルチフィールドに最も直感的です。unified および fvh ハイライターに対して有効ですが、このオプションの動作は各ハイライターで異なります。

unified ハイライターの場合:

  • matched_fields 配列には、ハイライトしたい元のフィールドを含めないでください。元のフィールドは自動的に matched_fields に追加され、ハイライト時にその一致を除外する方法はありません。
  • matched_fields および元のフィールドは、異なる戦略(offsets の有無、term_vectors の有無)でインデックス化できます。
  • 一致が組み合わされる元のフィールドのみがロードされるため、そのフィールドのみが storeyes に設定することで利益を得ます。

fvh ハイライターの場合:

  • matched_fields 配列には、必要に応じて元のフィールドが含まれる場合と含まれない場合があります。元のフィールドの一致をハイライトに含めたい場合は、それを matched_fields 配列に追加します。
  • すべての matched_fieldsterm_vectorwith_positions_offsets に設定する必要があります。
  • 一致が組み合わされる元のフィールドのみがロードされるため、そのフィールドのみが storeyes に設定することで利益を得ます。

    • no_match_size
    • 一致するフラグメントがない場合にフィールドの先頭から返したいテキストの量。デフォルトは 0(何も返されません)。
    • number_of_fragments
    • 返すフラグメントの最大数。フラグメント数が 0 に設定されている場合、フラグメントは返されません。代わりに、フィールドの全内容がハイライトされて返されます。これは、タイトルや住所などの短いテキストをハイライトする必要がある場合に便利ですが、断片化は必要ありません。number_of_fragments が 0 の場合、fragment_size は無視されます。デフォルトは 5 です。
    • order
    • score に設定すると、スコアによってハイライトされたフラグメントがソートされます。デフォルトでは、フラグメントはフィールドに表示される順序で出力されます(順序:none)。このオプションを score に設定すると、最も関連性の高いフラグメントが最初に出力されます。各ハイライターは、関連性スコアを計算するための独自のロジックを適用します。異なるハイライターが最良のフラグメントを見つける方法の詳細については、ドキュメント ハイライターが内部でどのように機能するか を参照してください。
    • phrase_limit
    • 文書内で考慮される一致するフレーズの数を制御します。fvh ハイライターがあまりにも多くのフレーズを分析して過剰なメモリを消費するのを防ぎます。matched_fields を使用する場合、各一致フィールドに対して phrase_limit フレーズが考慮されます。制限を上げると、クエリ時間が増加し、メモリを多く消費します。fvh ハイライターでのみサポートされています。デフォルトは 256 です。
    • pre_tags
    • ハイライトされたテキストに使用する HTML タグを定義するために post_tags と組み合わせて使用します。デフォルトでは、ハイライトされたテキストは <em> および </em> タグでラップされます。文字列の配列として指定します。
    • post_tags
    • ハイライトされたテキストに使用する HTML タグを定義するために pre_tags と組み合わせて使用します。デフォルトでは、ハイライトされたテキストは <em> および </em> タグでラップされます。文字列の配列として指定します。
    • require_field_match
    • デフォルトでは、クエリ一致を含むフィールドのみがハイライトされます。すべてのフィールドをハイライトするには、require_field_matchfalse に設定します。デフォルトは true です。

  • max_analyzed_offset
  • デフォルトでは、ハイライトリクエストの分析される最大文字数は、index.highlight.max_analyzed_offset 設定で定義された値によって制限されており、文字数がこの制限を超えるとエラーが返されます。この設定が非負の値に設定されている場合、ハイライトはこの定義された最大制限で停止し、残りのテキストは処理されず、したがってハイライトされず、エラーも返されません。max_analyzed_offset クエリ設定は、index.highlight.max_analyzed_offset をオーバーライドしません。クエリ設定よりも低い値に設定されている場合は、index.highlight.max_analyzed_offset が優先されます。
  • tags_schema
  • ビルトインタグスキーマを使用するには styled に設定します。styled スキーマは、次の pre_tags を定義し、post_tags</em> として定義します。

Html

  1. <em class="hlt1">, <em class="hlt2">, <em class="hlt3">,
  2. <em class="hlt4">, <em class="hlt5">, <em class="hlt6">,
  3. <em class="hlt7">, <em class="hlt8">, <em class="hlt9">,
  4. <em class="hlt10">

  • type
  • 使用するハイライター:unifiedplain、または fvh。デフォルトは unified です。

ハイライトの例

グローバル設定をオーバーライド

ハイライター設定をグローバルに指定し、個々のフィールドに対して選択的にオーバーライドできます。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "number_of_fragments": 3,
  9. "fragment_size": 150,
  10. "fields": {
  11. "body": {
  12. "pre_tags": [
  13. "<em>"
  14. ],
  15. "post_tags": [
  16. "</em>"
  17. ]
  18. },
  19. "blog.title": {
  20. "number_of_fragments": 0
  21. },
  22. "blog.author": {
  23. "number_of_fragments": 0
  24. },
  25. "blog.comment": {
  26. "number_of_fragments": 5,
  27. "order": "score"
  28. }
  29. }
  30. },
  31. )
  32. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. number_of_fragments: 3,
  10. fragment_size: 150,
  11. fields: {
  12. body: {
  13. pre_tags: [
  14. '<em>'
  15. ],
  16. post_tags: [
  17. '</em>'
  18. ]
  19. },
  20. 'blog.title' => {
  21. number_of_fragments: 0
  22. },
  23. 'blog.author' => {
  24. number_of_fragments: 0
  25. },
  26. 'blog.comment' => {
  27. number_of_fragments: 5,
  28. order: 'score'
  29. }
  30. }
  31. }
  32. }
  33. )
  34. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. number_of_fragments: 3,
  9. fragment_size: 150,
  10. fields: {
  11. body: {
  12. pre_tags: ["<em>"],
  13. post_tags: ["</em>"],
  14. },
  15. "blog.title": {
  16. number_of_fragments: 0,
  17. },
  18. "blog.author": {
  19. number_of_fragments: 0,
  20. },
  21. "blog.comment": {
  22. number_of_fragments: 5,
  23. order: "score",
  24. },
  25. },
  26. },
  27. });
  28. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "number_of_fragments" : 3,
  8. "fragment_size" : 150,
  9. "fields" : {
  10. "body" : { "pre_tags" : ["<em>"], "post_tags" : ["</em>"] },
  11. "blog.title" : { "number_of_fragments" : 0 },
  12. "blog.author" : { "number_of_fragments" : 0 },
  13. "blog.comment" : { "number_of_fragments" : 5, "order" : "score" }
  14. }
  15. }
  16. }

ハイライトクエリを指定

ハイライト時に追加情報を考慮するために highlight_query を指定できます。たとえば、次のクエリは、highlight_query に検索クエリとリスコアクエリの両方を含みます。highlight_query がない場合、ハイライトは検索クエリのみを考慮します。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "comment": {
  5. "query": "foo bar"
  6. }
  7. }
  8. },
  9. rescore={
  10. "window_size": 50,
  11. "query": {
  12. "rescore_query": {
  13. "match_phrase": {
  14. "comment": {
  15. "query": "foo bar",
  16. "slop": 1
  17. }
  18. }
  19. },
  20. "rescore_query_weight": 10
  21. }
  22. },
  23. source=False,
  24. highlight={
  25. "order": "score",
  26. "fields": {
  27. "comment": {
  28. "fragment_size": 150,
  29. "number_of_fragments": 3,
  30. "highlight_query": {
  31. "bool": {
  32. "must": {
  33. "match": {
  34. "comment": {
  35. "query": "foo bar"
  36. }
  37. }
  38. },
  39. "should": {
  40. "match_phrase": {
  41. "comment": {
  42. "query": "foo bar",
  43. "slop": 1,
  44. "boost": 10
  45. }
  46. }
  47. },
  48. "minimum_should_match": 0
  49. }
  50. }
  51. }
  52. }
  53. },
  54. )
  55. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. comment: {
  6. query: 'foo bar'
  7. }
  8. }
  9. },
  10. rescore: {
  11. window_size: 50,
  12. query: {
  13. rescore_query: {
  14. match_phrase: {
  15. comment: {
  16. query: 'foo bar',
  17. slop: 1
  18. }
  19. }
  20. },
  21. rescore_query_weight: 10
  22. }
  23. },
  24. _source: false,
  25. highlight: {
  26. order: 'score',
  27. fields: {
  28. comment: {
  29. fragment_size: 150,
  30. number_of_fragments: 3,
  31. highlight_query: {
  32. bool: {
  33. must: {
  34. match: {
  35. comment: {
  36. query: 'foo bar'
  37. }
  38. }
  39. },
  40. should: {
  41. match_phrase: {
  42. comment: {
  43. query: 'foo bar',
  44. slop: 1,
  45. boost: 10
  46. }
  47. }
  48. },
  49. minimum_should_match: 0
  50. }
  51. }
  52. }
  53. }
  54. }
  55. }
  56. )
  57. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. comment: {
  5. query: "foo bar",
  6. },
  7. },
  8. },
  9. rescore: {
  10. window_size: 50,
  11. query: {
  12. rescore_query: {
  13. match_phrase: {
  14. comment: {
  15. query: "foo bar",
  16. slop: 1,
  17. },
  18. },
  19. },
  20. rescore_query_weight: 10,
  21. },
  22. },
  23. _source: false,
  24. highlight: {
  25. order: "score",
  26. fields: {
  27. comment: {
  28. fragment_size: 150,
  29. number_of_fragments: 3,
  30. highlight_query: {
  31. bool: {
  32. must: {
  33. match: {
  34. comment: {
  35. query: "foo bar",
  36. },
  37. },
  38. },
  39. should: {
  40. match_phrase: {
  41. comment: {
  42. query: "foo bar",
  43. slop: 1,
  44. boost: 10,
  45. },
  46. },
  47. },
  48. minimum_should_match: 0,
  49. },
  50. },
  51. },
  52. },
  53. },
  54. });
  55. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query": {
  4. "match": {
  5. "comment": {
  6. "query": "foo bar"
  7. }
  8. }
  9. },
  10. "rescore": {
  11. "window_size": 50,
  12. "query": {
  13. "rescore_query": {
  14. "match_phrase": {
  15. "comment": {
  16. "query": "foo bar",
  17. "slop": 1
  18. }
  19. }
  20. },
  21. "rescore_query_weight": 10
  22. }
  23. },
  24. "_source": false,
  25. "highlight": {
  26. "order": "score",
  27. "fields": {
  28. "comment": {
  29. "fragment_size": 150,
  30. "number_of_fragments": 3,
  31. "highlight_query": {
  32. "bool": {
  33. "must": {
  34. "match": {
  35. "comment": {
  36. "query": "foo bar"
  37. }
  38. }
  39. },
  40. "should": {
  41. "match_phrase": {
  42. "comment": {
  43. "query": "foo bar",
  44. "slop": 1,
  45. "boost": 10.0
  46. }
  47. }
  48. },
  49. "minimum_should_match": 0
  50. }
  51. }
  52. }
  53. }
  54. }
  55. }

ハイライタータイプを設定

type フィールドは、特定のハイライタータイプを強制することを可能にします。許可される値は、unifiedplain、および fvh です。次の例は、プレーンハイライターの使用を強制するものです:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "fields": {
  9. "comment": {
  10. "type": "plain"
  11. }
  12. }
  13. },
  14. )
  15. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. fields: {
  10. comment: {
  11. type: 'plain'
  12. }
  13. }
  14. }
  15. }
  16. )
  17. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. fields: {
  9. comment: {
  10. type: "plain",
  11. },
  12. },
  13. },
  14. });
  15. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query": {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight": {
  7. "fields": {
  8. "comment": { "type": "plain" }
  9. }
  10. }
  11. }

ハイライトタグを構成

デフォルトでは、ハイライトはハイライトされたテキストを <em> および </em> でラップします。これは、pre_tags および post_tags を設定することで制御できます。たとえば:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "pre_tags": [
  9. "<tag1>"
  10. ],
  11. "post_tags": [
  12. "</tag1>"
  13. ],
  14. "fields": {
  15. "body": {}
  16. }
  17. },
  18. )
  19. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. pre_tags: [
  10. '<tag1>'
  11. ],
  12. post_tags: [
  13. '</tag1>'
  14. ],
  15. fields: {
  16. body: {}
  17. }
  18. }
  19. }
  20. )
  21. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. pre_tags: ["<tag1>"],
  9. post_tags: ["</tag1>"],
  10. fields: {
  11. body: {},
  12. },
  13. },
  14. });
  15. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "pre_tags" : ["<tag1>"],
  8. "post_tags" : ["</tag1>"],
  9. "fields" : {
  10. "body" : {}
  11. }
  12. }
  13. }

ファストベクターハイライターを使用する場合、追加のタグを指定でき、”重要度” が順序付けられます。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "pre_tags": [
  9. "<tag1>",
  10. "<tag2>"
  11. ],
  12. "post_tags": [
  13. "</tag1>",
  14. "</tag2>"
  15. ],
  16. "fields": {
  17. "body": {}
  18. }
  19. },
  20. )
  21. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. pre_tags: [
  10. '<tag1>',
  11. '<tag2>'
  12. ],
  13. post_tags: [
  14. '</tag1>',
  15. '</tag2>'
  16. ],
  17. fields: {
  18. body: {}
  19. }
  20. }
  21. }
  22. )
  23. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. pre_tags: ["<tag1>", "<tag2>"],
  9. post_tags: ["</tag1>", "</tag2>"],
  10. fields: {
  11. body: {},
  12. },
  13. },
  14. });
  15. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "pre_tags" : ["<tag1>", "<tag2>"],
  8. "post_tags" : ["</tag1>", "</tag2>"],
  9. "fields" : {
  10. "body" : {}
  11. }
  12. }
  13. }

ビルトイン styled タグスキーマを使用することもできます:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "tags_schema": "styled",
  9. "fields": {
  10. "comment": {}
  11. }
  12. },
  13. )
  14. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. tags_schema: 'styled',
  10. fields: {
  11. comment: {}
  12. }
  13. }
  14. }
  15. )
  16. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. tags_schema: "styled",
  9. fields: {
  10. comment: {},
  11. },
  12. },
  13. });
  14. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "tags_schema" : "styled",
  8. "fields" : {
  9. "comment" : {}
  10. }
  11. }
  12. }

すべてのフィールドでハイライト

デフォルトでは、クエリ一致を含むフィールドのみがハイライトされます。require_field_matchfalse に設定すると、すべてのフィールドがハイライトされます。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "require_field_match": False,
  9. "fields": {
  10. "body": {
  11. "pre_tags": [
  12. "<em>"
  13. ],
  14. "post_tags": [
  15. "</em>"
  16. ]
  17. }
  18. }
  19. },
  20. )
  21. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. require_field_match: false,
  10. fields: {
  11. body: {
  12. pre_tags: [
  13. '<em>'
  14. ],
  15. post_tags: [
  16. '</em>'
  17. ]
  18. }
  19. }
  20. }
  21. }
  22. )
  23. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. require_field_match: false,
  9. fields: {
  10. body: {
  11. pre_tags: ["<em>"],
  12. post_tags: ["</em>"],
  13. },
  14. },
  15. },
  16. });
  17. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "require_field_match": false,
  8. "fields": {
  9. "body" : { "pre_tags" : ["<em>"], "post_tags" : ["</em>"] }
  10. }
  11. }
  12. }

複数フィールドの一致を組み合わせる

unified および fvh ハイライターでサポートされています。

統一ハイライターとファストベクターハイライターは、複数のフィールドの一致を組み合わせて単一のフィールドをハイライトできます。これは、同じ文字列を異なる方法で分析するマルチフィールドに最も直感的です。

次の例では、commentstandard アナライザーによって分析され、comment.englishenglish アナライザーによって分析されます。

Python

  1. resp = client.indices.create(
  2. index="index1",
  3. mappings={
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "analyzer": "standard",
  8. "fields": {
  9. "english": {
  10. "type": "text",
  11. "analyzer": "english"
  12. }
  13. }
  14. }
  15. }
  16. },
  17. )
  18. print(resp)

Js

  1. const response = await client.indices.create({
  2. index: "index1",
  3. mappings: {
  4. properties: {
  5. comment: {
  6. type: "text",
  7. analyzer: "standard",
  8. fields: {
  9. english: {
  10. type: "text",
  11. analyzer: "english",
  12. },
  13. },
  14. },
  15. },
  16. },
  17. });
  18. console.log(response);

コンソール

  1. PUT index1
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "analyzer": "standard",
  8. "fields": {
  9. "english": {
  10. "type": "text",
  11. "analyzer": "english"
  12. }
  13. }
  14. }
  15. }
  16. }
  17. }

Python

  1. resp = client.bulk(
  2. index="index1",
  3. refresh=True,
  4. operations=[
  5. {
  6. "index": {
  7. "_id": "doc1"
  8. }
  9. },
  10. {
  11. "comment": "run with scissors"
  12. },
  13. {
  14. "index": {
  15. "_id": "doc2"
  16. }
  17. },
  18. {
  19. "comment": "running with scissors"
  20. }
  21. ],
  22. )
  23. print(resp)

Js

  1. const response = await client.bulk({
  2. index: "index1",
  3. refresh: "true",
  4. operations: [
  5. {
  6. index: {
  7. _id: "doc1",
  8. },
  9. },
  10. {
  11. comment: "run with scissors",
  12. },
  13. {
  14. index: {
  15. _id: "doc2",
  16. },
  17. },
  18. {
  19. comment: "running with scissors",
  20. },
  21. ],
  22. });
  23. console.log(response);

コンソール

  1. PUT index1/_bulk?refresh=true
  2. {"index": {"_id": "doc1" }}
  3. {"comment": "run with scissors"}
  4. { "index" : {"_id": "doc2"} }
  5. {"comment": "running with scissors"}

Python

  1. resp = client.search(
  2. index="index1",
  3. query={
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": [
  7. "comment",
  8. "comment.english"
  9. ]
  10. }
  11. },
  12. highlight={
  13. "order": "score",
  14. "fields": {
  15. "comment": {}
  16. }
  17. },
  18. )
  19. print(resp)

Js

  1. const response = await client.search({
  2. index: "index1",
  3. query: {
  4. query_string: {
  5. query: "running with scissors",
  6. fields: ["comment", "comment.english"],
  7. },
  8. },
  9. highlight: {
  10. order: "score",
  11. fields: {
  12. comment: {},
  13. },
  14. },
  15. });
  16. console.log(response);

コンソール

  1. GET index1/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": ["comment", "comment.english"]
  7. }
  8. },
  9. "highlight": {
  10. "order": "score",
  11. "fields": {
  12. "comment": {}
  13. }
  14. }
  15. }

上記のリクエストは「ハサミで走る」と「ハサミで走る」を両方一致させ、「走っている」と「ハサミ」をハイライトしますが、「走る」はハイライトしません。両方のフレーズが大きな文書に出現する場合、「ハサミで走っている」がフラグメントリストで「ハサミで走る」よりも上にソートされます。なぜなら、そのフラグメントに一致が多いからです。

コンソール-結果

  1. {
  2. ...
  3. "hits" : {
  4. "total" : {
  5. "value" : 2,
  6. "relation" : "eq"
  7. },
  8. "max_score": 1.0577903,
  9. "hits" : [
  10. {
  11. "_index" : "index1",
  12. "_id" : "doc2",
  13. "_score" : 1.0577903,
  14. "_source" : {
  15. "comment" : "running with scissors"
  16. },
  17. "highlight" : {
  18. "comment" : [
  19. "<em>running</em> <em>with</em> <em>scissors</em>"
  20. ]
  21. }
  22. },
  23. {
  24. "_index" : "index1",
  25. "_id" : "doc1",
  26. "_score" : 0.36464313,
  27. "_source" : {
  28. "comment" : "run with scissors"
  29. },
  30. "highlight" : {
  31. "comment" : [
  32. "run <em>with</em> <em>scissors</em>"
  33. ]
  34. }
  35. }
  36. ]
  37. }
  38. }

以下のリクエストは「走る」と「走っている」と「ハサミ」をハイライトします。なぜなら、matched_fields パラメータが、ハイライトのために comment.english フィールドの一致を元の comment フィールドの一致と組み合わせる必要があることを指示しているからです。

Python

  1. resp = client.search(
  2. index="index1",
  3. query={
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": [
  7. "comment",
  8. "comment.english"
  9. ]
  10. }
  11. },
  12. highlight={
  13. "order": "score",
  14. "fields": {
  15. "comment": {
  16. "matched_fields": [
  17. "comment.english"
  18. ]
  19. }
  20. }
  21. },
  22. )
  23. print(resp)

Js

  1. const response = await client.search({
  2. index: "index1",
  3. query: {
  4. query_string: {
  5. query: "running with scissors",
  6. fields: ["comment", "comment.english"],
  7. },
  8. },
  9. highlight: {
  10. order: "score",
  11. fields: {
  12. comment: {
  13. matched_fields: ["comment.english"],
  14. },
  15. },
  16. },
  17. });
  18. console.log(response);

コンソール

  1. GET index1/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": ["comment", "comment.english"]
  7. }
  8. },
  9. "highlight": {
  10. "order": "score",
  11. "fields": {
  12. "comment": {
  13. "matched_fields": ["comment.english"]
  14. }
  15. }
  16. }
  17. }

コンソール-結果

  1. {
  2. ...
  3. "hits" : {
  4. "total" : {
  5. "value" : 2,
  6. "relation" : "eq"
  7. },
  8. "max_score": 1.0577903,
  9. "hits" : [
  10. {
  11. "_index" : "index1",
  12. "_id" : "doc2",
  13. "_score" : 1.0577903,
  14. "_source" : {
  15. "comment" : "running with scissors"
  16. },
  17. "highlight" : {
  18. "comment" : [
  19. "<em>running</em> <em>with</em> <em>scissors</em>"
  20. ]
  21. }
  22. },
  23. {
  24. "_index" : "index1",
  25. "_id" : "doc1",
  26. "_score" : 0.36464313,
  27. "_source" : {
  28. "comment" : "run with scissors"
  29. },
  30. "highlight" : {
  31. "comment" : [
  32. "<em>run</em> <em>with</em> <em>scissors</em>"
  33. ]
  34. }
  35. }
  36. ]
  37. }
  38. }

次の例では、commentstandard アナライザーによって分析され、comment.englishenglish アナライザーによって分析されます。

Python

  1. resp = client.indices.create(
  2. index="index2",
  3. mappings={
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "analyzer": "standard",
  8. "term_vector": "with_positions_offsets",
  9. "fields": {
  10. "english": {
  11. "type": "text",
  12. "analyzer": "english",
  13. "term_vector": "with_positions_offsets"
  14. }
  15. }
  16. }
  17. }
  18. },
  19. )
  20. print(resp)

Js

  1. const response = await client.indices.create({
  2. index: "index2",
  3. mappings: {
  4. properties: {
  5. comment: {
  6. type: "text",
  7. analyzer: "standard",
  8. term_vector: "with_positions_offsets",
  9. fields: {
  10. english: {
  11. type: "text",
  12. analyzer: "english",
  13. term_vector: "with_positions_offsets",
  14. },
  15. },
  16. },
  17. },
  18. },
  19. });
  20. console.log(response);

コンソール

  1. PUT index2
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "analyzer": "standard",
  8. "term_vector": "with_positions_offsets",
  9. "fields": {
  10. "english": {
  11. "type": "text",
  12. "analyzer": "english",
  13. "term_vector": "with_positions_offsets"
  14. }
  15. }
  16. }
  17. }
  18. }
  19. }

Python

  1. resp = client.bulk(
  2. index="index2",
  3. refresh=True,
  4. operations=[
  5. {
  6. "index": {
  7. "_id": "doc1"
  8. }
  9. },
  10. {
  11. "comment": "run with scissors"
  12. },
  13. {
  14. "index": {
  15. "_id": "doc2"
  16. }
  17. },
  18. {
  19. "comment": "running with scissors"
  20. }
  21. ],
  22. )
  23. print(resp)

Js

  1. const response = await client.bulk({
  2. index: "index2",
  3. refresh: "true",
  4. operations: [
  5. {
  6. index: {
  7. _id: "doc1",
  8. },
  9. },
  10. {
  11. comment: "run with scissors",
  12. },
  13. {
  14. index: {
  15. _id: "doc2",
  16. },
  17. },
  18. {
  19. comment: "running with scissors",
  20. },
  21. ],
  22. });
  23. console.log(response);

コンソール

  1. PUT index2/_bulk?refresh=true
  2. {"index": {"_id": "doc1" }}
  3. {"comment": "run with scissors"}
  4. { "index" : {"_id": "doc2"} }
  5. {"comment": "running with scissors"}

Python

  1. resp = client.search(
  2. index="index2",
  3. query={
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": [
  7. "comment",
  8. "comment.english"
  9. ]
  10. }
  11. },
  12. highlight={
  13. "order": "score",
  14. "fields": {
  15. "comment": {
  16. "type": "fvh"
  17. }
  18. }
  19. },
  20. )
  21. print(resp)

Js

  1. const response = await client.search({
  2. index: "index2",
  3. query: {
  4. query_string: {
  5. query: "running with scissors",
  6. fields: ["comment", "comment.english"],
  7. },
  8. },
  9. highlight: {
  10. order: "score",
  11. fields: {
  12. comment: {
  13. type: "fvh",
  14. },
  15. },
  16. },
  17. });
  18. console.log(response);

コンソール

  1. GET index2/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": ["comment", "comment.english"]
  7. }
  8. },
  9. "highlight": {
  10. "order": "score",
  11. "fields": {
  12. "comment": {
  13. "type" : "fvh"
  14. }
  15. }
  16. }
  17. }

上記のリクエストは「ハサミで走る」と「ハサミで走る」を両方一致させ、「走っている」と「ハサミ」をハイライトしますが、「走る」はハイライトしません。両方のフレーズが大きな文書に出現する場合、「ハサミで走っている」がフラグメントリストで「ハサミで走る」よりも上にソートされます。なぜなら、そのフラグメントに一致が多いからです。

コンソール-結果

  1. {
  2. ...
  3. "hits" : {
  4. "total" : {
  5. "value" : 2,
  6. "relation" : "eq"
  7. },
  8. "max_score": 1.0577903,
  9. "hits" : [
  10. {
  11. "_index" : "index2",
  12. "_id" : "doc2",
  13. "_score" : 1.0577903,
  14. "_source" : {
  15. "comment" : "running with scissors"
  16. },
  17. "highlight" : {
  18. "comment" : [
  19. "<em>running</em> <em>with</em> <em>scissors</em>"
  20. ]
  21. }
  22. },
  23. {
  24. "_index" : "index2",
  25. "_id" : "doc1",
  26. "_score" : 0.36464313,
  27. "_source" : {
  28. "comment" : "run with scissors"
  29. },
  30. "highlight" : {
  31. "comment" : [
  32. "run <em>with</em> <em>scissors</em>"
  33. ]
  34. }
  35. }
  36. ]
  37. }
  38. }

以下のリクエストは「走る」や「ハサミ」をハイライトしませんが、一致が組み合わされるフィールド(matched_fields)を一致フィールドにリストしないことが問題ないことを示しています。

Python

  1. resp = client.search(
  2. index="index2",
  3. query={
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": [
  7. "comment",
  8. "comment.english"
  9. ]
  10. }
  11. },
  12. highlight={
  13. "order": "score",
  14. "fields": {
  15. "comment": {
  16. "type": "fvh",
  17. "matched_fields": [
  18. "comment",
  19. "comment.english"
  20. ]
  21. }
  22. }
  23. },
  24. )
  25. print(resp)

Js

  1. const response = await client.search({
  2. index: "index2",
  3. query: {
  4. query_string: {
  5. query: "running with scissors",
  6. fields: ["comment", "comment.english"],
  7. },
  8. },
  9. highlight: {
  10. order: "score",
  11. fields: {
  12. comment: {
  13. type: "fvh",
  14. matched_fields: ["comment", "comment.english"],
  15. },
  16. },
  17. },
  18. });
  19. console.log(response);

コンソール

  1. GET index2/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": ["comment", "comment.english"]
  7. }
  8. },
  9. "highlight": {
  10. "order": "score",
  11. "fields": {
  12. "comment": {
  13. "type" : "fvh",
  14. "matched_fields": ["comment", "comment.english"]
  15. }
  16. }
  17. }
  18. }

コンソール-結果

  1. {
  2. ...
  3. "hits" : {
  4. "total" : {
  5. "value" : 2,
  6. "relation" : "eq"
  7. },
  8. "max_score": 1.0577903,
  9. "hits" : [
  10. {
  11. "_index" : "index2",
  12. "_id" : "doc2",
  13. "_score" : 1.0577903,
  14. "_source" : {
  15. "comment" : "running with scissors"
  16. },
  17. "highlight" : {
  18. "comment" : [
  19. "<em>running</em> <em>with</em> <em>scissors</em>"
  20. ]
  21. }
  22. },
  23. {
  24. "_index" : "index2",
  25. "_id" : "doc1",
  26. "_score" : 0.36464313,
  27. "_source" : {
  28. "comment" : "run with scissors"
  29. },
  30. "highlight" : {
  31. "comment" : [
  32. "<em>run</em> <em>with</em> <em>scissors</em>"
  33. ]
  34. }
  35. }
  36. ]
  37. }
  38. }

以下のリクエストは「走る」や「ハサミ」をハイライトしませんが、一致が組み合わされるフィールド(comment.english)を一致フィールドにリストしないことが問題ないことを示しています。

Python

  1. resp = client.search(
  2. index="index2",
  3. query={
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": [
  7. "comment",
  8. "comment.english"
  9. ]
  10. }
  11. },
  12. highlight={
  13. "order": "score",
  14. "fields": {
  15. "comment.english": {
  16. "type": "fvh",
  17. "matched_fields": [
  18. "comment"
  19. ]
  20. }
  21. }
  22. },
  23. )
  24. print(resp)

Js

  1. const response = await client.search({
  2. index: "index2",
  3. query: {
  4. query_string: {
  5. query: "running with scissors",
  6. fields: ["comment", "comment.english"],
  7. },
  8. },
  9. highlight: {
  10. order: "score",
  11. fields: {
  12. "comment.english": {
  13. type: "fvh",
  14. matched_fields: ["comment"],
  15. },
  16. },
  17. },
  18. });
  19. console.log(response);

コンソール

  1. GET index2/_search
  2. {
  3. "query": {
  4. "query_string": {
  5. "query": "running with scissors",
  6. "fields": ["comment", "comment.english"]
  7. }
  8. },
  9. "highlight": {
  10. "order": "score",
  11. "fields": {
  12. "comment.english": {
  13. "type" : "fvh",
  14. "matched_fields": ["comment"]
  15. }
  16. }
  17. }
  18. }

コンソール-結果

  1. {
  2. ...
  3. "hits" : {
  4. "total" : {
  5. "value" : 2,
  6. "relation" : "eq"
  7. },
  8. "max_score": 1.0577903,
  9. "hits" : [
  10. {
  11. "_index" : "index2",
  12. "_id" : "doc2",
  13. "_score" : 1.0577903,
  14. "_source" : {
  15. "comment" : "running with scissors"
  16. },
  17. "highlight" : {
  18. "comment.english" : [
  19. "<em>running</em> <em>with</em> <em>scissors</em>"
  20. ]
  21. }
  22. },
  23. {
  24. "_index" : "index2",
  25. "_id" : "doc1",
  26. "_score" : 0.36464313,
  27. "_source" : {
  28. "comment" : "run with scissors"
  29. },
  30. "highlight" : {
  31. "comment.english" : [
  32. "run <em>with</em> <em>scissors</em>"
  33. ]
  34. }
  35. }
  36. ]
  37. }
  38. }

matched_fields を非空配列に設定することには少しオーバーヘッドがあるため、常にmatched_fields を使用することをお勧めします。

Js

  1. "highlight": {
  2. "fields": {
  3. "comment": {}
  4. }
  5. }

to

Js

  1. "highlight": {
  2. "fields": {
  3. "comment": {
  4. "matched_fields": ["comment"],
  5. "type" : "fvh"
  6. }
  7. }
  8. }

技術的には、一致が組み合わされるフィールドと同じ基盤の文字列を共有しないフィールドを matched_fields に追加することも問題ありません。結果はあまり意味をなさないかもしれませんし、一致の1つがテキストの端を超えている場合、全体のクエリが失敗します。

ハイライトされたフィールドの明示的な順序

Elasticsearch は、送信された順序でフィールドをハイライトしますが、JSON スペックに従って、オブジェクトは順序がありません。フィールドがハイライトされる順序を明示的に指定する必要がある場合は、fields を配列として指定します:

Python

  1. resp = client.search(
  2. highlight={
  3. "fields": [
  4. {
  5. "title": {}
  6. },
  7. {
  8. "text": {}
  9. }
  10. ]
  11. },
  12. )
  13. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. highlight: {
  4. fields: [
  5. {
  6. title: {}
  7. },
  8. {
  9. text: {}
  10. }
  11. ]
  12. }
  13. }
  14. )
  15. puts response

Js

  1. const response = await client.search({
  2. highlight: {
  3. fields: [
  4. {
  5. title: {},
  6. },
  7. {
  8. text: {},
  9. },
  10. ],
  11. },
  12. });
  13. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "highlight": {
  4. "fields": [
  5. { "title": {} },
  6. { "text": {} }
  7. ]
  8. }
  9. }

Elasticsearch に組み込まれているハイライターは、フィールドがハイライトされる順序を気にしませんが、プラグインは気にするかもしれません。

ハイライトされたフラグメントを制御

ハイライトされた各フィールドは、ハイライトされたフラグメントのサイズを文字数で制御できます(デフォルトは 100)、および返すフラグメントの最大数(デフォルトは 5)。たとえば:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "fields": {
  9. "comment": {
  10. "fragment_size": 150,
  11. "number_of_fragments": 3
  12. }
  13. }
  14. },
  15. )
  16. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. fields: {
  10. comment: {
  11. fragment_size: 150,
  12. number_of_fragments: 3
  13. }
  14. }
  15. }
  16. }
  17. )
  18. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. fields: {
  9. comment: {
  10. fragment_size: 150,
  11. number_of_fragments: 3,
  12. },
  13. },
  14. },
  15. });
  16. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "fields" : {
  8. "comment" : {"fragment_size" : 150, "number_of_fragments" : 3}
  9. }
  10. }
  11. }

この上に、ハイライトされたフラグメントをスコアでソートする必要があることを指定することも可能です:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "order": "score",
  9. "fields": {
  10. "comment": {
  11. "fragment_size": 150,
  12. "number_of_fragments": 3
  13. }
  14. }
  15. },
  16. )
  17. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. order: 'score',
  10. fields: {
  11. comment: {
  12. fragment_size: 150,
  13. number_of_fragments: 3
  14. }
  15. }
  16. }
  17. }
  18. )
  19. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. order: "score",
  9. fields: {
  10. comment: {
  11. fragment_size: 150,
  12. number_of_fragments: 3,
  13. },
  14. },
  15. },
  16. });
  17. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "order" : "score",
  8. "fields" : {
  9. "comment" : {"fragment_size" : 150, "number_of_fragments" : 3}
  10. }
  11. }
  12. }

number_of_fragments 値が 0 に設定されている場合、フラグメントは生成されず、代わりにフィールドの全内容が返され、もちろんハイライトされます。これは、短いテキスト(文書のタイトルや住所など)をハイライトする必要がある場合に非常に便利ですが、断片化は必要ありません。この場合、fragment_size は無視されます。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "fields": {
  9. "body": {},
  10. "blog.title": {
  11. "number_of_fragments": 0
  12. }
  13. }
  14. },
  15. )
  16. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. fields: {
  10. body: {},
  11. 'blog.title' => {
  12. number_of_fragments: 0
  13. }
  14. }
  15. }
  16. }
  17. )
  18. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. fields: {
  9. body: {},
  10. "blog.title": {
  11. number_of_fragments: 0,
  12. },
  13. },
  14. },
  15. });
  16. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query" : {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight" : {
  7. "fields" : {
  8. "body" : {},
  9. "blog.title" : {"number_of_fragments" : 0}
  10. }
  11. }
  12. }

fvh を使用する場合、ハイライトを開始するマージンを制御するために fragment_offset パラメータを使用できます。

一致するフラグメントがない場合のデフォルトは、何も返さないことです。代わりに、no_match_size(デフォルト 0)を設定することで、返したいテキストの長さに基づいてフィールドの先頭からテキストのスニペットを返すことができます。実際の長さは、単語の境界で分割しようとするため、指定された長さよりも短くなる場合や長くなる場合があります。

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "user.id": "kimchy"
  5. }
  6. },
  7. highlight={
  8. "fields": {
  9. "comment": {
  10. "fragment_size": 150,
  11. "number_of_fragments": 3,
  12. "no_match_size": 150
  13. }
  14. }
  15. },
  16. )
  17. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. 'user.id' => 'kimchy'
  6. }
  7. },
  8. highlight: {
  9. fields: {
  10. comment: {
  11. fragment_size: 150,
  12. number_of_fragments: 3,
  13. no_match_size: 150
  14. }
  15. }
  16. }
  17. }
  18. )
  19. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. "user.id": "kimchy",
  5. },
  6. },
  7. highlight: {
  8. fields: {
  9. comment: {
  10. fragment_size: 150,
  11. number_of_fragments: 3,
  12. no_match_size: 150,
  13. },
  14. },
  15. },
  16. });
  17. console.log(response);

コンソール

  1. GET /_search
  2. {
  3. "query": {
  4. "match": { "user.id": "kimchy" }
  5. },
  6. "highlight": {
  7. "fields": {
  8. "comment": {
  9. "fragment_size": 150,
  10. "number_of_fragments": 3,
  11. "no_match_size": 150
  12. }
  13. }
  14. }
  15. }

投稿リストを使用してハイライト

ここでは、投稿を使用してハイライトを許可するためにインデックスマッピングで comment フィールドを設定する例を示します:

Python

  1. resp = client.indices.create(
  2. index="example",
  3. mappings={
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "index_options": "offsets"
  8. }
  9. }
  10. },
  11. )
  12. print(resp)

Ruby

  1. response = client.indices.create(
  2. index: 'example',
  3. body: {
  4. mappings: {
  5. properties: {
  6. comment: {
  7. type: 'text',
  8. index_options: 'offsets'
  9. }
  10. }
  11. }
  12. }
  13. )
  14. puts response

Js

  1. const response = await client.indices.create({
  2. index: "example",
  3. mappings: {
  4. properties: {
  5. comment: {
  6. type: "text",
  7. index_options: "offsets",
  8. },
  9. },
  10. },
  11. });
  12. console.log(response);

コンソール

  1. PUT /example
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comment" : {
  6. "type": "text",
  7. "index_options" : "offsets"
  8. }
  9. }
  10. }
  11. }

ここでは、comment フィールドを設定して term_vectors を使用してハイライトを許可する例を示します(これによりインデックスが大きくなります):

Python

  1. resp = client.indices.create(
  2. index="example",
  3. mappings={
  4. "properties": {
  5. "comment": {
  6. "type": "text",
  7. "term_vector": "with_positions_offsets"
  8. }
  9. }
  10. },
  11. )
  12. print(resp)

Ruby

  1. response = client.indices.create(
  2. index: 'example',
  3. body: {
  4. mappings: {
  5. properties: {
  6. comment: {
  7. type: 'text',
  8. term_vector: 'with_positions_offsets'
  9. }
  10. }
  11. }
  12. }
  13. )
  14. puts response

Js

  1. const response = await client.indices.create({
  2. index: "example",
  3. mappings: {
  4. properties: {
  5. comment: {
  6. type: "text",
  7. term_vector: "with_positions_offsets",
  8. },
  9. },
  10. },
  11. });
  12. console.log(response);

コンソール

  1. PUT /example
  2. {
  3. "mappings": {
  4. "properties": {
  5. "comment" : {
  6. "type": "text",
  7. "term_vector" : "with_positions_offsets"
  8. }
  9. }
  10. }
  11. }

プレーンハイライターのためのフラグメンターを指定

plain ハイライターを使用する場合、simple および span フラグメンターの間で選択できます:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. query={
  4. "match_phrase": {
  5. "message": "number 1"
  6. }
  7. },
  8. highlight={
  9. "fields": {
  10. "message": {
  11. "type": "plain",
  12. "fragment_size": 15,
  13. "number_of_fragments": 3,
  14. "fragmenter": "simple"
  15. }
  16. }
  17. },
  18. )
  19. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. query: {
  5. match_phrase: {
  6. message: 'number 1'
  7. }
  8. },
  9. highlight: {
  10. fields: {
  11. message: {
  12. type: 'plain',
  13. fragment_size: 15,
  14. number_of_fragments: 3,
  15. fragmenter: 'simple'
  16. }
  17. }
  18. }
  19. }
  20. )
  21. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. query: {
  4. match_phrase: {
  5. message: "number 1",
  6. },
  7. },
  8. highlight: {
  9. fields: {
  10. message: {
  11. type: "plain",
  12. fragment_size: 15,
  13. number_of_fragments: 3,
  14. fragmenter: "simple",
  15. },
  16. },
  17. },
  18. });
  19. console.log(response);

Console

  1. GET my-index-000001/_search
  2. {
  3. "query": {
  4. "match_phrase": { "message": "number 1" }
  5. },
  6. "highlight": {
  7. "fields": {
  8. "message": {
  9. "type": "plain",
  10. "fragment_size": 15,
  11. "number_of_fragments": 3,
  12. "fragmenter": "simple"
  13. }
  14. }
  15. }
  16. }

Response:

Console-Result

  1. {
  2. ...
  3. "hits": {
  4. "total": {
  5. "value": 1,
  6. "relation": "eq"
  7. },
  8. "max_score": 1.6011951,
  9. "hits": [
  10. {
  11. "_index": "my-index-000001",
  12. "_id": "1",
  13. "_score": 1.6011951,
  14. "_source": {
  15. "message": "some message with the number 1",
  16. "context": "bar"
  17. },
  18. "highlight": {
  19. "message": [
  20. " with the <em>number</em>",
  21. " <em>1</em>"
  22. ]
  23. }
  24. }
  25. ]
  26. }
  27. }

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. query={
  4. "match_phrase": {
  5. "message": "number 1"
  6. }
  7. },
  8. highlight={
  9. "fields": {
  10. "message": {
  11. "type": "plain",
  12. "fragment_size": 15,
  13. "number_of_fragments": 3,
  14. "fragmenter": "span"
  15. }
  16. }
  17. },
  18. )
  19. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. query: {
  5. match_phrase: {
  6. message: 'number 1'
  7. }
  8. },
  9. highlight: {
  10. fields: {
  11. message: {
  12. type: 'plain',
  13. fragment_size: 15,
  14. number_of_fragments: 3,
  15. fragmenter: 'span'
  16. }
  17. }
  18. }
  19. }
  20. )
  21. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. query: {
  4. match_phrase: {
  5. message: "number 1",
  6. },
  7. },
  8. highlight: {
  9. fields: {
  10. message: {
  11. type: "plain",
  12. fragment_size: 15,
  13. number_of_fragments: 3,
  14. fragmenter: "span",
  15. },
  16. },
  17. },
  18. });
  19. console.log(response);

Console

  1. GET my-index-000001/_search
  2. {
  3. "query": {
  4. "match_phrase": { "message": "number 1" }
  5. },
  6. "highlight": {
  7. "fields": {
  8. "message": {
  9. "type": "plain",
  10. "fragment_size": 15,
  11. "number_of_fragments": 3,
  12. "fragmenter": "span"
  13. }
  14. }
  15. }
  16. }

Response:

Console-Result

  1. {
  2. ...
  3. "hits": {
  4. "total": {
  5. "value": 1,
  6. "relation": "eq"
  7. },
  8. "max_score": 1.6011951,
  9. "hits": [
  10. {
  11. "_index": "my-index-000001",
  12. "_id": "1",
  13. "_score": 1.6011951,
  14. "_source": {
  15. "message": "some message with the number 1",
  16. "context": "bar"
  17. },
  18. "highlight": {
  19. "message": [
  20. " with the <em>number</em> <em>1</em>"
  21. ]
  22. }
  23. }
  24. ]
  25. }
  26. }
  1. #### ハイライターの内部動作
  2. クエリとテキスト(ドキュメントフィールドの内容)が与えられた場合、ハイライターの目的は、クエリに対して最適なテキストの断片を見つけ、見つかった断片内のクエリ用語を強調表示することです。これには、ハイライターがいくつかの質問に対処する必要があります:
  3. - テキストをどのように断片に分割するか?
  4. - すべての断片の中から最適な断片をどのように見つけるか?
  5. - 断片内のクエリ用語をどのように強調表示するか?
  6. ## テキストをどのように断片に分割するか?
  7. 関連設定:`````fragment_size``````````fragmenter``````````type`````のハイライター、`````boundary_chars``````````boundary_max_scan``````````boundary_scanner``````````boundary_scanner_locale`````
  8. プレーンハイライターは、与えられたアナライザーを使用してテキストを分析し、トークンストリームを作成することから始まります。プレーンハイライターは、トークンストリームを断片に分割するために非常に単純なアルゴリズムを使用します。トークンストリーム内の用語をループし、現在の用語のend_offset`````fragment_size`````に作成された断片の数を掛けた値を超えるたびに、新しい断片が作成されます。ハイライトされた用語の間でテキストが分割されないようにするために、`````span`````フラグメンターを使用してもう少し計算が行われます。しかし、全体として、分割は`````fragment_size`````によってのみ行われるため、いくつかの断片は非常に奇妙なものになる可能性があります。たとえば、句読点で始まるものです。
  9. 統一またはFVHハイライターは、Java`````BreakIterator`````を利用してテキストを断片に分割するのが得意です。これにより、`````fragment_size`````が許可されている限り、断片が有効な文であることが保証されます。
  10. ## 最適な断片をどのように見つけるか?
  11. 関連設定:`````number_of_fragments`````
  12. 最適で最も関連性の高い断片を見つけるために、ハイライターは与えられたクエリに対して各断片にスコアを付ける必要があります。目標は、ドキュメントの*ヒット*を生成するのに参加した用語のみをスコアリングすることです。いくつかの複雑なクエリに対しては、これはまだ進行中の作業です。
  13. プレーンハイライターは、現在のトークンストリームからメモリ内インデックスを作成し、Luceneのクエリ実行プランナーを通じて元のクエリ基準を再実行して、現在のテキストの低レベルの一致情報にアクセスします。より複雑なクエリの場合、元のクエリはスパンクエリに変換される可能性があります。スパンクエリはフレーズをより正確に処理できます。この取得した低レベルの一致情報を使用して、各個別の断片にスコアを付けます。プレーンハイライターのスコアリング方法は非常に単純です。各断片は、この断片内で見つかったユニークなクエリ用語の数によってスコアが付けられます。個々の用語のスコアは、そのブーストに等しく、デフォルトでは1です。したがって、デフォルトでは、1つのユニークなクエリ用語を含む断片はスコア1を取得し、2つのユニークなクエリ用語を含む断片はスコア2を取得します。断片はそのスコアによってソートされ、最も高いスコアの断片が最初に出力されます。
  14. FVHは、テキストを分析してメモリ内インデックスを構築する必要がなく、事前にインデックスされたドキュメント用語ベクトルを使用し、それらの中からクエリに対応する用語を見つけます。FVHは、各断片に見つかったクエリ用語の数によってスコアを付けます。プレーンハイライターと同様に、個々の用語のスコアはそのブースト値に等しいです。プレーンハイライターとは対照的に、すべてのクエリ用語がカウントされ、ユニークな用語だけではありません。
  15. 統一ハイライターは、事前にインデックスされた用語ベクトルまたは事前にインデックスされた用語オフセットを使用できます。利用可能な場合は、そうでなければ、プレーンハイライターと同様に、テキストからメモリ内インデックスを作成する必要があります。統一ハイライターは、断片にスコアを付けるためにBM25スコアリングモデルを使用します。
  16. ## 断片内のクエリ用語をどのように強調表示するか?
  17. 関連設定:`````pre-tags``````````post-tags`````
  18. 目標は、ドキュメントの*ヒット*を生成するのに参加した用語のみを強調表示することです。いくつかの複雑なブールクエリに対しては、これはまだ進行中の作業です。ハイライターはクエリのブールロジックを反映せず、葉(用語、フレーズ、接頭辞など)クエリのみを抽出します。
  19. プレーンハイライターは、トークンストリームと元のテキストを与えられた場合、元のテキストを再構成して、前のステップからの低レベルの一致情報構造に含まれるトークンストリームの用語のみを強調表示します。
  20. FVHと統一ハイライターは、いくつかの生の形式で断片を表す中間データ構造を使用し、実際のテキストでそれらを埋めます。
  21. ハイライターは、`````pre-tags``````````post-tags`````を使用して強調表示された用語をエンコードします。
  22. ## 統一ハイライターの作業の例
  23. 統一ハイライターがどのように機能するかを詳しく見てみましょう。
  24. まず、`````content`````というテキストフィールドを持つインデックスを作成します。これは、`````english`````アナライザーを使用してインデックスされ、オフセットや用語ベクトルなしでインデックスされます。
  25. #### Js
  26. ``````js
  27. PUT test_index
  28. {
  29. "mappings": {
  30. "properties": {
  31. "content": {
  32. "type": "text",
  33. "analyzer": "english"
  34. }
  35. }
  36. }
  37. }
  38. `

次のドキュメントをインデックスに追加します:

Js

  1. PUT test_index/_doc/doc1
  2. {
  3. "content" : "For you I'm only a fox like a hundred thousand other foxes. But if you tame me, we'll need each other. You'll be the only boy in the world for me. I'll be the only fox in the world for you."
  4. }

次のクエリをハイライトリクエストで実行しました:

Js

  1. GET test_index/_search
  2. {
  3. "query": {
  4. "match_phrase" : {"content" : "only fox"}
  5. },
  6. "highlight": {
  7. "type" : "unified",
  8. "number_of_fragments" : 3,
  9. "fields": {
  10. "content": {}
  11. }
  12. }
  13. }
  1. ``````bash
  2. {"token":"onli","start_offset":12,"end_offset":16,"position":3},
  3. {"token":"fox","start_offset":19,"end_offset":22,"position":5},
  4. {"token":"fox","start_offset":53,"end_offset":58,"position":11},
  5. {"token":"onli","start_offset":117,"end_offset":121,"position":24},
  6. {"token":"onli","start_offset":159,"end_offset":163,"position":34},
  7. {"token":"fox","start_offset":164,"end_offset":167,"position":35}
  8. `

私たちの複雑なフレーズクエリはスパンクエリに変換されます:spanNear([text:onli, text:fox], 0, true)、これは「onli」と「fox」という用語が互いに0距離以内で、指定された順序であることを意味します。スパンクエリは、以前に作成されたメモリ内インデックスに対して実行され、次の一致を見つけます:

  1. {"term":"onli", "start_offset":159, "end_offset":163},
  2. {"term":"fox", "start_offset":164, "end_offset":167}

私たちの例では、単一の一致が得られましたが、複数の一致がある可能性があります。一致が得られると、統一ハイライターはフィールドのテキストを「パッセージ」と呼ばれるものに分割します。各パッセージには少なくとも1つの一致が含まれている必要があります。統一ハイライターは、JavaのBreakIteratorを使用して、各パッセージがfragment_sizeを超えない限り、完全な文を表すことを保証します。私たちの例では、次のプロパティを持つ単一のパッセージが得られました(ここではプロパティのサブセットのみを示しています):

  1. Passage:
  2. startOffset: 147
  3. endOffset: 189
  4. score: 3.7158387
  5. matchStarts: [159, 164]
  6. matchEnds: [163, 167]
  7. numMatches: 2

パッセージにはスコアがあり、これはパッセージ用に適応されたBM25スコアリング式を使用して計算されます。スコアにより、ユーザーが要求したnumber_of_fragmentsよりも多くのパッセージが利用可能な場合、最も高いスコアのパッセージを選択できます。スコアは、ユーザーが要求した場合、order: "score"によってパッセージをソートすることも可能にします。

最後のステップとして、統一ハイライターはフィールドのテキストから各パッセージに対応する文字列を抽出します:

  1. "I'll be the only fox in the world for you."

そして、すべての一致をこの文字列内で、パッセージのmatchStartsおよびmatchEnds情報を使用して、およびタグでフォーマットします:

  1. I'll be the <em>only</em> <em>fox</em> in the world for you.

このようにフォーマットされた文字列は、ユーザーに返されるハイライターの最終結果です。