検索結果のフィルタリング

検索結果をフィルタリングするには、2つの方法を使用できます:

  • filter 条項を使用したブールクエリを使用します。検索リクエストは、ブールフィルタを検索ヒットと集約の両方に適用します。
  • 検索APIのpost_filterパラメータを使用します。検索リクエストは、ポストフィルタを検索ヒットのみに適用し、集約には適用しません。ポストフィルタを使用して、より広範な結果セットに基づいて集約を計算し、その後さらに結果を絞り込むことができます。
    ポストフィルタの後にヒットを再スコアリングして、関連性を向上させ、結果を再順序付けることもできます。

ポストフィルタ

  1. たとえば、次の特性を持つシャツを販売しているとします:
  2. #### Python
  3. ``````python
  4. resp = client.indices.create(
  5. index="shirts",
  6. mappings={
  7. "properties": {
  8. "brand": {
  9. "type": "keyword"
  10. },
  11. "color": {
  12. "type": "keyword"
  13. },
  14. "model": {
  15. "type": "keyword"
  16. }
  17. }
  18. },
  19. )
  20. print(resp)
  21. resp1 = client.index(
  22. index="shirts",
  23. id="1",
  24. refresh=True,
  25. document={
  26. "brand": "gucci",
  27. "color": "red",
  28. "model": "slim"
  29. },
  30. )
  31. print(resp1)
  32. `

Ruby

  1. response = client.indices.create(
  2. index: 'shirts',
  3. body: {
  4. mappings: {
  5. properties: {
  6. brand: {
  7. type: 'keyword'
  8. },
  9. color: {
  10. type: 'keyword'
  11. },
  12. model: {
  13. type: 'keyword'
  14. }
  15. }
  16. }
  17. }
  18. )
  19. puts response
  20. response = client.index(
  21. index: 'shirts',
  22. id: 1,
  23. refresh: true,
  24. body: {
  25. brand: 'gucci',
  26. color: 'red',
  27. model: 'slim'
  28. }
  29. )
  30. puts response

Js

  1. const response = await client.indices.create({
  2. index: "shirts",
  3. mappings: {
  4. properties: {
  5. brand: {
  6. type: "keyword",
  7. },
  8. color: {
  9. type: "keyword",
  10. },
  11. model: {
  12. type: "keyword",
  13. },
  14. },
  15. },
  16. });
  17. console.log(response);
  18. const response1 = await client.index({
  19. index: "shirts",
  20. id: 1,
  21. refresh: "true",
  22. document: {
  23. brand: "gucci",
  24. color: "red",
  25. model: "slim",
  26. },
  27. });
  28. console.log(response1);

コンソール

  1. PUT /shirts
  2. {
  3. "mappings": {
  4. "properties": {
  5. "brand": { "type": "keyword"},
  6. "color": { "type": "keyword"},
  7. "model": { "type": "keyword"}
  8. }
  9. }
  10. }
  11. PUT /shirts/_doc/1?refresh
  12. {
  13. "brand": "gucci",
  14. "color": "red",
  15. "model": "slim"
  16. }

ユーザーが2つのフィルタを指定したと想像してください:

  1. #### Python
  2. ``````python
  3. resp = client.search(
  4. index="shirts",
  5. query={
  6. "bool": {
  7. "filter": [
  8. {
  9. "term": {
  10. "color": "red"
  11. }
  12. },
  13. {
  14. "term": {
  15. "brand": "gucci"
  16. }
  17. }
  18. ]
  19. }
  20. },
  21. )
  22. print(resp)
  23. `

Ruby

  1. response = client.search(
  2. index: 'shirts',
  3. body: {
  4. query: {
  5. bool: {
  6. filter: [
  7. {
  8. term: {
  9. color: 'red'
  10. }
  11. },
  12. {
  13. term: {
  14. brand: 'gucci'
  15. }
  16. }
  17. ]
  18. }
  19. }
  20. }
  21. )
  22. puts response

Js

  1. const response = await client.search({
  2. index: "shirts",
  3. query: {
  4. bool: {
  5. filter: [
  6. {
  7. term: {
  8. color: "red",
  9. },
  10. },
  11. {
  12. term: {
  13. brand: "gucci",
  14. },
  15. },
  16. ],
  17. },
  18. },
  19. });
  20. console.log(response);

コンソール

  1. GET /shirts/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "filter": [
  6. { "term": { "color": "red" }},
  7. { "term": { "brand": "gucci" }}
  8. ]
  9. }
  10. }
  11. }

ただし、ファセットナビゲーションを使用して、ユーザーがクリックできる他のオプションのリストを表示したいとも考えています。おそらく、modelフィールドがあり、ユーザーが赤いグッチのt-shirtsまたはdress-shirtsに検索結果を制限できるようにします。

