Carpe Diem

備忘録

Elasticsearchでスコアに重み付けを行う

概要

SEO対策という言葉があるように、Googleの検索結果は単なる文字列の一致率ではなくホームページ自体にスコアが付けられており、それを元に検索の順位が決まります。
今回はElasticsearchでそのようなスコア順に並べる方法を説明します。

環境

  • Elasticsearch 5.0.0

データの用意

同じ名前のmy siteというサイトを用意します。そしてそれぞれのスコアをsで設定しています。

curl -s -XPOST localhost:9200/my_index/my_type/_bulk -d '
{"index": {"_id": "1"}}
{"title": "my site", "body": "Bad site", "s": 1}
{"index": {"_id": "2"}}
{"title": "my site", "body": "Normal site", "s": 10}
{"index": {"_id": "3"}}
{"title": "my site", "body": "Good site", "s": 100}
'

通常の文字列一致検索

通常のクエリだと

$ curl -s -XGET localhost:9200/my_index/my_type/_search?pretty -d '
{
    "query": { "match": { "title": "my site" } }
}'

結果

{
  "took" : 6,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 0.51623213,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "2",
        "_score" : 0.51623213,
        "_source" : {
          "title" : "my site",
          "body" : "Normal site",
          "s" : 10
        }
      },
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "1",
        "_score" : 0.51623213,
        "_source" : {
          "title" : "my site",
          "body" : "Bad site",
          "s" : 1
        }
      },
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "3",
        "_score" : 0.51623213,
        "_source" : {
          "title" : "my site",
          "body" : "Good site",
          "s" : 100
        }
      }
    ]
  }
}

となります。

サイト スコア
Good site 0.51623213
Normal site 0.51623213
Bad site 0.51623213

どれもスコアが同じ0.51623213なので順番は保証されません。

重み付けした検索

Function Score Queryを使います。パラメータを説明すると

パラメータ 役割
field_value_factor 特定のフィールドによってスコアを上げる
field スコアに反映させたいフィールド
modifier スコアの計算方法。デフォルトはnoneらしいですが試してみると掛け算されました
missing フィールドが無いときのデフォルト値

ではクエリを実行します。

$ curl -s -XGET localhost:9200/my_index/my_type/_search?pretty -d '
{
    "query": {
        "function_score": {
            "query": { "match": { "title": "my site" } },
            "field_value_factor": {
              "field": "s",
              "modifier": "log1p",
              "missing": 1
            }
        }
    }
}'

結果

{
  "took" : 5,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "failed" : 0
  },
  "hits" : {
    "total" : 3,
    "max_score" : 1.0346951,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "3",
        "_score" : 1.0346951,
        "_source" : {
          "title" : "my site",
          "body" : "Good site",
          "s" : 100
        }
      },
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "2",
        "_score" : 0.53760034,
        "_source" : {
          "title" : "my site",
          "body" : "Normal site",
          "s" : 10
        }
      },
      {
        "_index" : "my_index",
        "_type" : "my_type",
        "_id" : "1",
        "_score" : 0.15540136,
        "_source" : {
          "title" : "my site",
          "body" : "Bad site",
          "s" : 1
        }
      }
    ]
  }
}

※今回はスコア計算にlog1pを使いましたが、この辺は実際に試してみてから調整する必要があります。

サイト スコア
Good site 1.0346951
Normal site 0.53760034
Bad site 0.15540136

ちゃんとスコアを元に順位をつけることができました。

ソース