LoginSignup
3
5

More than 5 years have passed since last update.

Elasticsearchで簡単なパーソナライズ

Last updated at Posted at 2015-07-05

概要

elasticsearchのterms filterとfunction_scoreを使って、パーソナライズのようなことをしてみます。

blogを検索をすることを考えます。
blogには下記のような属性があるとします。

属性 説明
source 作者
date 投稿日付
text 本文
tag ブログのカテゴリのタグ

blogを検索するユーザには下記のような属性があるとします。

属性 説明
follows お気に入りユーザ的なやつ
tags お気に入りのカテゴリとか

たとえば、blogを全文検索したときのソート結果について、フォローしているユーザの日記や、
好きなカテゴリの日記を上位に表示したりということをやります。

準備

blogのインデックスを作ります。

curl -XPUT 'http://localhost:9200/blog' -d '
{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
    },
    "mappings": {
        "item": {
            "_all": {
                "enabled": false
            },
            "_source": {
                "enabled": true
            },
            "_type": {
                "index": "no"
            },
            "_id": {
                "index": "not_analyzed",
                "store": "yes"
            },
            "_routing": {},
            "properties": {
                "text": {
                    "type": "string",
                    "store": "yes"
                },
                "date": {
                    "type": "date",
                    "format": "yyyy-MM-dd HH:mm:ss"
                },
                "tag": {
                    "type": "string"
                },
                "source": {
                    "type": "integer"
                }
            }
        }
    }
}'

userのインデックスを作ります。

curl -XPUT localhost:9200/users -d '
{
    "settings": {
        "number_of_shards": 1,
        "number_of_replicas": 0
    },
    "mappings": {
        "source": {
            "_all": {
                "enabled": false
            },
            "_source": {
                "enabled": true
            },
            "_type": {
                "index": "no"
            },
            "_id": {
                "index": "not_analyzed",
                "store": "yes"
            },
            "_routing": {},
            "properties": {
                "follows": {
                    "type": "integer"
                },
                "tags": {
                    "type": "string"
                }
            }
        }
    }
}'

blogのドキュメントを作ります。

curl -XPUT "http://localhost:9200/blog/item/1" -d '{
"text": "aaa bbb ccc",
"date": "2015-06-22 15:00:00",
"tag": "game",
"source": "111"
}'

curl -XPUT "http://localhost:9200/blog/item/2" -d '{
"text": "bbb ccc ddd eee",
"date": "2015-06-22 15:00:00",
"tag": "game",
"source": "222"
}'

curl -XPUT "http://localhost:9200/blog/item/3" -d '{
"text": "ccc ddd eee",
"date": "2015-06-22 15:00:00",
"tag": "music",
"source": "333"
}'

curl -XPUT "http://localhost:9200/blog/item/4" -d '{
"text": "aaa bbb ccc",
"date": "2015-06-22 15:00:00",
"tag": "game",
"source": "444"
}'

userを作ります。
ユーザ333と444を気に入っており、音楽カテゴリのブログを好むユーザです。

curl -XPUT "http://localhost:9200/users/user/1" -d '{
"follows": [333,444],
"tags": "music"
}'

検索

普通の検索

curl -XGET "http://localhost:9200/blog/_search" -d '{
  "query" : {
    "simple_query_string" : {
      "query": "ccc",
      "fields": ["text"]
     }
  }
}'

結果

{
    "took": 2,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "hits": {
        "total": 4,
        "max_score": 0.3884282,
        "hits": [
            {
                "_index": "blog",
                "_type": "item",
                "_id": "1",
                "_score": 0.3884282,
                "_source": {
                    "text": "aaa bbb ccc",
                    "date": "2015-06-22 15:00:00",
                    "tag": "game",
                    "source": "111"
                }
            },
            {
                "_index": "blog",
                "_type": "item",
                "_id": "2",
                "_score": 0.3884282,
                "_source": {
                    "text": "bbb ccc ddd eee",
                    "date": "2015-06-22 15:00:00",
                    "tag": "game",
                    "source": "222"
                }
            },
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

ユーザ1がフォローしているユーザと気に入っているタグを重視してソート

ユーザ1がフォローしているユーザ(333と444)と、
気に入っているタグ(music)を指定して検索します。
function_scoreで、filterがマッチしたドキュメントのスコアを2倍しています。

curl -XGET "http://localhost:9200/blog/_search" -d '{
    "query": {
        "function_score": {
            "query": {
                "simple_query_string": {
                    "query": "ccc",
                    "fields": [
                        "text"
                    ]
                }
            },
            "functions": [
                {
                    "filter": {
                        "terms": {
                            "source": [
                                333,
                                444
                            ]
                        }
                    },
                    "weight": 2
                },
                {
                    "filter": {
                        "terms": {
                            "tag": [
                                "music"
                            ]
                        }
                    },
                    "weight": 2
                }
            ],
            "score_mode": "multiply"
        }
    }
}'

結果

{
    "took": 3,
    "timed_out": false,
    "_shards": {
        "total": 1,
        "successful": 1,
        "failed": 0
    },
    "hits": {
        "total": 4,
        "max_score": 1.5537128,
        "hits": [
            {
                "_index": "blog",
                "_type": "item",
                "_id": "3",
                "_score": 1.5537128,
                "_source": {
                    "text": "ccc ddd eee",
                    "date": "2015-06-22 15:00:00",
                    "tag": "music",
                    "source": "333"
                }
            },
            {
                "_index": "blog",
                "_type": "item",
                "_id": "4",
                "_score": 0.7768564,
                "_source": {
                    "text": "aaa bbb ccc",
                    "date": "2015-06-22 15:00:00",
                    "tag": "game",
                    "source": "444"
                }
            },
            {
                "_index": "blog",
                "_type": "item",
                "_id": "1",
                "_score": 0.3884282,
                "_source": {
                    "text": "aaa bbb ccc",
                    "date": "2015-06-22 15:00:00",
                    "tag": "game",
                    "source": "111"
                }
            },
            {
                "_index": "blog",
                "_type": "item",
                "_id": "2",
                "_score": 0.3884282,
                "_source": {
                    "text": "bbb ccc ddd eee",
                    "date": "2015-06-22 15:00:00",
                    "tag": "game",
                    "source": "222"
                }
            }
        ]
    }
}

lookup

なお、上記のクエリは、terms filterのlookupという機能を使用して下記のように書くことが可能です。
usersインデックスに作ったユーザ1のfollows,tags属性をフィルタにセットして検索しています。
https://www.elastic.co/guide/en/elasticsearch/reference/1.4/query-dsl-terms-filter.html#_terms_lookup_twitter_example
これを使ったほうが効率が良さそうです。
(たくさんフォローしてたりしてもクエリが長くならないし、lookupキャッシュとかも作ってくれるらしい。)

curl -XGET "http://localhost:9200/blog/_search" -d '{
"query": {
  "function_score": {
    "query": { 
       "simple_query_string" : {
         "query": "ccc",
         "fields": ["text"]
       }
     },
     "functions": [ 
     {
       "filter": { 
         "terms": {
           "source" : {
             "index" : "users",
             "type" : "user",
             "id" : "1",
             "path" : "follows"
           }
         }
       }, 
       "weight": 2
     },
     {
       "filter": { 
         "terms": {
           "tag" : {
             "index" : "users",
             "type" : "user",
             "id" : "1",
             "path" : "tags"
           }
         }
       },
       "weight": 2
     }
     ],
     "score_mode": "multiply"
   }
 }
}'
3
5
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
5