Profile API

Profile APIはデバッグツールであり、検索実行に大きなオーバーヘッドを追加します。

検索リクエスト内の個々のコンポーネントの実行に関する詳細なタイミング情報を提供します。

Description

Profile APIは、ユーザーが検索リクエストが低レベルでどのように実行されるかを理解できるようにし、特定のリクエストが遅い理由を理解し、それを改善するための手段を講じることができるようにします。Profile APIは、他のことの中で、ネットワーク遅延、リクエストがキューで過ごす時間、またはコーディネートノードでのシャード応答のマージにかかる時間を測定しないことに注意してください。

Profile APIからの出力は、特に多くのシャードにわたって実行される複雑なリクエストに対して非常に冗長です。出力を理解するために、レスポンスを整形して表示することをお勧めします。

Examples

任意の _search リクエストは、トップレベルの profile パラメータを追加することでプロファイルできます:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. profile=True,
  4. query={
  5. "match": {
  6. "message": "GET /search"
  7. }
  8. },
  9. )
  10. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. profile: true,
  5. query: {
  6. match: {
  7. message: 'GET /search'
  8. }
  9. }
  10. }
  11. )
  12. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. profile: true,
  4. query: {
  5. match: {
  6. message: "GET /search",
  7. },
  8. },
  9. });
  10. console.log(response);

Console

  1. GET /my-index-000001/_search
  2. {
  3. "profile": true,
  4. "query" : {
  5. "match" : { "message" : "GET /search" }
  6. }
  7. }
トップレベルの profile パラメータを true に設定すると、検索のプロファイリングが有効になります。

APIは次の結果を返します:

Console-Result

  1. {
  2. "took": 25,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 5,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.17402273,
  16. "hits": [...]
  17. },
  18. "profile": {
  19. "shards": [
  20. {
  21. "id": "[q2aE02wS1R8qQFnYu6vDVQ][my-index-000001][0]",
  22. "node_id": "q2aE02wS1R8qQFnYu6vDVQ",
  23. "shard_id": 0,
  24. "index": "my-index-000001",
  25. "cluster": "(local)",
  26. "searches": [
  27. {
  28. "query": [
  29. {
  30. "type": "BooleanQuery",
  31. "description": "message:get message:search",
  32. "time_in_nanos" : 11972972,
  33. "breakdown" : {
  34. "set_min_competitive_score_count": 0,
  35. "match_count": 5,
  36. "shallow_advance_count": 0,
  37. "set_min_competitive_score": 0,
  38. "next_doc": 39022,
  39. "match": 4456,
  40. "next_doc_count": 5,
  41. "score_count": 5,
  42. "compute_max_score_count": 0,
  43. "compute_max_score": 0,
  44. "advance": 84525,
  45. "advance_count": 1,
  46. "score": 37779,
  47. "build_scorer_count": 2,
  48. "create_weight": 4694895,
  49. "shallow_advance": 0,
  50. "create_weight_count": 1,
  51. "build_scorer": 7112295,
  52. "count_weight": 0,
  53. "count_weight_count": 0
  54. },
  55. "children": [
  56. {
  57. "type": "TermQuery",
  58. "description": "message:get",
  59. "time_in_nanos": 3801935,
  60. "breakdown": {
  61. "set_min_competitive_score_count": 0,
  62. "match_count": 0,
  63. "shallow_advance_count": 3,
  64. "set_min_competitive_score": 0,
  65. "next_doc": 0,
  66. "match": 0,
  67. "next_doc_count": 0,
  68. "score_count": 5,
  69. "compute_max_score_count": 3,
  70. "compute_max_score": 32487,
  71. "advance": 5749,
  72. "advance_count": 6,
  73. "score": 16219,
  74. "build_scorer_count": 3,
  75. "create_weight": 2382719,
  76. "shallow_advance": 9754,
  77. "create_weight_count": 1,
  78. "build_scorer": 1355007,
  79. "count_weight": 0,
  80. "count_weight_count": 0
  81. }
  82. },
  83. {
  84. "type": "TermQuery",
  85. "description": "message:search",
  86. "time_in_nanos": 205654,
  87. "breakdown": {
  88. "set_min_competitive_score_count": 0,
  89. "match_count": 0,
  90. "shallow_advance_count": 3,
  91. "set_min_competitive_score": 0,
  92. "next_doc": 0,
  93. "match": 0,
  94. "next_doc_count": 0,
  95. "score_count": 5,
  96. "compute_max_score_count": 3,
  97. "compute_max_score": 6678,
  98. "advance": 12733,
  99. "advance_count": 6,
  100. "score": 6627,
  101. "build_scorer_count": 3,
  102. "create_weight": 130951,
  103. "shallow_advance": 2512,
  104. "create_weight_count": 1,
  105. "build_scorer": 46153,
  106. "count_weight": 0,
  107. "count_weight_count": 0
  108. }
  109. }
  110. ]
  111. }
  112. ],
  113. "rewrite_time": 451233,
  114. "collector": [
  115. {
  116. "name": "QueryPhaseCollector",
  117. "reason": "search_query_phase",
  118. "time_in_nanos": 775274,
  119. "children" : [
  120. {
  121. "name": "SimpleTopScoreDocCollector",
  122. "reason": "search_top_hits",
  123. "time_in_nanos": 775274
  124. }
  125. ]
  126. }
  127. ]
  128. }
  129. ],
  130. "aggregations": [],
  131. "fetch": {
  132. "type": "fetch",
  133. "description": "",
  134. "time_in_nanos": 660555,
  135. "breakdown": {
  136. "next_reader": 7292,
  137. "next_reader_count": 1,
  138. "load_stored_fields": 299325,
  139. "load_stored_fields_count": 5,
  140. "load_source": 3863,
  141. "load_source_count": 5
  142. },
  143. "debug": {
  144. "stored_fields": ["_id", "_routing", "_source"]
  145. },
  146. "children": [
  147. {
  148. "type" : "FetchFieldsPhase",
  149. "description" : "",
  150. "time_in_nanos" : 238762,
  151. "breakdown" : {
  152. "process_count" : 5,
  153. "process" : 227914,
  154. "next_reader" : 10848,
  155. "next_reader_count" : 1
  156. }
  157. },
  158. {
  159. "type": "FetchSourcePhase",
  160. "description": "",
  161. "time_in_nanos": 20443,
  162. "breakdown": {
  163. "next_reader": 745,
  164. "next_reader_count": 1,
  165. "process": 19698,
  166. "process_count": 5
  167. },
  168. "debug": {
  169. "fast_path": 5
  170. }
  171. },
  172. {
  173. "type": "StoredFieldsPhase",
  174. "description": "",
  175. "time_in_nanos": 5310,
  176. "breakdown": {
  177. "next_reader": 745,
  178. "next_reader_count": 1,
  179. "process": 4445,
  180. "process_count": 5
  181. }
  182. }
  183. ]
  184. }
  185. }
  186. ]
  187. }
  188. }
検索結果が返されますが、簡潔さのためにここでは省略されています。

単純なクエリでも、レスポンスは比較的複雑です。より複雑な例に進む前に、部分ごとに分解してみましょう。

