Scripting: Add explicit error message when script_score script returns NaN

When a scoring script returns not a number, the current message is confusing (IllegalArgumentException[docID must be >= 0 and < maxDoc=3 (got docID=2147483647)]). This commit adds the error message ScriptException[script score function returns a wrong score: NaN].

Closes #2426
This commit is contained in:
tlrx 2014-12-02 21:40:16 +01:00
parent 7b64a5c2c8
commit a046ee756d
3 changed files with 71 additions and 2 deletions

View File

@ -22,6 +22,7 @@ package org.elasticsearch.common.lucene.search.function;
import org.apache.lucene.index.LeafReaderContext; import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Explanation; import org.apache.lucene.search.Explanation;
import org.apache.lucene.search.Scorer; import org.apache.lucene.search.Scorer;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.script.SearchScript; import org.elasticsearch.script.SearchScript;
import java.io.IOException; import java.io.IOException;
@ -96,7 +97,11 @@ public class ScriptScoreFunction extends ScoreFunction {
script.setNextDocId(docId); script.setNextDocId(docId);
scorer.docid = docId; scorer.docid = docId;
scorer.score = subQueryScore; scorer.score = subQueryScore;
return script.runAsDouble(); double result = script.runAsDouble();
if (Double.isNaN(result)) {
throw new ScriptException("script_score returned NaN");
}
return result;
} }
@Override @Override

View File

@ -0,0 +1,64 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.common.lucene.search.function;
import org.elasticsearch.script.AbstractFloatSearchScript;
import org.elasticsearch.script.ScriptException;
import org.elasticsearch.test.ElasticsearchTestCase;
import org.junit.Test;
import static org.hamcrest.Matchers.equalTo;
public class ScriptScoreFunctionTests extends ElasticsearchTestCase {
/**
* Tests https://github.com/elasticsearch/elasticsearch/issues/2426
*/
@Test
public void testScriptScoresReturnsNaN() {
try {
ScoreFunction scoreFunction = new ScriptScoreFunction("Float.NaN", null, new FloatValueScript(Float.NaN));
scoreFunction.score(randomInt(), randomFloat());
fail("should have thrown an exception about the script_score returning NaN");
} catch (ScriptException e) {
assertThat("message contains error about script_score returning NaN: " + e.getMessage(),
e.getMessage().contains("NaN"), equalTo(true));
}
}
static class FloatValueScript extends AbstractFloatSearchScript {
private final float value;
FloatValueScript(float value) {
this.value = value;
}
@Override
public float runAsFloat() {
return value;
}
@Override
public void setNextDocId(int doc) {
// nothing here
}
}
}

View File

@ -169,7 +169,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
assertThat(responseWithWeights.getHits().getAt(0).getExplanation().toString(), assertThat(responseWithWeights.getHits().getAt(0).getExplanation().toString(),
equalTo("6.0 = (MATCH) function score, product of:\n 1.0 = (MATCH) ConstantScore(text_field:value), product of:\n 1.0 = boost\n 1.0 = queryNorm\n 6.0 = (MATCH) Math.min of\n 6.0 = (MATCH) function score, score mode [multiply]\n 1.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 1.0 = (MATCH) Function for field geo_point_field:\n 1.0 = exp(-0.5*pow(MIN of: [Math.max(arcDistance([10.0, 20.0](=doc value),[10.0, 20.0](=origin)) - 0.0(=offset), 0)],2.0)/7.213475204444817E11)\n 2.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 2.0 = (MATCH) product of:\n 1.0 = field value function: ln(doc['double_field'].value * factor=1.0)\n 2.0 = weight\n 3.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 3.0 = (MATCH) product of:\n 1.0 = script score function, computed with script:\"_index['text_field']['value'].tf()\n 3.0 = weight\n 3.4028235E38 = maxBoost\n 1.0 = queryBoost\n") equalTo("6.0 = (MATCH) function score, product of:\n 1.0 = (MATCH) ConstantScore(text_field:value), product of:\n 1.0 = boost\n 1.0 = queryNorm\n 6.0 = (MATCH) Math.min of\n 6.0 = (MATCH) function score, score mode [multiply]\n 1.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 1.0 = (MATCH) Function for field geo_point_field:\n 1.0 = exp(-0.5*pow(MIN of: [Math.max(arcDistance([10.0, 20.0](=doc value),[10.0, 20.0](=origin)) - 0.0(=offset), 0)],2.0)/7.213475204444817E11)\n 2.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 2.0 = (MATCH) product of:\n 1.0 = field value function: ln(doc['double_field'].value * factor=1.0)\n 2.0 = weight\n 3.0 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 3.0 = (MATCH) product of:\n 1.0 = script score function, computed with script:\"_index['text_field']['value'].tf()\n 3.0 = weight\n 3.4028235E38 = maxBoost\n 1.0 = queryBoost\n")
); );
responseWithWeights = client().search( responseWithWeights = client().search(
searchRequest().source( searchRequest().source(
searchSource().query( searchSource().query(