これは、terms集約を使用して行うことができます:

Python

  1. resp = client.search(
  2. index="shirts",
  3. query={
  4. "bool": {
  5. "filter": [
  6. {
  7. "term": {
  8. "color": "red"
  9. }
  10. },
  11. {
  12. "term": {
  13. "brand": "gucci"
  14. }
  15. }
  16. ]
  17. }
  18. },
  19. aggs={
  20. "models": {
  21. "terms": {
  22. "field": "model"
  23. }
  24. }
  25. },
  26. )
  27. print(resp)

Ruby

  1. response = client.search(
  2. index: 'shirts',
  3. body: {
  4. query: {
  5. bool: {
  6. filter: [
  7. {
  8. term: {
  9. color: 'red'
  10. }
  11. },
  12. {
  13. term: {
  14. brand: 'gucci'
  15. }
  16. }
  17. ]
  18. }
  19. },
  20. aggregations: {
  21. models: {
  22. terms: {
  23. field: 'model'
  24. }
  25. }
  26. }
  27. }
  28. )
  29. puts response

Js

  1. const response = await client.search({
  2. index: "shirts",
  3. query: {
  4. bool: {
  5. filter: [
  6. {
  7. term: {
  8. color: "red",
  9. },
  10. },
  11. {
  12. term: {
  13. brand: "gucci",
  14. },
  15. },
  16. ],
  17. },
  18. },
  19. aggs: {
  20. models: {
  21. terms: {
  22. field: "model",
  23. },
  24. },
  25. },
  26. });
  27. console.log(response);

コンソール

  1. GET /shirts/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "filter": [
  6. { "term": { "color": "red" }},
  7. { "term": { "brand": "gucci" }}
  8. ]
  9. }
  10. },
  11. "aggs": {
  12. "models": {
  13. "terms": { "field": "model" }
  14. }
  15. }
  16. }
グッチの赤いシャツの最も人気のあるモデルを返します。

しかし、他の色のグッチのシャツがどれだけあるかをユーザーに伝えたいとも考えています。terms集約をcolorフィールドに追加するだけでは、クエリが赤いグッチのシャツのみを返すため、色redしか得られません。

代わりに、集約中にすべての色のシャツを含め、その後colorsフィルタを検索結果にのみ適用したいと考えています。これがpost_filterの目的です:

Python

  1. resp = client.search(
  2. index="shirts",
  3. query={
  4. "bool": {
  5. "filter": {
  6. "term": {
  7. "brand": "gucci"
  8. }
  9. }
  10. }
  11. },
  12. aggs={
  13. "colors": {
  14. "terms": {
  15. "field": "color"
  16. }
  17. },
  18. "color_red": {
  19. "filter": {
  20. "term": {
  21. "color": "red"
  22. }
  23. },
  24. "aggs": {
  25. "models": {
  26. "terms": {
  27. "field": "model"
  28. }
  29. }
  30. }
  31. }
  32. },
  33. post_filter={
  34. "term": {
  35. "color": "red"
  36. }
  37. },
  38. )
  39. print(resp)

Ruby

  1. response = client.search(
  2. index: 'shirts',
  3. body: {
  4. query: {
  5. bool: {
  6. filter: {
  7. term: {
  8. brand: 'gucci'
  9. }
  10. }
  11. }
  12. },
  13. aggregations: {
  14. colors: {
  15. terms: {
  16. field: 'color'
  17. }
  18. },
  19. color_red: {
  20. filter: {
  21. term: {
  22. color: 'red'
  23. }
  24. },
  25. aggregations: {
  26. models: {
  27. terms: {
  28. field: 'model'
  29. }
  30. }
  31. }
  32. }
  33. },
  34. post_filter: {
  35. term: {
  36. color: 'red'
  37. }
  38. }
  39. }
  40. )
  41. puts response

Js

  1. const response = await client.search({
  2. index: "shirts",
  3. query: {
  4. bool: {
  5. filter: {
  6. term: {
  7. brand: "gucci",
  8. },
  9. },
  10. },
  11. },
  12. aggs: {
  13. colors: {
  14. terms: {
  15. field: "color",
  16. },
  17. },
  18. color_red: {
  19. filter: {
  20. term: {
  21. color: "red",
  22. },
  23. },
  24. aggs: {
  25. models: {
  26. terms: {
  27. field: "model",
  28. },
  29. },
  30. },
  31. },
  32. },
  33. post_filter: {
  34. term: {
  35. color: "red",
  36. },
  37. },
  38. });
  39. console.log(response);