プロファイルレスポンスの全体的な構造は次のとおりです:

Console-Result

  1. {
  2. "profile": {
  3. "shards": [
  4. {
  5. "id": "[q2aE02wS1R8qQFnYu6vDVQ][my-index-000001][0]",
  6. "node_id": "q2aE02wS1R8qQFnYu6vDVQ",
  7. "shard_id": 0,
  8. "index": "my-index-000001",
  9. "cluster": "(local)",
  10. "searches": [
  11. {
  12. "query": [...],
  13. "rewrite_time": 51443,
  14. "collector": [...]
  15. }
  16. ],
  17. "aggregations": [...],
  18. "fetch": {...}
  19. }
  20. ]
  21. }
  22. }
応答に参加した各シャードのプロファイルが返され、ユニークなIDで識別されます。
クエリがローカルクラスターで実行された場合、クラスター名はコンポジットIDから省略され、ここでは「(local)」とマークされます。リモート_clusterで実行されるプロファイルでは、クロスクラスター検索を使用して、「id」値は[q2aE02wS1R8qQFnYu6vDVQ][remote1:my-index-000001][0]のようになり、「cluster」値はremote1になります。
クエリのタイミングやその他のデバッグ情報。
累積リライト時間。
各コレクターの名前と呼び出しタイミング。
集約タイミング、呼び出し回数、デバッグ情報。
フェッチタイミングとデバッグ情報。

検索リクエストは、インデックス内の1つ以上のシャードに対して実行される可能性があり、検索は1つ以上のインデックスをカバーする可能性があるため、プロファイルレスポンスのトップレベル要素はshardオブジェクトの配列です。各シャードオブジェクトは、そのシャードを一意に識別するidをリストします。IDの形式は[nodeID][clusterName:indexName][shardID]です。検索がローカルクラスターに対して実行される場合、clusterNameは追加されず、形式は[nodeID][indexName][shardID]です。

プロファイル自体は、1つ以上の「検索」で構成される場合があります。「検索」とは、基盤となるLuceneインデックスに対して実行されるクエリです。ユーザーによって提出されたほとんどの検索リクエストは、Luceneインデックスに対して単一のsearchを実行するだけです。しかし、時折、グローバル集約を含むように複数の検索が実行されることがあります(これは、グローバルコンテキストのために二次的な「match_all」クエリを実行する必要があります)。

searchオブジェクト内には、プロファイル情報の2つの配列があります: query配列とcollector配列。searchオブジェクトの横には、集約のプロファイル情報を含むaggregationsオブジェクトがあります。将来的には、suggesthighlightなどのセクションが追加される可能性があります。

また、クエリのリライトに費やされた合計時間を示すrewriteメトリックもあります(ナノ秒単位)。

他の統計APIと同様に、Profile APIは人間が読みやすい出力をサポートしています。これは、クエリ文字列に?human=trueを追加することで有効にできます。この場合、出力には、丸められた人間が読みやすいタイミング情報を含む追加のtimeフィールドが含まれます(例: "time": "391,9ms""time": "123.3micros")。

Profiling Queries

Profile APIによって提供される詳細は、Luceneのクラス名や概念を直接公開しているため、結果を完全に解釈するにはかなりの高度なLuceneの知識が必要です。このページは、Luceneがクエリを実行する方法についての短期講座を提供し、Profile APIを使用してクエリを正常に診断およびデバッグできるようにすることを試みていますが、これはあくまで概要です。完全な理解のためには、Luceneのドキュメントや、場合によってはコードを参照してください。

とはいえ、遅いクエリを修正するために完全な理解が必要なわけではありません。特定のクエリのコンポーネントが遅いことがわかれば十分であり、advanceフェーズがそのクエリの原因である理由を必ずしも理解する必要はありません。

query Section

  1. [](#88b1a2cf17d7289db8c7e15d472efb4c)
  2. #### Console-Result
  3. ``````console-result
  4. "query": [
  5. {
  6. "type": "BooleanQuery",
  7. "description": "message:get message:search",
  8. "time_in_nanos": "11972972",
  9. "breakdown": {...},
  10. "children": [
  11. {
  12. "type": "TermQuery",
  13. "description": "message:get",
  14. "time_in_nanos": "3801935",
  15. "breakdown": {...}
  16. },
  17. {
  18. "type": "TermQuery",
  19. "description": "message:search",
  20. "time_in_nanos": "205654",
  21. "breakdown": {...}
  22. }
  23. ]
  24. }
  25. ]
  26. `
分解タイミングは簡潔さのために省略されています。

プロファイル構造に基づいて、matchクエリはLuceneによって2つのクローズを持つBooleanQueryにリライトされたことがわかります(両方ともTermQueryを保持しています)。typeフィールドはLuceneのクラス名を表示し、しばしばElasticsearchの同等の名前と一致します。descriptionフィールドはクエリのLucene説明テキストを表示し、クエリの部分を区別するのに役立ちます(例: message:getmessage:searchは両方ともTermQueryであり、そうでなければ同一に見えます)。

  1. `````breakdown`````フィールドは、時間がどのように使われたかについての詳細な統計を提供します。これについては後で見ていきます。最後に、`````children`````配列は、存在する可能性のあるサブクエリをリストします。2つの値(「get search」)を検索したため、BooleanQueryには2つの子TermQueriesがあります。これらは同一の情報(タイプ、時間、内訳など)を持っています。子は独自の子を持つことが許可されています。
  2. ### Timing Breakdown
  3. `````breakdown`````コンポーネントは、低レベルのLucene実行に関する詳細なタイミング統計をリストします:
  4. [](#f025cead6c1af8c26dd75e971dd82e1a)
  5. #### Console-Result
  6. ``````console-result
  7. "breakdown": {
  8. "set_min_competitive_score_count": 0,
  9. "match_count": 5,
  10. "shallow_advance_count": 0,
  11. "set_min_competitive_score": 0,
  12. "next_doc": 39022,
  13. "match": 4456,
  14. "next_doc_count": 5,
  15. "score_count": 5,
  16. "compute_max_score_count": 0,
  17. "compute_max_score": 0,
  18. "advance": 84525,
  19. "advance_count": 1,
  20. "score": 37779,
  21. "build_scorer_count": 2,
  22. "create_weight": 4694895,
  23. "shallow_advance": 0,
  24. "create_weight_count": 1,
  25. "build_scorer": 7112295,
  26. "count_weight": 0,
  27. "count_weight_count": 0
  28. }
  29. `

タイミングは壁時計のナノ秒でリストされ、全く正規化されていません。全体のtime_in_nanosに関するすべての注意事項がここに適用されます。内訳の意図は、A) Luceneのどの機械が実際に時間を消費しているか、B) 様々なコンポーネント間の時間の違いの大きさを感じ取ることです。全体の時間と同様に、内訳はすべての子の時間を含みます。

統計の意味は次のとおりです:

All parameters:

