-
Notifications
You must be signed in to change notification settings - Fork 25.6k
Description
Elasticsearch version: 5.1.2 and 5.3.2
JVM version: 1.8.0_111 and 1.8.0_131
OS version: 4.4.0-75-generic and 2.6.32-642.13.1.el6.x86_64
Description of the problem including expected versus actual behavior:
Consider the following query:
curl -XPOST http://localhost:9200/test/_search -d '{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"filter": { "match": { "test_value": 1 } },
"weight": 1
},
{
"filter": { "match": { "test_value":1 } },
"script_score": {
"script": "-1"
},
"weight": -1
}
],
"score_mode": "sum",
"boost_mode": "replace"
}
}
}' | jq
In principle, this should produce a score of 2 (with a contribution of 1 from each filter) for any event with test_value = 1. However, this query will always produce a score of 1. Any pair of functions with a weight of 1 and weight of -1 will produce a score of 1. Any set of functions where the sum of the weights is 0 will produce a score of 1.
I think the bug is in FiltersFunctionScoreQuery.java around line 311.
default: // Avg / Total
double totalFactor = 0.0f;
double weightSum = 0;
for (int i = 0; i < filterFunctions.length; i++) {
if (docSets[i].get(docId)) {
totalFactor += functions[i].score(docId, subQueryScore);
if (filterFunctions[i].function instanceof WeightFactorFunction) {
weightSum += ((WeightFactorFunction) filterFunctions[i].function).getWeight();
} else {
weightSum += 1.0;
}
}
}
if (weightSum != 0) {
factor = totalFactor;
if (scoreMode == ScoreMode.AVG) {
factor /= weightSum;
}
}
break;
}
return factor;
There is an if-statement checking if the weightSum is zero, presumably to avoid a divide by zero when computing the weighted average. However, since the same block of code is used to compute a straightforward weighted sum (not just a weighted average), this if-statement results in the method returning the default value for factor which is 1. I believe if we are computing a weighted sum, we should just set factor = totalFactor with no dependence on the value of weightSum.
Steps to reproduce:
curl -XPUT http://localhost:9200/test -d '{
"mappings": {
"test": {
"properties": {
"test_value": {
"type": "integer"
}
}
}
}
}'
curl -XPOST http://localhost:9200/test/test -d '{
"test_value": 1
}'
# Returns a score of 2.1 as expected
curl -XPOST http://localhost:9200/test/_search -d '{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"filter": { "match": { "test_value": 1 } },
"weight": 1
},
{
"filter": { "match": { "test_value":1 } },
"script_score": {
"script": "-1"
},
"weight": -1.1
}
],
"score_mode": "sum",
"boost_mode": "replace"
}
}
}' | jq
# Returns a score of 1 instead of the expected score of 2
curl -XPOST http://localhost:9200/test/_search -d '{
"query": {
"function_score": {
"query": { "match_all": {} },
"functions": [
{
"filter": { "match": { "test_value": 1 } },
"weight": 1
},
{
"filter": { "match": { "test_value":1 } },
"script_score": {
"script": "-1"
},
"weight": -1
}
],
"score_mode": "sum",
"boost_mode": "replace"
}
}
}' | jq