赞
踩
以下内容都是基于elasticsearch-6.3.2.jar
“explain”: true 会打印分数计算过程
{
"value": 2.0702924e-8,
"description": "Function for field createdAt:",
"details": [
{
"value": 2.0702924e-8,
"description": "exp(- MAX[Math.max(Math.abs(1.582024989E12(=doc value) - 1.5842304E12(=origin))) - 0.0(=offset), 0)] * 8.022536812036404E-9)",
"details": []
}
]
}
以上结果片段是衰减函数exp的计算日志,由details-》description 大概是可以看出exp函数的公式, 但是有一个陌生的数字 8.022536812036404E-9 这是什么?怎么来了?日志中无法看到它的来路,只能在源码中一探究竟。
经过一系列的代码定位 搜索 终于找到了打印此处日志的代码块
org.elasticsearch.index.query.functionscore.ExponentialDecayFunctionBuilder.explainFunction:72
public Explanation explainFunction(String valueExpl, double value, double scale) {
return Explanation.match(
(float) evaluate(value, scale),
"exp(- " + valueExpl + " * " + -1 * scale + ")");
}
继续深入
org.elasticsearch.index.query.functionscore.DecayFunctionBuilder:558
public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
if (distance.advanceExact(docId) == false) {
return Explanation.noMatch("No value for the distance");
}
double value = distance.doubleValue();
return Explanation.match(
(float) score(docId, subQueryScore.getValue()),
"Function for field " + getFieldName() + ":",
func.explainFunction(getDistanceString(ctx, docId), value, scale));
}
func.explainFunction 看下func的面目
org.elasticsearch.index.query.functionscore.DecayFunctionBuilder:506
private final DecayFunction func;
public interface DecayFunction { double evaluate(double value, double scale); Explanation explainFunction(String valueString, double value, double scale); /** * The final scale parameter is computed from the scale parameter given by * the user and a value. This value is the value that the decay function * should compute if document distance and user defined scale equal. The * scale parameter for the function must be adjusted accordingly in this * function * * @param scale * the raw scale value given by the user * @param decay * the value which decay function should take once the distance * reaches this scale * */ double processScale(double scale, double decay); }
实现类有三个ExponentialDecayScoreFunction、GaussScoreFunction、LinearDecayScoreFunction 刚好对应了es的三种衰减函数。
注意一下processScale这个方法,看注释说 最终比例参数 是此方法计算出来的,看一下exp是怎么实现的。
org.elasticsearch.index.query.functionscore.ExponentialDecayFunctionBuilder:79
public double processScale(double scale, double decay) {
return Math.log(decay) / scale;
}
看到此处就可以明白 8.022536812036404E-9是怎么来的了。
回到org.elasticsearch.index.query.functionscore.DecayFunctionBuilder:558
public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
if (distance.advanceExact(docId) == false) {
return Explanation.noMatch("No value for the distance");
}
double value = distance.doubleValue();
return Explanation.match(
(float) score(docId, subQueryScore.getValue()),
"Function for field " + getFieldName() + ":",
func.explainFunction(getDistanceString(ctx, docId), value, scale));
}
此处调用了explainFunction方法 前面已经说过此方法用于打印调试日志。进入getDistanceString 方法看一下,此方法是一个抽象方法,实现类有有两个,用于数值衰减 和 地理位置衰减。
protected String getDistanceString(LeafReaderContext ctx, int docId) throws IOException { StringBuilder values = new StringBuilder(mode.name()); values.append("["); final SortedNumericDoubleValues doubleValues = fieldData.load(ctx).getDoubleValues(); if (doubleValues.advanceExact(docId)) { final int num = doubleValues.docValueCount(); for (int i = 0; i < num; i++) { double value = doubleValues.nextValue(); values.append("Math.max(Math.abs("); values.append(value).append("(=doc value) - "); values.append(origin).append("(=origin))) - "); values.append(offset).append("(=offset), 0)"); if (i != num - 1) { values.append(", "); } } } else { values.append("0.0"); } values.append("]"); return values.toString(); }
正是调试日志。
再看func.explainFunction(getDistanceString(ctx, docId), value, scale));
value参数是double value = distance.doubleValue()得到的,继续深入,可以看到
final NumericDoubleValues distance = distance(ctx); 537行
进入distance方法
protected NumericDoubleValues distance(LeafReaderContext context) { final SortedNumericDoubleValues doubleValues = fieldData.load(context).getDoubleValues(); return mode.select(new SortingNumericDoubleValues() { @Override public boolean advanceExact(int docId) throws IOException { if (doubleValues.advanceExact(docId)) { int n = doubleValues.docValueCount(); resize(n); for (int i = 0; i < n; i++) { values[i] = Math.max(0.0d, Math.abs(doubleValues.nextValue() - origin) - offset); } sort(); return true; } else { return false; } } }, 0.0); }
446行
Math.max(0.0d, Math.abs(doubleValues.nextValue() - origin) - offset);
再看func.explainFunction(getDistanceString(ctx, docId), value, scale));
看下scale参数的值
org.elasticsearch.index.query.functionscore.DecayFunctionBuilder:519行
this.scale = func.processScale(userSuppiedScale, decay);
processScale这个方法和我们前面猜想的一样 就是根据设置的scale 和 decay计算出一个比例
返回主线 继续看 explainScore 方法 556行
(float) score(docId, subQueryScore.getValue())
这里就是计算最终分数的了,点进去看下
public double score(int docId, float subQueryScore) throws IOException {
if (distance.advanceExact(docId)) {
return func.evaluate(distance.doubleValue(), scale);
} else {
return 0;
}
}
evaluate 此方法正是前面说的接口中三个方法的其中之一,看下exp如何实现的
public double evaluate(double value, double scale) {
return Math.exp(scale * value);
}
此刻真相大白
value=Math.max(0.0d, Math.abs(doubleValues.nextValue() - origin) - offset);
processScale= Math.log(decay) / scale;
最终:
sorce=Math.exp(processScale* value)
衰减函数配置
"exp": {
"createdAt": {
"origin": "2020-03-15 00:00:00",
"scale": "1d",
"offset": "0d",
"decay": 0.5
},
"multi_value_mode": "MAX"
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。