elasticsearch カスタム スコアリング function_score の使用法を実際のケースを通して説明する

序文

elasticsearchこれは非常に強力な検索機能を提供しますが、相関スコアを使用するだけでは不十分な場合があるため、elasticsearchカスタム スコアリング関数が提供されますfunction_score。この記事では、簡単なケースを組み合わせてfunction_scoreその使用方法を説明します。function-score-query最も権威のあるドキュメント公式ドキュメント:
function_score 公式ドキュメント

基本的なデータの準備

次のフィールドを含むニュース テーブルを作成します。

分野 タイプ 説明する
ID 長さ ニュースID
タイトル タイトル
タグ ラベル
read_count 長さ 読み取り回数
like_count 長さ いいね!
コメント数 長さ コメント
ランク ダブル カスタムウェイト
位置 配列 記事掲載時の緯度と経度
パブタイム 日にち リリースタイム

作成elasticsearchMapping:

PUT /news
{
    
    
  "mappings": {
    
    
    "properties": {
    
    
      "id": {
    
    
        "type": "long"
      },
      "title": {
    
    
        "type": "text",
        "analyzer": "standard"
      },
      "tags": {
    
    
        "type": "keyword"
      },
      "read_count": {
    
    
        "type": "long"
      },
     "like_count": {
    
    
        "type": "long"
      },
     "comment_count": {
    
    
        "type": "long"
      },
      "rank": {
    
    
        "type": "double"
      },
      "location": {
    
    
          "type": "geo_point"
        },
      "pub_time": {
    
    
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd HH:mm||yyyy-MM-dd||epoch_millis"
      }
    }
  }
}

テストデータを準備します。

ID タイトル タグ read_count コメント数 like_count ランク 位置 パブタイム
1 台風「ドゥスールイ」が福建省晋江市に上陸し、多くの部門や場所が対応に全力を尽くした 台風、杜蘇瑞、福建省 10000 2000年 600 0 118.55199,24.78144 2023-07-29 09:47
2 台風「ドゥスールイ」の影響で、北京では7月29日から8月1日まで大雨が降る見込み 台風、ドゥ・スルイ、北京 1000 200 60 0 116.23128,40.22077 2023-06-29 14:49:38
3 杭州市、台風の青色警報信号を解除 台風、杭州 10 2 6 0.9 120.21201,30.2084 2020-07-29 14:49:38

データを一括追加しますelasticsearch

POST _bulk
{
    
    "create": {
    
    "_index": "news", "_id": 1}}
{
    
    "comment_count":600,"id":1,"like_count":2000,"location":[118.55199,24.78144],"pub_time":"2023-07-29 09:47","rank":0.0,"read_count":10000,"tags":["台风","杜苏芮","福建"],"title":"台风“杜苏芮”登陆福建晋江 多部门多地全力应对"}
{
    
    "create": {
    
    "_index": "news", "_id": 2}}
{
    
    "comment_count":60,"id":2,"like_count":200,"location":[116.23128,40.22077],"pub_time":"2023-06-29 14:49:38","rank":0.0,"read_count":1000,"tags":["台风","杜苏芮","北京"],"title":"受台风“杜苏芮”影响 北京7月29日至8月1日将有强降雨"}
{
    
    "create": {
    
    "_index": "news", "_id": 3}}
{
    
    "comment_count":6,"id":3,"like_count":20,"location":[120.21201,30.208],"pub_time":"2020-07-29 14:49:38","rank":0.99,"read_count":100,"tags":["台风","杭州"],"title":"杭州解除台风蓝色预警信号"}

random_scoreの使用法

、 、の機能をrandom_score理解し、デモを直接見てみましょうweightscore_modeboost_mode


GET /news/_search
{
    
    
  "query": {
    
    
    "function_score": {
    
    
      "query": {
    
    "match": {
    
    
        "title": "台风"
      }},
      "functions": [
        {
    
    
          "random_score": {
    
    }, 
          "weight": 1
        },
         {
    
    
          "filter": {
    
     "match": {
    
     "title": "杭州" } },
          "weight":42
        }
      ],
      "score_mode": "sum",
      "boost_mode": "replace"
    }
  }
}

対応するJAVAクエリコード:

        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.should(QueryBuilders.matchQuery("title","杭州"));
        FunctionScoreQueryBuilder.FilterFunctionBuilder[] filterFunctionBuilders = new FunctionScoreQueryBuilder.FilterFunctionBuilder[1];
        ScoreFunctionBuilder<RandomScoreFunctionBuilder> randomScoreFilter = new RandomScoreFunctionBuilder();
        ((RandomScoreFunctionBuilder) randomScoreFilter).seed(2);
        filterFunctionBuilders[0] = new FunctionScoreQueryBuilder.FilterFunctionBuilder(randomScoreFilter);
        FunctionScoreQueryBuilder query = QueryBuilders.functionScoreQuery(queryBuilder, filterFunctionBuilders).scoreMode(FunctionScoreQuery.ScoreMode.SUM).boostMode(CombineFunction.SUM);
        SearchSourceBuilder searchSourceBuilder= new SearchSourceBuilder().query(query);
        SearchRequest searchRequest= new SearchRequest().searchType(SearchType.DFS_QUERY_THEN_FETCH).indices("news").source(searchSourceBuilder);
        SearchResponse response =  restClient.search(searchRequest, RequestOptions.DEFAULT);
        SearchHits hits = response.getHits();
        String searchSource;
        for (SearchHit hit : hits)
        {
    
    
            searchSource = hit.getSourceAsString();
            System.out.println(searchSource);
        }