コンソール

  1. GET /shirts/_search
  2. {
  3. "query": {
  4. "bool": {
  5. "filter": {
  6. "term": { "brand": "gucci" }
  7. }
  8. }
  9. },
  10. "aggs": {
  11. "colors": {
  12. "terms": { "field": "color" }
  13. },
  14. "color_red": {
  15. "filter": {
  16. "term": { "color": "red" }
  17. },
  18. "aggs": {
  19. "models": {
  20. "terms": { "field": "model" }
  21. }
  22. }
  23. }
  24. },
  25. "post_filter": {
  26. "term": { "color": "red" }
  27. }
  28. }
メインクエリは、色に関係なくすべてのグッチのシャツを見つけます。
colors aggは、グッチのシャツの人気のある色を返します。
color_red aggは、modelsサブ集約をのグッチのシャツに制限します。
最後に、post_filterは、検索hitsから赤以外の色を削除します。

フィルタリングされた検索結果の再スコアリング

再スコアリングは、queryおよびpost_filterフェーズによって返された上位(例:100 - 500)ドキュメントの順序を変更することで精度を向上させるのに役立ちます。コストのかかるアルゴリズムをインデックス内のすべてのドキュメントに適用する代わりに、二次的(通常はコストのかかる)アルゴリズムを使用します。

  1. 現在、再スコアAPIには1つの実装しかありません:クエリ再スコアラーで、スコアを調整するためにクエリを使用します。将来的には、ペアワイズ再スコアラーなどの代替の再スコアラーが利用可能になるかもしれません。
  2. 明示的な[`````sort`````](/read/elasticsearch-8-15/edefedafdd88134e.md)(`````_score`````を降順で除く)が`````rescore`````クエリと共に提供されると、エラーが発生します。
  3. ユーザーにページネーションを公開する際は、各ページを通過する際に`````window_size`````を変更しないでください(異なる`````from`````値を渡すことによって)。そうしないと、上位ヒットが変更され、ユーザーがページを通過する際に結果が混乱して変わる可能性があります。
  4. ### クエリ再スコアラー
  5. クエリ再スコアラーは、[`````query`````](89159571f146b334.md#request-body-search-query)および[`````post_filter`````](3f786c09568adf05.md#post-filter)フェーズによって返された上位Kの結果に対してのみ2回目のクエリを実行します。各シャードで調べられるドキュメントの数は、デフォルトで10の`````window_size`````パラメータによって制御できます。
  6. デフォルトでは、元のクエリと再スコアクエリからのスコアは線形に組み合わされ、各ドキュメントの最終`````_score`````を生成します。元のクエリと再スコアクエリの相対的重要性は、それぞれ`````query_weight`````および`````rescore_query_weight`````で制御できます。どちらも`````1`````にデフォルト設定されています。
  7. たとえば:
  8. #### Python
  9. ``````python
  10. resp = client.search(
  11. query={
  12. "match": {
  13. "message": {
  14. "operator": "or",
  15. "query": "the quick brown"
  16. }
  17. }
  18. },
  19. rescore={
  20. "window_size": 50,
  21. "query": {
  22. "rescore_query": {
  23. "match_phrase": {
  24. "message": {
  25. "query": "the quick brown",
  26. "slop": 2
  27. }
  28. }
  29. },
  30. "query_weight": 0.7,
  31. "rescore_query_weight": 1.2
  32. }
  33. },
  34. )
  35. print(resp)
  36. `

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. message: {
  6. operator: 'or',
  7. query: 'the quick brown'
  8. }
  9. }
  10. },
  11. rescore: {
  12. window_size: 50,
  13. query: {
  14. rescore_query: {
  15. match_phrase: {
  16. message: {
  17. query: 'the quick brown',
  18. slop: 2
  19. }
  20. }
  21. },
  22. query_weight: 0.7,
  23. rescore_query_weight: 1.2
  24. }
  25. }
  26. }
  27. )
  28. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. message: {
  5. operator: "or",
  6. query: "the quick brown",
  7. },
  8. },
  9. },
  10. rescore: {
  11. window_size: 50,
  12. query: {
  13. rescore_query: {
  14. match_phrase: {
  15. message: {
  16. query: "the quick brown",
  17. slop: 2,
  18. },
  19. },
  20. },
  21. query_weight: 0.7,
  22. rescore_query_weight: 1.2,
  23. },
  24. },
  25. });
  26. console.log(response);

コンソール

  1. POST /_search
  2. {
  3. "query" : {
  4. "match" : {
  5. "message" : {
  6. "operator" : "or",
  7. "query" : "the quick brown"
  8. }
  9. }
  10. },
  11. "rescore" : {
  12. "window_size" : 50,
  13. "query" : {
  14. "rescore_query" : {
  15. "match_phrase" : {
  16. "message" : {
  17. "query" : "the quick brown",
  18. "slop" : 2
  19. }
  20. }
  21. },
  22. "query_weight" : 0.7,
  23. "rescore_query_weight" : 1.2
  24. }
  25. }
  26. }

スコアの組み合わせ方は、score_modeで制御できます:

スコアモード 説明
total 元のスコアと再スコアクエリのスコアを加算します。デフォルト。
multiply 元のスコアに再スコアクエリのスコアを掛けます。function query再スコアに便利です。
avg 元のスコアと再スコアクエリのスコアの平均を取ります。
max 元のスコアと再スコアクエリのスコアの最大値を取ります。
min 元のスコアと再スコアクエリのスコアの最小値を取ります。

複数の再スコア

複数の再スコアを順番に実行することも可能です:

Python

  1. resp = client.search(
  2. query={
  3. "match": {
  4. "message": {
  5. "operator": "or",
  6. "query": "the quick brown"
  7. }
  8. }
  9. },
  10. rescore=[
  11. {
  12. "window_size": 100,
  13. "query": {
  14. "rescore_query": {
  15. "match_phrase": {
  16. "message": {
  17. "query": "the quick brown",
  18. "slop": 2
  19. }
  20. }
  21. },
  22. "query_weight": 0.7,
  23. "rescore_query_weight": 1.2
  24. }
  25. },
  26. {
  27. "window_size": 10,
  28. "query": {
  29. "score_mode": "multiply",
  30. "rescore_query": {
  31. "function_score": {
  32. "script_score": {
  33. "script": {
  34. "source": "Math.log10(doc.count.value + 2)"
  35. }
  36. }
  37. }
  38. }
  39. }
  40. }
  41. ],
  42. )
  43. print(resp)

Ruby

  1. response = client.search(
  2. body: {
  3. query: {
  4. match: {
  5. message: {
  6. operator: 'or',
  7. query: 'the quick brown'
  8. }
  9. }
  10. },
  11. rescore: [
  12. {
  13. window_size: 100,
  14. query: {
  15. rescore_query: {
  16. match_phrase: {
  17. message: {
  18. query: 'the quick brown',
  19. slop: 2
  20. }
  21. }
  22. },
  23. query_weight: 0.7,
  24. rescore_query_weight: 1.2
  25. }
  26. },
  27. {
  28. window_size: 10,
  29. query: {
  30. score_mode: 'multiply',
  31. rescore_query: {
  32. function_score: {
  33. script_score: {
  34. script: {
  35. source: 'Math.log10(doc.count.value + 2)'
  36. }
  37. }
  38. }
  39. }
  40. }
  41. }
  42. ]
  43. }
  44. )
  45. puts response

Js

  1. const response = await client.search({
  2. query: {
  3. match: {
  4. message: {
  5. operator: "or",
  6. query: "the quick brown",
  7. },
  8. },
  9. },
  10. rescore: [
  11. {
  12. window_size: 100,
  13. query: {
  14. rescore_query: {
  15. match_phrase: {
  16. message: {
  17. query: "the quick brown",
  18. slop: 2,
  19. },
  20. },
  21. },
  22. query_weight: 0.7,
  23. rescore_query_weight: 1.2,
  24. },
  25. },
  26. {
  27. window_size: 10,
  28. query: {
  29. score_mode: "multiply",
  30. rescore_query: {
  31. function_score: {
  32. script_score: {
  33. script: {
  34. source: "Math.log10(doc.count.value + 2)",
  35. },
  36. },
  37. },
  38. },
  39. },
  40. },
  41. ],
  42. });
  43. console.log(response);

コンソール

  1. POST /_search
  2. {
  3. "query" : {
  4. "match" : {
  5. "message" : {
  6. "operator" : "or",
  7. "query" : "the quick brown"
  8. }
  9. }
  10. },
  11. "rescore" : [ {
  12. "window_size" : 100,
  13. "query" : {
  14. "rescore_query" : {
  15. "match_phrase" : {
  16. "message" : {
  17. "query" : "the quick brown",
  18. "slop" : 2
  19. }
  20. }
  21. },
  22. "query_weight" : 0.7,
  23. "rescore_query_weight" : 1.2
  24. }
  25. }, {
  26. "window_size" : 10,
  27. "query" : {
  28. "score_mode": "multiply",
  29. "rescore_query" : {
  30. "function_score" : {
  31. "script_score": {
  32. "script": {
  33. "source": "Math.log10(doc.count.value + 2)"
  34. }
  35. }
  36. }
  37. }
  38. }
  39. } ]
  40. }

最初の再スコアはクエリの結果を取得し、次の再スコアは最初の結果を取得します。2回目の再スコアは、最初の再スコアによって行われたソートを「見る」ため、最初の再スコアで大きなウィンドウを使用して、2回目の再スコアのためにドキュメントを小さなウィンドウに引き込むことが可能です。