create_weight Luceneのクエリは、複数のIndexSearchers間で再利用可能でなければなりません(特定のLuceneインデックスに対して検索を実行するエンジンと考えてください)。これにより、Luceneは厄介な状況に置かれます。多くのクエリは、使用されるインデックスに関連する一時的な状態/統計を蓄積する必要がありますが、Query契約では不変でなければならないと規定されています。
これを回避するために、Luceneは各クエリにWeightオブジェクトを生成するように要求します。これは、この特定の(IndexSearcher、Query)タプルに関連する状態を保持する一時的なコンテキストオブジェクトとして機能します。weightメトリックは、このプロセスにかかる時間を示します
build_scorer このパラメータは、クエリのScorerを構築するのにかかる時間を示します。Scorerは、一致するドキュメントを反復処理し、ドキュメントごとにスコアを生成するメカニズムです(例: 「foo」がドキュメントにどれだけ一致するか)。
注意: これはScorerオブジェクトを生成するのに必要な時間を記録し、実際にドキュメントのスコアを付ける時間ではありません。一部のクエリは、最適化、複雑さなどに応じてScorerの初期化が早かったり遅かったりします。これにより、キャッシングに関連するタイミングが表示される場合もあります。
next_doc Luceneメソッドnext_docは、クエリに一致する次のドキュメントのDoc IDを返します。この統計は、次の一致がどのドキュメントであるかを決定するのにかかる時間を示します。このプロセスは、クエリの性質によって大きく異なります。Next_docは、advance()の便利な特殊形式であり、多くのLuceneクエリにとって便利です。これは、advance(docId() + 1)に相当します。
advance advanceはnext_docの「低レベル」バージョンです: 次の一致するドキュメントを見つけるという同じ目的を果たしますが、呼び出しクエリがスキップを特定して移動するなどの追加のタスクを実行する必要があります。
ただし、すべてのクエリがnext_docを使用できるわけではないため、advanceもそのクエリのためにタイミングが計測されます。結合(例: Booleanのmustクローズ)は、advanceの典型的な消費者です。
match フレーズクエリなどの一部のクエリは、「二段階」プロセスを使用してドキュメントに一致します。最初に、ドキュメントが「おおよそ」一致し、もしおおよそ一致すれば、より厳密(かつ高コスト)なプロセスで二度目にチェックされます。二段階の検証は、match統計が測定するものです。
たとえば、フレーズクエリは、フレーズ内のすべての用語がドキュメントに存在することを確認することによって、最初にドキュメントをおおよそチェックします。すべての用語が存在する場合、次に、用語がフレーズを形成するために順序通りであることを確認するために二段階の検証を実行します。これは、用語の存在を確認するよりも相対的に高コストです。
この二段階プロセスは、ほんの一握りのクエリでのみ使用されるため、match統計はしばしばゼロになります。
score これは、特定のドキュメントをそのScorerを介してスコア付けするのにかかる時間を記録します。
*_count 特定のメソッドの呼び出し回数を記録します。たとえば、"next_doc_count": 2,は、nextDoc()メソッドが2つの異なるドキュメントで呼び出されたことを意味します。これは、異なるクエリコンポーネント間のカウントを比較することによって、クエリがどれだけ選択的であるかを判断するのに役立ちます。

collectors Section

Collectors部分のレスポンスは、高レベルの実行詳細を示します。Luceneは、「Collector」を定義することによって機能し、一致するドキュメントのトラバース、スコアリング、および収集を調整します。Collectorsは、単一のクエリが集約結果を記録し、スコープのない「グローバル」クエリを実行し、クエリ後のフィルターを実行する方法でもあります。

前の例を見てみましょう:

Console-Result

  1. "collector": [
  2. {
  3. "name": "QueryPhaseCollector",
  4. "reason": "search_query_phase",
  5. "time_in_nanos": 775274,
  6. "children" : [
  7. {
  8. "name": "SimpleTopScoreDocCollector",
  9. "reason": "search_top_hits",
  10. "time_in_nanos": 775274
  11. }
  12. ]
  13. }
  14. ]

トップレベルのコレクターQueryPhaseCollectorがあり、子SimpleTopScoreDocCollectorを保持しています。SimpleTopScoreDocCollectorは、Elasticsearchで使用されるデフォルトの「スコアリングとソート」Collectorです。reasonフィールドは、クラス名の平易な英語の説明を提供しようとします。time_in_nanosは、クエリツリー内の時間と似ています: すべての子を含む壁時計の時間です。同様に、childrenはすべてのサブコレクターをリストします。集約が要求されると、QueryPhaseCollectorは集約を実行している理由aggregationを持つ追加の子コレクターを保持します。

Collectorの時間は、クエリの時間とは独立しています。これらは独立して計算、結合、正規化されます!Luceneの実行の性質上、Collectorの時間をクエリセクションに「マージ」することは不可能であるため、別々の部分に表示されます。

参考までに、さまざまなコレクターの理由は次のとおりです:

search_top_hits ドキュメントをスコアリングおよびソートするコレクター。これは最も一般的なコレクターであり、ほとんどの単純な検索で見られます。
search_count クエリに一致するドキュメントの数をカウントするだけのコレクターですが、ソースを取得しません。これは、size: 0が指定されているときに見られます。
search_query_phase クエリフェーズの一部として、トップヒットの収集と集約を組み込むコレクターです。これは、n一致するドキュメントが見つかった後に検索実行を終了することをサポートします(terminate_afterが指定されているとき)、およびnよりもスコアが高い一致するドキュメントのみを返すことができます(min_scoreが提供されているとき)。さらに、提供されたpost_filterに基づいて一致するトップヒットをフィルタリングできます。
search_timeout 指定された期間が経過した後に実行を停止するコレクターです。これは、timeoutトップレベルパラメータが指定されているときに見られます。
aggregation Elasticsearchがクエリスコープに対して集約を実行するために使用するコレクターです。単一のaggregationコレクターがすべての集約のためにドキュメントを収集するために使用されるため、名前の中に集約のリストが表示されます。

| global_aggregation | 指定されたクエリではなく、グローバルクエリスコープに対して集約を実行するコレクターです。グローバルスコープは実行されたクエリとは必然的に異なるため、データセット全体を収集するために独自のmatch_allクエリを実行する必要があります(これがクエリセクションに追加されるのが見られます)。

rewrite Section

Luceneのすべてのクエリは「リライト」プロセスを経ます。クエリ(およびそのサブクエリ)は1回以上リライトされる可能性があり、プロセスはクエリが変更を停止するまで続きます。このプロセスにより、Luceneは冗長なクローズを削除したり、より効率的な実行パスのために1つのクエリを置き換えたりするなどの最適化を実行できます。たとえば、Boolean → Boolean → TermQueryは、すべてのBooleanがこの場合不要であるため、TermQueryにリライトできます。

リライトプロセスは複雑で表示が難しいため、クエリは大きく変化する可能性があります。中間結果を表示するのではなく、合計リライト時間は単に値(ナノ秒単位)として表示されます。この値は累積的であり、リライトされるすべてのクエリの合計時間を含みます。

A more complex example