検索結果:
画像.png

このクエリは を使用しますfunction_score「Typhoon」を検索するqueryことで、 2 つのスコアを追加しました。1 つはランダムにスコアを生成するもので、スコアの重みは 1 です。2 つ目は、タイトルに「杭州」がある場合、スコアの重みは次のとおりです。 42、titlefunctionsrandom_scoreweight

  • random_score
    名前が示すとおり、(0,1) の間のランダムなスコアを生成することです。私が考える応用シナリオの 1 つは、製品が必要とするある日、ニュースの見方は人それぞれです。一日を与えてください。random_score毎回抽出されるデータはランダムであり、誰もが見るニュースは異なりますが、このランダムクエリは実装よりもはるかに簡単で、コストゼロで「Mysql人には千の顔がある」を実現しています。

  • weight
    これは、生成されたスコアに重みを追加するためです。上記のデモでは、私たちは 1 番目 weight=1と 2 番目weight=42です。検索結果のスコアから、「杭州は台風の青色の警告信号を解除します」のスコアは 42.40192 であることがわかります。次の場合、重量が 42 倍に増加するため、わずか 0.8194501 になります。

  • score_mode

score_modeこの関数はfunctions、途中で計算された複数のスコアを集計するものです。たとえば、 yes を使用すると、上で取得したスコアと真ん中で取得した 42 点を加算するsumことを意味します。つまり、最初のスコア 42.40192 が生成されます 0.40192さらに42得点を獲得した。デフォルトでは を使用することになっており、合計 6 つの計算方法があります。random_scorefilterrandom_scorefilterscore_modemultiply

random_score関数 計算
multiply スコアは乗算されます (デフォルト)
sum スコアは合計されます
avg スコアは平均化されます
first 一致するフィルターを持つ最初の関数が適用されます
max 最大スコアが使用されます
min 最小スコアが使用されます
  • boost_mode

boost_modeこの機能は、functions合計スコアとクエリによって取得されたスコアqueryを計算することです。たとえば、使用中のスコアをreplaceフル使用中のfunctionsスコアに置き換えて使用しますqueryboost_mode計算方法は合計 6 つあります。

boost_mode関数 採点方法
multiply クエリスコアと関数スコアが乗算されます (デフォルト)
replace 関数スコアのみが使用され、クエリスコアは無視されます。
sum クエリスコアと関数スコアが追加されます
avg 平均
max クエリスコアと関数スコアの最大値
min クエリスコアと関数スコアの最小値

script_scoreの使用法

script_scoreこれは、さまざまな関数を使用してドキュメントに表示されるフィールドを計算し、必要なスコアを計算できることを意味します。

画像.png

GET /news/_search
{
    
    
  "query": {
    
    
    "function_score": {
    
    
      "query": {
    
    
        "match": {
    
     "title": "台风" }
      },
      "script_score": {
    
    
        "script": {
    
    
          "params": {
    
    
            "readCount": 1,
            "likeCount":5,
            "commentCount":10
          },
          "source": "Math.log(params.readCount* doc['read_count'].value +params.likeCount* doc['like_count'].value+params.commentCount* doc['comment_count'].value) "
        }
      },
      "boost_mode": "multiply"
    }
  }
}

各ニュース記事には阅读数点赞数据、があり评论数、これら 3 つの指標を使用して記事の人気度を評価するスコアを計算し、queryその人気度とスコアを乗算することで、人気の高い記事を上位に表示することができます。このデモでは、単純な重み付けを使用して記事の人気度を計算します。一般的に、それが阅读数最大であり、点赞数次に评论数最小になります。

