ときどき起きる

だいたい寝ている

Pinned Queryを使って検索結果を固定する【翻訳】

Pinned Query とその使い方について

Pinned Queryは Elasticsearch 7.4 でリリースされた機能の一つで、1つもしくは複数の任意に選択された文書を他の文書に優先して表示する機能です。この機能は文書の関連度に影響されないため、文書を優先手近位表示したい場合や、検索者を特定の文書に導きたい場合などに利用することができます。

具体的には以下のようなユースケースが考えられます

  • ナレッジ検索の開発者が、クエリ毎に最もクリックされている記事を優先的に表示する。
  • 新しくリリースしたプロダクトの宣伝のために検索結果の上位に固定する。また、季節に応じたマーケティングのためにこの機能を利用する。

Pinned Query の実装方法

仮のプロダクトデータを使って Pinned Query を試してみましょう

仮データの登録

PUT product/_bulk
{ "index" : { "_id": "prod101" } }
{ "brand" : "Apple", "model" : "iphone 11", "price": 800 }
{ "index" : { "_id": "prod102" } }
{ "brand" : "Samsung", "model" : "galaxy m32", "price": 700 }
{ "index" : { "_id": "prod103"} }
{ "brand" : "Apple", "model" : "iphone 13", "price": 1000 }
{ "index" : { "_id": "prod104"} }
{ "brand" : "Apple", "model" : "iphone 12", "price": 900 }
{ "index" : { "_id": "prod104"} }
{ "brand" : "Apple", "model" : "iphone 7", "price": 400 }

Pinned Query を使わない検索

POST product/_search
{
 "query": {
   "match": {
     "model": "iphone"
   }
 }
}

レスポンス:

"hits": [
     {
       "_index": "product",
       "_id": "prod101",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 11",
         "price": 800
       }
     },
     {
       "_index": "product",
       "_id": "prod103",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 13",
         "price": 1000
       }
     },
     {
       "_index": "product",
       "_id": "prod104",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 12",
         "price": 900
       }
     },
     {
       "_index": "product",
       "_id": "prod105",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 7",
         "price": 400
       }
     }
   ]

この時点では検索結果は関連度に応じてランキングされており、最新の機種が上位に来ていません。ここで Pinned Query を用いることでどのように最新機種を優先的に表示できるか見てみましょう。

Pinned Query を利用した検索

ここでは、最新のiPhoneを優先的に表示しようとしています。

POST product/_search
{
 "query": {
   "pinned": {
     "ids": [ "prod103", "prod104"],
     "organic": {
       "match": {
         "model": "iphone"
       }
     }
   }
 }
}
Pinned Query のレスポンス

Elasticsearch からのレスポンスの上位に iPhone 13 と iPhone 12 がそれぞれ表示されており、その他のプロダクトはそれに続く形で返却されています。

"hits": [
     {
       "_index": "product",
       "_id": "prod103",
       "_score": 1.7014124e+38,
       "_source": {
         "brand": "Apple",
         "model": "iphone 13",
         "price": 1000
       }
     },
     {
       "_index": "product",
       "_id": "prod104",
       "_score": 1.7014122e+38,
       "_source": {
         "brand": "Apple",
         "model": "iphone 12",
         "price": 900
       }
     },
     {
       "_index": "product",
       "_id": "prod101",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 11",
         "price": 800
       }
     },
     {
       "_index": "product",
       "_id": "prod105",
       "_score": 0.2578291,
       "_source": {
         "brand": "Apple",
         "model": "iphone 7",
         "price": 400
       }
     }
   ]

Pinned Query を複数インデックスに対して利用する方法

もし複数インデックスをまたいで検索詩たい場合、以下のようにインデックス名も合わせて指定してください。

GET /_search
{
 "query": {
   "pinned": {
     "docs": [
       {
         "_index": "product-01",
         "_id": "prod101"
       },
       {
         "_index": "product-01",
         "_id": "prod102"
       },
       {
         "_index": "product-02",
         "_id": "prod105"
       }
     ],
     "organic": {
       "match": {
         "model": "iphone"
       }
     }
   }
 }
}

Pinned Query では最大 100 id までしか指定できない点にご注意ください

Pinned Query で指定した文書が表示されないケース

Elasticsearch は何も指定しない場合関連度順に文書を返却します。一方日付順や在庫順など、任意の並び順を sort オプションで指定する場合も想定されますが、これらの並び順では関連度は無視されるため、Pinned Query で指定されたドキュメントは優先的に表示されません。

例として、お手頃な iPhone 7iPhone 11 を指定しつつ、値段が高い順で検索してみましょう。

POST product/_search
{
 "sort": [
   {
     "price": {
       "order": "desc"
     }
   }
 ],
 "query": {
   "pinned": {
     "ids": [ "prod105", "prod101"],
     "organic": {
       "match": {
         "model": "iphone"
       }
     }
   }
 }
}
レスポンス

想定としては Pinned Query で指定した iPhone 7iPhone 11 が優先的に表示されてほしいところですが、実際には値段順で結果が返却され Pinned Query は考慮されていないことがわかります。

"hits": [
     {
       "_index": "product",
       "_id": "prod103",
       "_score": null,
       "_source": {
         "brand": "Apple",
         "model": "iphone 13",
         "price": 1000
       },
       "sort": [
         1000
       ]
     },
     {
       "_index": "product",
       "_id": "prod104",
       "_score": null,
       "_source": {
         "brand": "Apple",
         "model": "iphone 12",
         "price": 900
       },
       "sort": [
         900
       ]
     },
     {
       "_index": "product",
       "_id": "prod101",
       "_score": null,
       "_source": {
         "brand": "Apple",
         "model": "iphone 11",
         "price": 800
       },
       "sort": [
         800
       ]
     },
     {
       "_index": "product",
       "_id": "prod105",
       "_score": null,
       "_source": {
         "brand": "Apple",
         "model": "iphone 7",
         "price": 400
       },
       "sort": [
         400
       ]
     }
   ]