やや複雑なクエリと関連する結果を示すために、次のクエリをプロファイルできます:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. profile=True,
  4. query={
  5. "term": {
  6. "user.id": {
  7. "value": "elkbee"
  8. }
  9. }
  10. },
  11. aggs={
  12. "my_scoped_agg": {
  13. "terms": {
  14. "field": "http.response.status_code"
  15. }
  16. },
  17. "my_global_agg": {
  18. "global": {},
  19. "aggs": {
  20. "my_level_agg": {
  21. "terms": {
  22. "field": "http.response.status_code"
  23. }
  24. }
  25. }
  26. }
  27. },
  28. post_filter={
  29. "match": {
  30. "message": "search"
  31. }
  32. },
  33. )
  34. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. profile: true,
  5. query: {
  6. term: {
  7. 'user.id' => {
  8. value: 'elkbee'
  9. }
  10. }
  11. },
  12. aggregations: {
  13. my_scoped_agg: {
  14. terms: {
  15. field: 'http.response.status_code'
  16. }
  17. },
  18. my_global_agg: {
  19. global: {},
  20. aggregations: {
  21. my_level_agg: {
  22. terms: {
  23. field: 'http.response.status_code'
  24. }
  25. }
  26. }
  27. }
  28. },
  29. post_filter: {
  30. match: {
  31. message: 'search'
  32. }
  33. }
  34. }
  35. )
  36. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. profile: true,
  4. query: {
  5. term: {
  6. "user.id": {
  7. value: "elkbee",
  8. },
  9. },
  10. },
  11. aggs: {
  12. my_scoped_agg: {
  13. terms: {
  14. field: "http.response.status_code",
  15. },
  16. },
  17. my_global_agg: {
  18. global: {},
  19. aggs: {
  20. my_level_agg: {
  21. terms: {
  22. field: "http.response.status_code",
  23. },
  24. },
  25. },
  26. },
  27. },
  28. post_filter: {
  29. match: {
  30. message: "search",
  31. },
  32. },
  33. });
  34. console.log(response);

Console

  1. GET /my-index-000001/_search
  2. {
  3. "profile": true,
  4. "query": {
  5. "term": {
  6. "user.id": {
  7. "value": "elkbee"
  8. }
  9. }
  10. },
  11. "aggs": {
  12. "my_scoped_agg": {
  13. "terms": {
  14. "field": "http.response.status_code"
  15. }
  16. },
  17. "my_global_agg": {
  18. "global": {},
  19. "aggs": {
  20. "my_level_agg": {
  21. "terms": {
  22. "field": "http.response.status_code"
  23. }
  24. }
  25. }
  26. }
  27. },
  28. "post_filter": {
  29. "match": {
  30. "message": "search"
  31. }
  32. }
  33. }

この例には:

  • クエリ
  • スコープ付き集約
  • グローバル集約
  • ポストフィルター

APIは次の結果を返します:

Console-Result

  1. {
  2. ...
  3. "profile": {
  4. "shards": [
  5. {
  6. "id": "[P6xvulHtQRWuD4YnubWb7A][my-index-000001][0]",
  7. "node_id": "P6xvulHtQRWuD4YnubWb7A",
  8. "shard_id": 0,
  9. "index": "my-index-000001",
  10. "cluster": "(local)",
  11. "searches": [
  12. {
  13. "query": [
  14. {
  15. "type": "TermQuery",
  16. "description": "message:search",
  17. "time_in_nanos": 141618,
  18. "breakdown": {
  19. "set_min_competitive_score_count": 0,
  20. "match_count": 0,
  21. "shallow_advance_count": 0,
  22. "set_min_competitive_score": 0,
  23. "next_doc": 0,
  24. "match": 0,
  25. "next_doc_count": 0,
  26. "score_count": 0,
  27. "compute_max_score_count": 0,
  28. "compute_max_score": 0,
  29. "advance": 3942,
  30. "advance_count": 4,
  31. "count_weight_count": 0,
  32. "score": 0,
  33. "build_scorer_count": 2,
  34. "create_weight": 38380,
  35. "shallow_advance": 0,
  36. "count_weight": 0,
  37. "create_weight_count": 1,
  38. "build_scorer": 99296
  39. }
  40. },
  41. {
  42. "type": "TermQuery",
  43. "description": "user.id:elkbee",
  44. "time_in_nanos": 163081,
  45. "breakdown": {
  46. "set_min_competitive_score_count": 0,
  47. "match_count": 0,
  48. "shallow_advance_count": 0,
  49. "set_min_competitive_score": 0,
  50. "next_doc": 2447,
  51. "match": 0,
  52. "next_doc_count": 4,
  53. "score_count": 4,
  54. "compute_max_score_count": 0,
  55. "compute_max_score": 0,
  56. "advance": 3552,
  57. "advance_count": 1,
  58. "score": 5027,
  59. "count_weight_count": 0,
  60. "build_scorer_count": 2,
  61. "create_weight": 107840,
  62. "shallow_advance": 0,
  63. "count_weight": 0,
  64. "create_weight_count": 1,
  65. "build_scorer": 44215
  66. }
  67. }
  68. ],
  69. "rewrite_time": 4769,
  70. "collector": [
  71. {
  72. "name": "QueryPhaseCollector",
  73. "reason": "search_query_phase",
  74. "time_in_nanos": 1945072,
  75. "children": [
  76. {
  77. "name": "SimpleTopScoreDocCollector",
  78. "reason": "search_top_hits",
  79. "time_in_nanos": 22577
  80. },
  81. {
  82. "name": "AggregatorCollector: [my_scoped_agg, my_global_agg]",
  83. "reason": "aggregation",
  84. "time_in_nanos": 867617
  85. }
  86. ]
  87. }
  88. ]
  89. }
  90. ],
  91. "aggregations": [...],
  92. "fetch": {...}
  93. }
  94. ]
  95. }
  96. }
"aggregations"部分は省略されています。次のセクションでカバーされます。

ご覧のとおり、出力は以前よりもかなり冗長です。クエリの主要な部分がすべて表されています:

  • 1. 最初のTermQuery(user.id:elkbee)は、メインのtermクエリを表します。
  • 2. 2番目のTermQuery(message:search)は、post_filterクエリを表します。

コレクターツリーは非常に簡単で、通常のスコアリングSimpleTopScoreDocCollectorを保持する単一のQueryPhaseCollectorと、すべてのスコープ付き集約を実行するBucketCollectorWrapperを示しています。