記事の人気度 = Log (コメント数 × 10 + いいね数 × 5 + 閲覧数) 記事の人気度 = Log(コメント数\× 10 + いいね数\× 5 + 閲覧数)記事の人気=Log (コメント_×10+いいね!×5+読み

ここではデモンストレーションのために、単純に記事の人気度を数えますが、実際はこれよりもはるかに複雑で、記事の種類によって重要性も異なる可能性があります。

field_value_factorの使用法

field_value_factorelasticsearchこれは、いくつかの組み込み関数を提供するものとして理解できます。script_score毎回記述するのがscript_scoreあまりにも便利すぎてはなりません。いくつかの組み込み関数があれば、そのまま使用するのがはるかに便利になります。実際に見てみましょうデモ

GET /news/_search
{
    
    
    "query": {
    
    
        "function_score": {
    
    
            "query": {
    
    
                "match": {
    
    
                    "title": "台风"
                }
            },
            "field_value_factor": {
    
    
                "field": "rank",
                "factor": 10,
                "modifier": "sqrt",
                "missing": 1
            },
            "boost_mode": "multiply"
        }
    }
}

ここにあるものはfield_value_factor同等ですscript_scoresqrt(10 * doc['rank'].value)ここでの係数は乗算する回数です。デフォルトは 1 回です。missingそのようなフィールドがない場合、デフォルト値は 1 です。modifierこれは計算関数であり、field計算されるフィールドです。

modifier計算機能には以下の種類があります。

modifier関数 採点方法
none フィールド値に乗数を適用しないでください。
log  フィールド値の常用対数を取得します 。この関数は 0 から 1 までの値に使用すると負の値を返し、エラーが発生するため、 log1p 代わりに使用することをお勧めします。
log1p フィールド値に 1 を加算し、常用対数を計算します。
log2p フィールド値に 2 を加算し、常用対数を計算します。
ln Take the natural logarithm of the field value. Because this function will return a negative value and cause an error if used on values between 0 and 1, it is recommended to use ln1p instead.
ln1p Add 1 to the field value and take the natural logarithm
ln2p Add 2 to the field value and take the natural logarithm
square Square the field value (multiply it by itself)
sqrt Take the square root of the field value
reciprocal Reciprocate the field value, same as 1/x where x is the field’s value

衰减函数Decay functions的使用

衰减函数可以理解成计算文档中某一个字段与给定值的距离,如果距离越近得分就越高,距离越远得分就越低,这个就比较适用于新闻发布时间的衰减了,越久前发布的新闻,得分应该越小,排序越往后。我们直接看Demo

GET /news/_search
{
    
    
    "query": {
    
    
        "function_score": {
    
    
            "query": {
    
    
                "match": {
    
    
                    "title": "台风"
                }
            },
            "functions": [
                {
    
    
                    "gauss": {
    
    
                        "pub_time": {
    
    
                            "origin": "now",
                            "offset": "7d",
                            "scale": "60d",
                            "decay": 0.9
                        }
                    }
                },
                {
    
    
                    "exp": {
    
    
                        "location": {
    
    
                            "origin": {
    
    
                                "lat": 120.21551,
                                "lon": 30.25308
                            },
                            "offset": "50km",
                            "scale": "50km",
                            "decay": 0.1
                        }
                    }
                }
            ],
            "score_mode": "sum", 
            "boost_mode": "sum"
        }
    }
}

搜索结果:
画像.png

衰减函数有3种,分别为gauss高斯函数、lin线程函数、exp对数函数,具体的计算公式可以参考官方文档,这里我们主要理解衰减函数的4个参数作用是什么。

画像.png

  • origin
    例えば、上記のニュースリリース時刻の計算の原点は現在時刻であり、経度および緯度の計算の原点はユーザーの検索位置であるなど、距離の計算の原点として理解できます。 、杭州にいる場合は、origin杭州の経度と緯度です。

  • offset
    このオフセットは、減衰する必要のない距離として理解できます。たとえば、上記のデモでは、距離は7dpub_timeですoffset。これは、過去 7 日間にリリースされたニュースを減衰する必要がないことを意味し、スコアは直接的には 1 です。緯度と経度の計算でoffset50km とは、ユーザーから 50km 以内のニュースは減衰する必要がないことを意味し、50km 以内のニュースは基本的に杭州のローカル ニュースであるため、減衰する必要はありません。

  • scaleこれらdecay
    2 つのパラメーターについては、3 つの関数の公式の減衰図を参照できます。scaleこれは、スコアが元の時間まで減衰したdecay後の距離を表します例えば、上記の時間減衰は7日以内のニュースは減衰しないことを意味し、67日前(7d+60d)のニューススコアは0.9、経度緯度減衰は50km以内の距離は減衰しないことを意味します。 、および 100km (50km+50km) の外れ値のスコアは 0.1 です。scalescaleoffset=7d, scale=60d,decay= 0.9offset=50km, scale=50km,decay= 0.1

要約する

elasticsearchこれfunction_scoreにより、非常に柔軟なカスタム スコアリング戦略がいくつか提供されます。実際のプロジェクトでは、独自のニーズに応じてこれらのスコアリング戦略を合理的に組み合わせ、独自の検索ニーズを満たすために対応するパラメーターを調整する必要があります。この記事では主に使用方法を紹介し、function_score次に実際の検索アプリケーションをもとに、よりわかりやすい検索効果を実現するためのこれらの機能の組み合わせや設定方法を紹介します。

おすすめ

転載: blog.csdn.net/whzhaochao/article/details/131999572