検索結果のフィルタリング
検索結果をフィルタリングするには、2つの方法を使用できます:
filter
条項を使用したブールクエリを使用します。検索リクエストは、ブールフィルタを検索ヒットと集約の両方に適用します。- 検索APIの
post_filter
パラメータを使用します。検索リクエストは、ポストフィルタを検索ヒットのみに適用し、集約には適用しません。ポストフィルタを使用して、より広範な結果セットに基づいて集約を計算し、その後さらに結果を絞り込むことができます。
ポストフィルタの後にヒットを再スコアリングして、関連性を向上させ、結果を再順序付けることもできます。
ポストフィルタ
たとえば、次の特性を持つシャツを販売しているとします:
#### Python
``````python
resp = client.indices.create(
index="shirts",
mappings={
"properties": {
"brand": {
"type": "keyword"
},
"color": {
"type": "keyword"
},
"model": {
"type": "keyword"
}
}
},
)
print(resp)
resp1 = client.index(
index="shirts",
id="1",
refresh=True,
document={
"brand": "gucci",
"color": "red",
"model": "slim"
},
)
print(resp1)
`
Ruby
response = client.indices.create(
index: 'shirts',
body: {
mappings: {
properties: {
brand: {
type: 'keyword'
},
color: {
type: 'keyword'
},
model: {
type: 'keyword'
}
}
}
}
)
puts response
response = client.index(
index: 'shirts',
id: 1,
refresh: true,
body: {
brand: 'gucci',
color: 'red',
model: 'slim'
}
)
puts response
Js
const response = await client.indices.create({
index: "shirts",
mappings: {
properties: {
brand: {
type: "keyword",
},
color: {
type: "keyword",
},
model: {
type: "keyword",
},
},
},
});
console.log(response);
const response1 = await client.index({
index: "shirts",
id: 1,
refresh: "true",
document: {
brand: "gucci",
color: "red",
model: "slim",
},
});
console.log(response1);
コンソール
PUT /shirts
{
"mappings": {
"properties": {
"brand": { "type": "keyword"},
"color": { "type": "keyword"},
"model": { "type": "keyword"}
}
}
}
PUT /shirts/_doc/1?refresh
{
"brand": "gucci",
"color": "red",
"model": "slim"
}
ユーザーが2つのフィルタを指定したと想像してください:
#### Python
``````python
resp = client.search(
index="shirts",
query={
"bool": {
"filter": [
{
"term": {
"color": "red"
}
},
{
"term": {
"brand": "gucci"
}
}
]
}
},
)
print(resp)
`
Ruby
response = client.search(
index: 'shirts',
body: {
query: {
bool: {
filter: [
{
term: {
color: 'red'
}
},
{
term: {
brand: 'gucci'
}
}
]
}
}
}
)
puts response
Js
const response = await client.search({
index: "shirts",
query: {
bool: {
filter: [
{
term: {
color: "red",
},
},
{
term: {
brand: "gucci",
},
},
],
},
},
});
console.log(response);
コンソール
GET /shirts/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
}
}
ただし、ファセットナビゲーションを使用して、ユーザーがクリックできる他のオプションのリストを表示したいとも考えています。おそらく、model
フィールドがあり、ユーザーが赤いグッチのt-shirts
またはdress-shirts
に検索結果を制限できるようにします。
これは、terms
集約を使用して行うことができます:
Python
resp = client.search(
index="shirts",
query={
"bool": {
"filter": [
{
"term": {
"color": "red"
}
},
{
"term": {
"brand": "gucci"
}
}
]
}
},
aggs={
"models": {
"terms": {
"field": "model"
}
}
},
)
print(resp)
Ruby
response = client.search(
index: 'shirts',
body: {
query: {
bool: {
filter: [
{
term: {
color: 'red'
}
},
{
term: {
brand: 'gucci'
}
}
]
}
},
aggregations: {
models: {
terms: {
field: 'model'
}
}
}
}
)
puts response
Js
const response = await client.search({
index: "shirts",
query: {
bool: {
filter: [
{
term: {
color: "red",
},
},
{
term: {
brand: "gucci",
},
},
],
},
},
aggs: {
models: {
terms: {
field: "model",
},
},
},
});
console.log(response);
コンソール
GET /shirts/_search
{
"query": {
"bool": {
"filter": [
{ "term": { "color": "red" }},
{ "term": { "brand": "gucci" }}
]
}
},
"aggs": {
"models": {
"terms": { "field": "model" }
}
}
}
グッチの赤いシャツの最も人気のあるモデルを返します。 |
しかし、他の色のグッチのシャツがどれだけあるかをユーザーに伝えたいとも考えています。terms
集約をcolor
フィールドに追加するだけでは、クエリが赤いグッチのシャツのみを返すため、色red
しか得られません。
代わりに、集約中にすべての色のシャツを含め、その後colors
フィルタを検索結果にのみ適用したいと考えています。これがpost_filter
の目的です:
Python
resp = client.search(
index="shirts",
query={
"bool": {
"filter": {
"term": {
"brand": "gucci"
}
}
}
},
aggs={
"colors": {
"terms": {
"field": "color"
}
},
"color_red": {
"filter": {
"term": {
"color": "red"
}
},
"aggs": {
"models": {
"terms": {
"field": "model"
}
}
}
}
},
post_filter={
"term": {
"color": "red"
}
},
)
print(resp)
Ruby
response = client.search(
index: 'shirts',
body: {
query: {
bool: {
filter: {
term: {
brand: 'gucci'
}
}
}
},
aggregations: {
colors: {
terms: {
field: 'color'
}
},
color_red: {
filter: {
term: {
color: 'red'
}
},
aggregations: {
models: {
terms: {
field: 'model'
}
}
}
}
},
post_filter: {
term: {
color: 'red'
}
}
}
)
puts response
Js
const response = await client.search({
index: "shirts",
query: {
bool: {
filter: {
term: {
brand: "gucci",
},
},
},
},
aggs: {
colors: {
terms: {
field: "color",
},
},
color_red: {
filter: {
term: {
color: "red",
},
},
aggs: {
models: {
terms: {
field: "model",
},
},
},
},
},
post_filter: {
term: {
color: "red",
},
},
});
console.log(response);
コンソール
GET /shirts/_search
{
"query": {
"bool": {
"filter": {
"term": { "brand": "gucci" }
}
}
},
"aggs": {
"colors": {
"terms": { "field": "color" }
},
"color_red": {
"filter": {
"term": { "color": "red" }
},
"aggs": {
"models": {
"terms": { "field": "model" }
}
}
}
},
"post_filter": {
"term": { "color": "red" }
}
}
メインクエリは、色に関係なくすべてのグッチのシャツを見つけます。 | |
colors aggは、グッチのシャツの人気のある色を返します。 |
|
color_red aggは、models サブ集約を赤のグッチのシャツに制限します。 |
|
最後に、post_filter は、検索hits から赤以外の色を削除します。 |
フィルタリングされた検索結果の再スコアリング
再スコアリングは、query
およびpost_filter
フェーズによって返された上位(例:100 - 500)ドキュメントの順序を変更することで精度を向上させるのに役立ちます。コストのかかるアルゴリズムをインデックス内のすべてのドキュメントに適用する代わりに、二次的(通常はコストのかかる)アルゴリズムを使用します。
現在、再スコアAPIには1つの実装しかありません:クエリ再スコアラーで、スコアを調整するためにクエリを使用します。将来的には、ペアワイズ再スコアラーなどの代替の再スコアラーが利用可能になるかもしれません。
明示的な[`````sort`````](/read/elasticsearch-8-15/edefedafdd88134e.md)(`````_score`````を降順で除く)が`````rescore`````クエリと共に提供されると、エラーが発生します。
ユーザーにページネーションを公開する際は、各ページを通過する際に`````window_size`````を変更しないでください(異なる`````from`````値を渡すことによって)。そうしないと、上位ヒットが変更され、ユーザーがページを通過する際に結果が混乱して変わる可能性があります。
### クエリ再スコアラー
クエリ再スコアラーは、[`````query`````](89159571f146b334.md#request-body-search-query)および[`````post_filter`````](3f786c09568adf05.md#post-filter)フェーズによって返された上位Kの結果に対してのみ2回目のクエリを実行します。各シャードで調べられるドキュメントの数は、デフォルトで10の`````window_size`````パラメータによって制御できます。
デフォルトでは、元のクエリと再スコアクエリからのスコアは線形に組み合わされ、各ドキュメントの最終`````_score`````を生成します。元のクエリと再スコアクエリの相対的重要性は、それぞれ`````query_weight`````および`````rescore_query_weight`````で制御できます。どちらも`````1`````にデフォルト設定されています。
たとえば:
#### Python
``````python
resp = client.search(
query={
"match": {
"message": {
"operator": "or",
"query": "the quick brown"
}
}
},
rescore={
"window_size": 50,
"query": {
"rescore_query": {
"match_phrase": {
"message": {
"query": "the quick brown",
"slop": 2
}
}
},
"query_weight": 0.7,
"rescore_query_weight": 1.2
}
},
)
print(resp)
`
Ruby
response = client.search(
body: {
query: {
match: {
message: {
operator: 'or',
query: 'the quick brown'
}
}
},
rescore: {
window_size: 50,
query: {
rescore_query: {
match_phrase: {
message: {
query: 'the quick brown',
slop: 2
}
}
},
query_weight: 0.7,
rescore_query_weight: 1.2
}
}
}
)
puts response
Js
const response = await client.search({
query: {
match: {
message: {
operator: "or",
query: "the quick brown",
},
},
},
rescore: {
window_size: 50,
query: {
rescore_query: {
match_phrase: {
message: {
query: "the quick brown",
slop: 2,
},
},
},
query_weight: 0.7,
rescore_query_weight: 1.2,
},
},
});
console.log(response);
コンソール
POST /_search
{
"query" : {
"match" : {
"message" : {
"operator" : "or",
"query" : "the quick brown"
}
}
},
"rescore" : {
"window_size" : 50,
"query" : {
"rescore_query" : {
"match_phrase" : {
"message" : {
"query" : "the quick brown",
"slop" : 2
}
}
},
"query_weight" : 0.7,
"rescore_query_weight" : 1.2
}
}
}
スコアの組み合わせ方は、score_mode
で制御できます:
スコアモード | 説明 |
---|---|
total |
元のスコアと再スコアクエリのスコアを加算します。デフォルト。 |
multiply |
元のスコアに再スコアクエリのスコアを掛けます。function query 再スコアに便利です。 |
avg |
元のスコアと再スコアクエリのスコアの平均を取ります。 |
max |
元のスコアと再スコアクエリのスコアの最大値を取ります。 |
min |
元のスコアと再スコアクエリのスコアの最小値を取ります。 |
複数の再スコア
複数の再スコアを順番に実行することも可能です:
Python
resp = client.search(
query={
"match": {
"message": {
"operator": "or",
"query": "the quick brown"
}
}
},
rescore=[
{
"window_size": 100,
"query": {
"rescore_query": {
"match_phrase": {
"message": {
"query": "the quick brown",
"slop": 2
}
}
},
"query_weight": 0.7,
"rescore_query_weight": 1.2
}
},
{
"window_size": 10,
"query": {
"score_mode": "multiply",
"rescore_query": {
"function_score": {
"script_score": {
"script": {
"source": "Math.log10(doc.count.value + 2)"
}
}
}
}
}
}
],
)
print(resp)
Ruby
response = client.search(
body: {
query: {
match: {
message: {
operator: 'or',
query: 'the quick brown'
}
}
},
rescore: [
{
window_size: 100,
query: {
rescore_query: {
match_phrase: {
message: {
query: 'the quick brown',
slop: 2
}
}
},
query_weight: 0.7,
rescore_query_weight: 1.2
}
},
{
window_size: 10,
query: {
score_mode: 'multiply',
rescore_query: {
function_score: {
script_score: {
script: {
source: 'Math.log10(doc.count.value + 2)'
}
}
}
}
}
}
]
}
)
puts response
Js
const response = await client.search({
query: {
match: {
message: {
operator: "or",
query: "the quick brown",
},
},
},
rescore: [
{
window_size: 100,
query: {
rescore_query: {
match_phrase: {
message: {
query: "the quick brown",
slop: 2,
},
},
},
query_weight: 0.7,
rescore_query_weight: 1.2,
},
},
{
window_size: 10,
query: {
score_mode: "multiply",
rescore_query: {
function_score: {
script_score: {
script: {
source: "Math.log10(doc.count.value + 2)",
},
},
},
},
},
},
],
});
console.log(response);
コンソール
POST /_search
{
"query" : {
"match" : {
"message" : {
"operator" : "or",
"query" : "the quick brown"
}
}
},
"rescore" : [ {
"window_size" : 100,
"query" : {
"rescore_query" : {
"match_phrase" : {
"message" : {
"query" : "the quick brown",
"slop" : 2
}
}
},
"query_weight" : 0.7,
"rescore_query_weight" : 1.2
}
}, {
"window_size" : 10,
"query" : {
"score_mode": "multiply",
"rescore_query" : {
"function_score" : {
"script_score": {
"script": {
"source": "Math.log10(doc.count.value + 2)"
}
}
}
}
}
} ]
}
最初の再スコアはクエリの結果を取得し、次の再スコアは最初の結果を取得します。2回目の再スコアは、最初の再スコアによって行われたソートを「見る」ため、最初の再スコアで大きなウィンドウを使用して、2回目の再スコアのためにドキュメントを小さなウィンドウに引き込むことが可能です。