Understanding MultiTermQuery output

  1. 本質的に、これらのクエリはセグメントごとに自分自身をリライトします。ワイルドカードクエリ`````b*`````を想像してみてください。これは、技術的には「b」文字で始まる任意のトークンに一致する可能性があります。すべての可能な組み合わせを列挙することは不可能なので、Luceneは評価されているセグメントのコンテキストでクエリをリライトします。たとえば、1つのセグメントには`````[bar, baz]`````トークンが含まれている可能性があるため、クエリは「bar」と「baz」のBooleanQueryの組み合わせにリライトされます。別のセグメントには`````[bakery]`````トークンのみが含まれている可能性があるため、クエリは「bakery」の単一のTermQueryにリライトされます。
  2. この動的なセグメントごとのリライトにより、クリーンなツリー構造が歪み、1つのクエリが次のクエリにどのようにリライトされるかを示すクリーンな「系譜」をもはや追跡できなくなります。現時点では、すべての詳細が混乱しすぎる場合は、そのクエリの子の詳細を折りたたむことをお勧めします。幸いなことに、すべてのタイミング統計は正確であり、レスポンスの物理的なレイアウトが正しくないだけなので、トップレベルのMultiTermQueryを分析し、その詳細が解釈するには難しすぎる場合は子を無視するだけで十分です。
  3. 将来的にはこの問題が解決されることを願っていますが、解決が難しい問題であり、まだ進行中です。 :)
  4. ### Profiling Aggregations
  5. #### aggregations Section
  6. `````aggregations`````セクションには、特定のシャードで実行された集約ツリーの詳細なタイミングが含まれています。この集約ツリーの全体的な構造は、元のElasticsearchリクエストに似ています。前のクエリを再度実行し、今回は集約プロファイルを見てみましょう:
  7. #### Python
  8. ``````python
  9. resp = client.search(
  10. index="my-index-000001",
  11. profile=True,
  12. query={
  13. "term": {
  14. "user.id": {
  15. "value": "elkbee"
  16. }
  17. }
  18. },
  19. aggs={
  20. "my_scoped_agg": {
  21. "terms": {
  22. "field": "http.response.status_code"
  23. }
  24. },
  25. "my_global_agg": {
  26. "global": {},
  27. "aggs": {
  28. "my_level_agg": {
  29. "terms": {
  30. "field": "http.response.status_code"
  31. }
  32. }
  33. }
  34. }
  35. },
  36. post_filter={
  37. "match": {
  38. "message": "search"
  39. }
  40. },
  41. )
  42. print(resp)
  43. `

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. body: {
  4. profile: true,
  5. query: {
  6. term: {
  7. 'user.id' => {
  8. value: 'elkbee'
  9. }
  10. }
  11. },
  12. aggregations: {
  13. my_scoped_agg: {
  14. terms: {
  15. field: 'http.response.status_code'
  16. }
  17. },
  18. my_global_agg: {
  19. global: {},
  20. aggregations: {
  21. my_level_agg: {
  22. terms: {
  23. field: 'http.response.status_code'
  24. }
  25. }
  26. }
  27. }
  28. },
  29. post_filter: {
  30. match: {
  31. message: 'search'
  32. }
  33. }
  34. }
  35. )
  36. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. profile: true,
  4. query: {
  5. term: {
  6. "user.id": {
  7. value: "elkbee",
  8. },
  9. },
  10. },
  11. aggs: {
  12. my_scoped_agg: {
  13. terms: {
  14. field: "http.response.status_code",
  15. },
  16. },
  17. my_global_agg: {
  18. global: {},
  19. aggs: {
  20. my_level_agg: {
  21. terms: {
  22. field: "http.response.status_code",
  23. },
  24. },
  25. },
  26. },
  27. },
  28. post_filter: {
  29. match: {
  30. message: "search",
  31. },
  32. },
  33. });
  34. console.log(response);

Console

  1. GET /my-index-000001/_search
  2. {
  3. "profile": true,
  4. "query": {
  5. "term": {
  6. "user.id": {
  7. "value": "elkbee"
  8. }
  9. }
  10. },
  11. "aggs": {
  12. "my_scoped_agg": {
  13. "terms": {
  14. "field": "http.response.status_code"
  15. }
  16. },
  17. "my_global_agg": {
  18. "global": {},
  19. "aggs": {
  20. "my_level_agg": {
  21. "terms": {
  22. "field": "http.response.status_code"
  23. }
  24. }
  25. }
  26. }
  27. },
  28. "post_filter": {
  29. "match": {
  30. "message": "search"
  31. }
  32. }
  33. }

これにより、次の集約プロファイル出力が得られます:

Console-Result

  1. {
  2. "profile": {
  3. "shards": [
  4. {
  5. "aggregations": [
  6. {
  7. "type": "NumericTermsAggregator",
  8. "description": "my_scoped_agg",
  9. "time_in_nanos": 79294,
  10. "breakdown": {
  11. "reduce": 0,
  12. "build_aggregation": 30885,
  13. "build_aggregation_count": 1,
  14. "initialize": 2623,
  15. "initialize_count": 1,
  16. "reduce_count": 0,
  17. "collect": 45786,
  18. "collect_count": 4,
  19. "build_leaf_collector": 18211,
  20. "build_leaf_collector_count": 1,
  21. "post_collection": 929,
  22. "post_collection_count": 1
  23. },
  24. "debug": {
  25. "total_buckets": 1,
  26. "result_strategy": "long_terms",
  27. "built_buckets": 1
  28. }
  29. },
  30. {
  31. "type": "GlobalAggregator",
  32. "description": "my_global_agg",
  33. "time_in_nanos": 104325,
  34. "breakdown": {
  35. "reduce": 0,
  36. "build_aggregation": 22470,
  37. "build_aggregation_count": 1,
  38. "initialize": 12454,
  39. "initialize_count": 1,
  40. "reduce_count": 0,
  41. "collect": 69401,
  42. "collect_count": 4,
  43. "build_leaf_collector": 8150,
  44. "build_leaf_collector_count": 1,
  45. "post_collection": 1584,
  46. "post_collection_count": 1
  47. },
  48. "debug": {
  49. "built_buckets": 1
  50. },
  51. "children": [
  52. {
  53. "type": "NumericTermsAggregator",
  54. "description": "my_level_agg",
  55. "time_in_nanos": 76876,
  56. "breakdown": {
  57. "reduce": 0,
  58. "build_aggregation": 13824,
  59. "build_aggregation_count": 1,
  60. "initialize": 1441,
  61. "initialize_count": 1,
  62. "reduce_count": 0,
  63. "collect": 61611,
  64. "collect_count": 4,
  65. "build_leaf_collector": 5564,
  66. "build_leaf_collector_count": 1,
  67. "post_collection": 471,
  68. "post_collection_count": 1
  69. },
  70. "debug": {
  71. "total_buckets": 1,
  72. "result_strategy": "long_terms",
  73. "built_buckets": 1
  74. }
  75. }
  76. ]
  77. }
  78. ]
  79. }
  80. ]
  81. }
  82. }

プロファイル構造から、my_scoped_aggNumericTermsAggregator(集約しているフィールドhttp.response.status_codeが数値フィールドであるため)として内部的に実行されていることがわかります。同じレベルで、GlobalAggregatormy_global_aggから来ているのが見えます。その集約には、NumericTermsAggregatorの2番目の項目の集約から来る子NumericTermsAggregatorがあります。

  1. 一部の集約は、集約の基盤となる実行の特徴を説明する専門的な`````debug`````情報を返す場合があります。これは、集約に取り組む人々にとって「役立つ」ものであり、他の人には役立たないと期待されるものです。これらはバージョン、集約、および集約実行戦略によって大きく異なる可能性があります。
  2. ### Timing Breakdown
  3. `````breakdown`````コンポーネントは、低レベルの実行に関する詳細な統計をリストします:
  4. #### Js
  5. ``````js
  6. "breakdown": {
  7. "reduce": 0,
  8. "build_aggregation": 30885,
  9. "build_aggregation_count": 1,
  10. "initialize": 2623,
  11. "initialize_count": 1,
  12. "reduce_count": 0,
  13. "collect": 45786,
  14. "collect_count": 4,
  15. "build_leaf_collector": 18211,
  16. "build_leaf_collector_count": 1
  17. }
  18. `

breakdownコンポーネント内の各プロパティは、集約の内部メソッドに対応しています。たとえば、build_leaf_collectorプロパティは、集約のgetLeafCollector()メソッドを実行するのにかかったナノ秒を測定します。_countで終わるプロパティは、特定のメソッドの呼び出し回数を記録します。たとえば、"collect_count": 2は、集約が2つの異なるドキュメントでcollect()を呼び出したことを意味します。reduceプロパティは将来の使用のために予約されており、常に0を返します。

タイミングは壁時計のナノ秒でリストされ、全く正規化されていません。全体のtimeに関するすべての注意事項がここに適用されます。内訳の意図は、A) Elasticsearchのどの機械が実際に時間を消費しているか、B) 様々なコンポーネント間の時間の違いの大きさを感じ取ることです。全体の時間と同様に、内訳はすべての子の時間を含みます。

Profiling Fetch

ドキュメントを取得したすべてのシャードには、プロファイル内にfetchセクションがあります。小さな検索を実行し、フェッチプロファイルを見てみましょう:

Python

  1. resp = client.search(
  2. index="my-index-000001",
  3. filter_path="profile.shards.fetch",
  4. profile=True,
  5. query={
  6. "term": {
  7. "user.id": {
  8. "value": "elkbee"
  9. }
  10. }
  11. },
  12. )
  13. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-index-000001',
  3. filter_path: 'profile.shards.fetch',
  4. body: {
  5. profile: true,
  6. query: {
  7. term: {
  8. 'user.id' => {
  9. value: 'elkbee'
  10. }
  11. }
  12. }
  13. }
  14. )
  15. puts response

Js

  1. const response = await client.search({
  2. index: "my-index-000001",
  3. filter_path: "profile.shards.fetch",
  4. profile: true,
  5. query: {
  6. term: {
  7. "user.id": {
  8. value: "elkbee",
  9. },
  10. },
  11. },
  12. });
  13. console.log(response);

Console

  1. GET /my-index-000001/_search?filter_path=profile.shards.fetch
  2. {
  3. "profile": true,
  4. "query": {
  5. "term": {
  6. "user.id": {
  7. "value": "elkbee"
  8. }
  9. }
  10. }
  11. }

そして、こちらがフェッチプロファイルです:

Console-Result

  1. {
  2. "profile": {
  3. "shards": [
  4. {
  5. "fetch": {
  6. "type": "fetch",
  7. "description": "",
  8. "time_in_nanos": 660555,
  9. "breakdown": {
  10. "next_reader": 7292,
  11. "next_reader_count": 1,
  12. "load_stored_fields": 299325,
  13. "load_stored_fields_count": 5,
  14. "load_source": 3863,
  15. "load_source_count": 5
  16. },
  17. "debug": {
  18. "stored_fields": ["_id", "_routing", "_source"]
  19. },
  20. "children": [
  21. {
  22. "type" : "FetchFieldsPhase",
  23. "description" : "",
  24. "time_in_nanos" : 238762,
  25. "breakdown" : {
  26. "process_count" : 5,
  27. "process" : 227914,
  28. "next_reader" : 10848,
  29. "next_reader_count" : 1
  30. }
  31. },
  32. {
  33. "type": "FetchSourcePhase",
  34. "description": "",
  35. "time_in_nanos": 20443,
  36. "breakdown": {
  37. "next_reader": 745,
  38. "next_reader_count": 1,
  39. "process": 19698,
  40. "process_count": 5
  41. },
  42. "debug": {
  43. "fast_path": 4
  44. }
  45. },
  46. {
  47. "type": "StoredFieldsPhase",
  48. "description": "",
  49. "time_in_nanos": 5310,
  50. "breakdown": {
  51. "next_reader": 745,
  52. "next_reader_count": 1,
  53. "process": 4445,
  54. "process_count": 5
  55. }
  56. }
  57. ]
  58. }
  59. }
  60. ]
  61. }
  62. }

これは、Elasticsearchがフェッチを実行する方法に関するデバッグ情報であり、リクエストごとやバージョンごとに変わる可能性があります。パッチバージョンでさえ、ここでの出力が変わる可能性があります。この一貫性の欠如がデバッグに役立つ理由です。

とにかく!time_in_nanosはフェッチフェーズの合計時間を測定します。breakdownは、next_readerでの各セグメントの準備のカウントと時間、およびload_stored_fieldsでの保存されたフィールドの読み込みにかかる時間を測定します。Debugには、特にstored_fieldsがフェッチが読み込む必要がある保存されたフィールドのリストを示す、さまざまな非タイミング情報が含まれています。リストが空である場合、フェッチは保存されたフィールドの読み込みを完全にスキップします。

  1. 私たちは、フェッチに必要なすべての保存されたフィールドを前もって読み込むように努めています。これにより、`````_source`````フェーズはヒットごとに数マイクロ秒になります。この場合、`````_source`````フェーズの真のコストは、内訳の`````load_stored_fields`````コンポーネントに隠れています。`````"_source": false, "stored_fields": ["_none_"]`````を設定することで、保存されたフィールドの読み込みを完全にスキップすることが可能です。
  2. ### Profiling DFS
  3. DFSフェーズは、クエリフェーズの前に実行され、クエリに関連するグローバル情報を収集します。現在、これは2つのケースで使用されています:
  4. - 1*.* `````search_type`````が[`````dfs_query_then_fetch`````](237e89fe5c1c5ae9.md#profiling-dfs-statistics)に設定され、インデックスに複数のシャードがある場合。
  5. - 2*.* 検索リクエストに[knnセクション](237e89fe5c1c5ae9.md#profiling-knn-search)が含まれている場合。
  6. これらのケースの両方は、検索リクエストの一部として`````profile``````````true`````に設定することでプロファイルできます。
  7. #### Profiling DFS Statistics
  8. `````search_type``````````dfs_query_then_fetch`````に設定され、インデックスに複数のシャードがある場合、dfsフェーズは検索結果の関連性を向上させるために用語統計を収集します。
  9. 次の例は、`````profile``````````true`````に設定した検索の例です:
  10. まず、複数のシャードを持つインデックスを設定し、`````keyword`````フィールドに異なる値を持つ2つのドキュメントをインデックスします。
  11. #### Python
  12. ``````python
  13. resp = client.indices.create(
  14. index="my-dfs-index",
  15. settings={
  16. "number_of_shards": 2,
  17. "number_of_replicas": 1
  18. },
  19. mappings={
  20. "properties": {
  21. "my-keyword": {
  22. "type": "keyword"
  23. }
  24. }
  25. },
  26. )
  27. print(resp)
  28. resp1 = client.bulk(
  29. index="my-dfs-index",
  30. refresh=True,
  31. operations=[
  32. {
  33. "index": {
  34. "_id": "1"
  35. }
  36. },
  37. {
  38. "my-keyword": "a"
  39. },
  40. {
  41. "index": {
  42. "_id": "2"
  43. }
  44. },
  45. {
  46. "my-keyword": "b"
  47. }
  48. ],
  49. )
  50. print(resp1)
  51. `

Ruby

  1. response = client.indices.create(
  2. index: 'my-dfs-index',
  3. body: {
  4. settings: {
  5. number_of_shards: 2,
  6. number_of_replicas: 1
  7. },
  8. mappings: {
  9. properties: {
  10. "my-keyword": {
  11. type: 'keyword'
  12. }
  13. }
  14. }
  15. }
  16. )
  17. puts response
  18. response = client.bulk(
  19. index: 'my-dfs-index',
  20. refresh: true,
  21. body: [
  22. {
  23. index: {
  24. _id: '1'
  25. }
  26. },
  27. {
  28. "my-keyword": 'a'
  29. },
  30. {
  31. index: {
  32. _id: '2'
  33. }
  34. },
  35. {
  36. "my-keyword": 'b'
  37. }
  38. ]
  39. )
  40. puts response

Js

  1. const response = await client.indices.create({
  2. index: "my-dfs-index",
  3. settings: {
  4. number_of_shards: 2,
  5. number_of_replicas: 1,
  6. },
  7. mappings: {
  8. properties: {
  9. "my-keyword": {
  10. type: "keyword",
  11. },
  12. },
  13. },
  14. });
  15. console.log(response);
  16. const response1 = await client.bulk({
  17. index: "my-dfs-index",
  18. refresh: "true",
  19. operations: [
  20. {
  21. index: {
  22. _id: "1",
  23. },
  24. },
  25. {
  26. "my-keyword": "a",
  27. },
  28. {
  29. index: {
  30. _id: "2",
  31. },
  32. },
  33. {
  34. "my-keyword": "b",
  35. },
  36. ],
  37. });
  38. console.log(response1);

Console

  1. PUT my-dfs-index
  2. {
  3. "settings": {
  4. "number_of_shards": 2,
  5. "number_of_replicas": 1
  6. },
  7. "mappings": {
  8. "properties": {
  9. "my-keyword": { "type": "keyword" }
  10. }
  11. }
  12. }
  13. POST my-dfs-index/_bulk?refresh=true
  14. { "index" : { "_id" : "1" } }
  15. { "my-keyword" : "a" }
  16. { "index" : { "_id" : "2" } }
  17. { "my-keyword" : "b" }
my-dfs-indexは複数のシャードで作成されます。

インデックスが設定されたので、検索クエリのdfsフェーズをプロファイルできます。この例では、用語クエリを使用します。

Python

  1. resp = client.search(
  2. index="my-dfs-index",
  3. search_type="dfs_query_then_fetch",
  4. pretty=True,
  5. size="0",
  6. profile=True,
  7. query={
  8. "term": {
  9. "my-keyword": {
  10. "value": "a"
  11. }
  12. }
  13. },
  14. )
  15. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-dfs-index',
  3. search_type: 'dfs_query_then_fetch',
  4. pretty: true,
  5. size: 0,
  6. body: {
  7. profile: true,
  8. query: {
  9. term: {
  10. "my-keyword": {
  11. value: 'a'
  12. }
  13. }
  14. }
  15. }
  16. )
  17. puts response

Js

  1. const response = await client.search({
  2. index: "my-dfs-index",
  3. search_type: "dfs_query_then_fetch",
  4. pretty: "true",
  5. size: 0,
  6. profile: true,
  7. query: {
  8. term: {
  9. "my-keyword": {
  10. value: "a",
  11. },
  12. },
  13. },
  14. });
  15. console.log(response);

Console

  1. GET /my-dfs-index/_search?search_type=dfs_query_then_fetch&pretty&size=0
  2. {
  3. "profile": true,
  4. "query": {
  5. "term": {
  6. "my-keyword": {
  7. "value": "a"
  8. }
  9. }
  10. }
  11. }
search_type URLパラメータはdfs_query_then_fetchに設定され、
dfsフェーズが実行されることを保証します。
profileパラメータはtrueに設定されます。

レスポンスでは、各シャードのdfsセクションと、検索フェーズの残りのプロファイル出力が含まれたプロファイルが表示されます。シャードのdfsセクションの1つは次のようになります:

Console-Result

  1. "dfs" : {
  2. "statistics" : {
  3. "type" : "statistics",
  4. "description" : "collect term statistics",
  5. "time_in_nanos" : 236955,
  6. "breakdown" : {
  7. "term_statistics" : 4815,
  8. "collection_statistics" : 27081,
  9. "collection_statistics_count" : 1,
  10. "create_weight" : 153278,
  11. "term_statistics_count" : 1,
  12. "rewrite_count" : 0,
  13. "create_weight_count" : 1,
  14. "rewrite" : 0
  15. }
  16. }
  17. }

このレスポンスのdfs.statistics部分には、用語統計を収集するのにかかった合計時間を示すtime_in_nanosが含まれており、個々の部分のさらなる内訳が表示されます。

k近傍(kNN)検索は、dfsフェーズ中に実行されます。

次の例は、profiletrueに設定した検索の例です:

まず、いくつかの密なベクトルを持つインデックスを設定します。

Python

  1. resp = client.indices.create(
  2. index="my-knn-index",
  3. mappings={
  4. "properties": {
  5. "my-vector": {
  6. "type": "dense_vector",
  7. "dims": 3,
  8. "index": True,
  9. "similarity": "l2_norm"
  10. }
  11. }
  12. },
  13. )
  14. print(resp)
  15. resp1 = client.bulk(
  16. index="my-knn-index",
  17. refresh=True,
  18. operations=[
  19. {
  20. "index": {
  21. "_id": "1"
  22. }
  23. },
  24. {
  25. "my-vector": [
  26. 1,
  27. 5,
  28. -20
  29. ]
  30. },
  31. {
  32. "index": {
  33. "_id": "2"
  34. }
  35. },
  36. {
  37. "my-vector": [
  38. 42,
  39. 8,
  40. -15
  41. ]
  42. },
  43. {
  44. "index": {
  45. "_id": "3"
  46. }
  47. },
  48. {
  49. "my-vector": [
  50. 15,
  51. 11,
  52. 23
  53. ]
  54. }
  55. ],
  56. )
  57. print(resp1)

Ruby

  1. response = client.indices.create(
  2. index: 'my-knn-index',
  3. body: {
  4. mappings: {
  5. properties: {
  6. "my-vector": {
  7. type: 'dense_vector',
  8. dims: 3,
  9. index: true,
  10. similarity: 'l2_norm'
  11. }
  12. }
  13. }
  14. }
  15. )
  16. puts response
  17. response = client.bulk(
  18. index: 'my-knn-index',
  19. refresh: true,
  20. body: [
  21. {
  22. index: {
  23. _id: '1'
  24. }
  25. },
  26. {
  27. "my-vector": [
  28. 1,
  29. 5,
  30. -20
  31. ]
  32. },
  33. {
  34. index: {
  35. _id: '2'
  36. }
  37. },
  38. {
  39. "my-vector": [
  40. 42,
  41. 8,
  42. -15
  43. ]
  44. },
  45. {
  46. index: {
  47. _id: '3'
  48. }
  49. },
  50. {
  51. "my-vector": [
  52. 15,
  53. 11,
  54. 23
  55. ]
  56. }
  57. ]
  58. )
  59. puts response

Js

  1. const response = await client.indices.create({
  2. index: "my-knn-index",
  3. mappings: {
  4. properties: {
  5. "my-vector": {
  6. type: "dense_vector",
  7. dims: 3,
  8. index: true,
  9. similarity: "l2_norm",
  10. },
  11. },
  12. },
  13. });
  14. console.log(response);
  15. const response1 = await client.bulk({
  16. index: "my-knn-index",
  17. refresh: "true",
  18. operations: [
  19. {
  20. index: {
  21. _id: "1",
  22. },
  23. },
  24. {
  25. "my-vector": [1, 5, -20],
  26. },
  27. {
  28. index: {
  29. _id: "2",
  30. },
  31. },
  32. {
  33. "my-vector": [42, 8, -15],
  34. },
  35. {
  36. index: {
  37. _id: "3",
  38. },
  39. },
  40. {
  41. "my-vector": [15, 11, 23],
  42. },
  43. ],
  44. });
  45. console.log(response1);

Console

  1. PUT my-knn-index
  2. {
  3. "mappings": {
  4. "properties": {
  5. "my-vector": {
  6. "type": "dense_vector",
  7. "dims": 3,
  8. "index": true,
  9. "similarity": "l2_norm"
  10. }
  11. }
  12. }
  13. }
  14. POST my-knn-index/_bulk?refresh=true
  15. { "index": { "_id": "1" } }
  16. { "my-vector": [1, 5, -20] }
  17. { "index": { "_id": "2" } }
  18. { "my-vector": [42, 8, -15] }
  19. { "index": { "_id": "3" } }
  20. { "my-vector": [15, 11, 23] }

インデックスが設定されたので、kNN検索クエリをプロファイルできます。

Python

  1. resp = client.search(
  2. index="my-knn-index",
  3. profile=True,
  4. knn={
  5. "field": "my-vector",
  6. "query_vector": [
  7. -5,
  8. 9,
  9. -12
  10. ],
  11. "k": 3,
  12. "num_candidates": 100
  13. },
  14. )
  15. print(resp)

Ruby

  1. response = client.search(
  2. index: 'my-knn-index',
  3. body: {
  4. profile: true,
  5. knn: {
  6. field: 'my-vector',
  7. query_vector: [
  8. -5,
  9. 9,
  10. -12
  11. ],
  12. k: 3,
  13. num_candidates: 100
  14. }
  15. }
  16. )
  17. puts response

Js

  1. const response = await client.search({
  2. index: "my-knn-index",
  3. profile: true,
  4. knn: {
  5. field: "my-vector",
  6. query_vector: [-5, 9, -12],
  7. k: 3,
  8. num_candidates: 100,
  9. },
  10. });
  11. console.log(response);

Console

  1. POST my-knn-index/_search
  2. {
  3. "profile": true,
  4. "knn": {
  5. "field": "my-vector",
  6. "query_vector": [-5, 9, -12],
  7. "k": 3,
  8. "num_candidates": 100
  9. }
  10. }
profileパラメータはtrueに設定されます。

レスポンスでは、各シャードのknnセクションがdfsセクションの一部として表示され、検索フェーズの残りのプロファイル出力が含まれます。

シャードのdfs.knnセクションの1つは次のようになります:

Js

  1. "dfs" : {
  2. "knn" : [
  3. {
  4. "vector_operations_count" : 4,
  5. "query" : [
  6. {
  7. "type" : "DocAndScoreQuery",
  8. "description" : "DocAndScore[100]",
  9. "time_in_nanos" : 444414,
  10. "breakdown" : {
  11. "set_min_competitive_score_count" : 0,
  12. "match_count" : 0,
  13. "shallow_advance_count" : 0,
  14. "set_min_competitive_score" : 0,
  15. "next_doc" : 1688,
  16. "match" : 0,
  17. "next_doc_count" : 3,
  18. "score_count" : 3,
  19. "compute_max_score_count" : 0,
  20. "compute_max_score" : 0,
  21. "advance" : 4153,
  22. "advance_count" : 1,
  23. "score" : 2099,
  24. "build_scorer_count" : 2,
  25. "create_weight" : 128879,
  26. "shallow_advance" : 0,
  27. "create_weight_count" : 1,
  28. "build_scorer" : 307595,
  29. "count_weight": 0,
  30. "count_weight_count": 0
  31. }
  32. }
  33. ],
  34. "rewrite_time" : 1275732,
  35. "collector" : [
  36. {
  37. "name" : "SimpleTopScoreDocCollector",
  38. "reason" : "search_top_hits",
  39. "time_in_nanos" : 17163
  40. }
  41. ]
  42. } ]
  43. }

レスポンスのdfs.knn部分では、クエリリライト、およびコレクターのタイミング出力が表示されます。他の多くのクエリとは異なり、kNN検索はクエリリライト中に大部分の作業を行います。これは、rewrite_timeがkNN検索に費やされた時間を表すことを意味します。属性vector_operations_countは、kNN検索中に実行されたベクトル操作の全体的なカウントを表します。

プロファイリングの考慮事項

任意のプロファイラと同様に、Profile APIは検索実行に対して無視できないオーバーヘッドを導入します。collectadvance、およびnext_docのような低レベルのメソッド呼び出しを計測する行為は、これらのメソッドがタイトなループで呼び出されるため、かなり高価になる可能性があります。したがって、プロファイリングはデフォルトで本番環境では有効にすべきではなく、非プロファイルのクエリ時間と比較すべきではありません。プロファイリングは単なる診断ツールです。

また、プロファイリングに適さないために特別なLuceneの最適化が無効になる場合もあります。これにより、一部のクエリが非プロファイルの対応物よりも相対的に長い時間を報告する可能性がありますが、一般的にはプロファイルされたクエリの他のコンポーネントと比較して劇的な影響はないはずです。

制限事項

  • 現在、プロファイリングはネットワークオーバーヘッドを測定していません。
  • プロファイリングは、キューでの待機時間、コーディネートノードでのシャード応答のマージ、または検索を高速化するために使用される内部データ構造であるグローバルオーディナルの構築などの追加作業にかかる時間を考慮していません。
  • 現在、提案に対するプロファイリング統計は利用できません。
  • 集約のリデュースフェーズのプロファイリングは現在利用できません。
  • プロファイラは、バージョンごとに変更される可能性のある内部を計測しています。その結果得られるjsonは主に不安定であると見なすべきであり、特にdebugセクションの内容については注意が必要です。