function score: fix cast in Gaussian decay function

Also fix the test
FunctionScoreTests#simpleWeightedFunctionsTestWithRandomWeightsAndRandomCombineMode
which sometimes failed due to rounding issues. Make sure
only floats are returned as scores to assure ratio of
expected and returned score is 1.0f.
This commit is contained in:
Britta Weber 2014-09-12 20:02:17 +02:00
parent 3142fec206
commit be7c75c745
2 changed files with 29 additions and 38 deletions

View File

@ -40,7 +40,7 @@ public class GaussDecayFunctionParser extends DecayFunctionParser {
public double evaluate(double value, double scale) {
// note that we already computed scale^2 in processScale() so we do
// not need to square it here.
return (float) Math.exp(0.5 * Math.pow(value, 2.0) / scale);
return Math.exp(0.5 * Math.pow(value, 2.0) / scale);
}
@Override

View File

@ -35,7 +35,6 @@ import java.io.IOException;
import java.util.concurrent.ExecutionException;
import static org.elasticsearch.client.Requests.searchRequest;
import static org.elasticsearch.common.io.Streams.copyToStringFromClasspath;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.FilterBuilders.termFilter;
import static org.elasticsearch.index.query.QueryBuilders.functionScoreQuery;
@ -51,10 +50,10 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
static final String TYPE = "type";
static final String INDEX = "index";
static final String TEXT_FIELD = "text_field";
static final String FLOAT_FIELD = "float_field";
static final String DOUBLE_FIELD = "double_field";
static final String GEO_POINT_FIELD = "geo_point_field";
static final XContentBuilder SIMPLE_DOC;
static final XContentBuilder MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD;
static final XContentBuilder MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD;
@Test
public void testExplainQueryOnlyOnce() throws IOException, ExecutionException, InterruptedException {
@ -106,7 +105,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
static {
XContentBuilder simpleDoc;
XContentBuilder mappingWithFloatAndGeoPointAndTestField;
XContentBuilder mappingWithDoubleAndGeoPointAndTestField;
try {
simpleDoc = jsonBuilder().startObject()
.field(TEXT_FIELD, "value")
@ -114,7 +113,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
.field("lat", 10)
.field("lon", 20)
.endObject()
.field(FLOAT_FIELD, 2.71828)
.field(DOUBLE_FIELD, Math.E)
.endObject();
} catch (IOException e) {
throw new ElasticsearchException("Exception while initializing FunctionScoreTests", e);
@ -122,7 +121,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
SIMPLE_DOC = simpleDoc;
try {
mappingWithFloatAndGeoPointAndTestField = jsonBuilder().startObject()
mappingWithDoubleAndGeoPointAndTestField = jsonBuilder().startObject()
.startObject(TYPE)
.startObject("properties")
.startObject(TEXT_FIELD)
@ -131,8 +130,8 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
.startObject(GEO_POINT_FIELD)
.field("type", "geo_point")
.endObject()
.startObject(FLOAT_FIELD)
.field("type", "float")
.startObject(DOUBLE_FIELD)
.field("type", "double")
.endObject()
.endObject()
.endObject()
@ -140,13 +139,13 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
} catch (IOException e) {
throw new ElasticsearchException("Exception while initializing FunctionScoreTests", e);
}
MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD = mappingWithFloatAndGeoPointAndTestField;
MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD = mappingWithDoubleAndGeoPointAndTestField;
}
@Test
public void testExplain() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate(INDEX).addMapping(
TYPE, MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD
TYPE, MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD
));
ensureYellow();
@ -158,12 +157,12 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
searchSource().query(
functionScoreQuery(termFilter(TEXT_FIELD, "value").cache(false))
.add(gaussDecayFunction(GEO_POINT_FIELD, new GeoPoint(10, 20), "1000km"))
.add(fieldValueFactorFunction(FLOAT_FIELD).modifier(FieldValueFactorFunction.Modifier.LN).setWeight(2))
.add(fieldValueFactorFunction(DOUBLE_FIELD).modifier(FieldValueFactorFunction.Modifier.LN).setWeight(2))
.add(scriptFunction("_index['" + TEXT_FIELD + "']['value'].tf()").setWeight(3))
).explain(true))).actionGet();
assertThat(responseWithWeights.getHits().getAt(0).getExplanation().toString(),
equalTo("5.999996 = (MATCH) function score, product of:\n 1.0 = (MATCH) ConstantScore(text_field:value), product of:\n 1.0 = boost\n 1.0 = queryNorm\n 5.999996 = (MATCH) Math.min of\n 5.999996 = (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 1.9999987 = (MATCH) function score, product of:\n 1.0 = match filter: *:*\n 1.9999987 = (MATCH) product of:\n 0.99999934 = field value function: ln(doc['float_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(
searchRequest().source(
@ -180,7 +179,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
@Test
public void simpleWeightedFunctionsTest() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate(INDEX).addMapping(
TYPE, MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD
TYPE, MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD
));
ensureYellow();
@ -191,7 +190,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
searchSource().query(
functionScoreQuery(termFilter(TEXT_FIELD, "value"))
.add(gaussDecayFunction(GEO_POINT_FIELD, new GeoPoint(10, 20), "1000km"))
.add(fieldValueFactorFunction(FLOAT_FIELD).modifier(FieldValueFactorFunction.Modifier.LN))
.add(fieldValueFactorFunction(DOUBLE_FIELD).modifier(FieldValueFactorFunction.Modifier.LN))
.add(scriptFunction("_index['" + TEXT_FIELD + "']['value'].tf()"))
))).actionGet();
SearchResponse responseWithWeights = client().search(
@ -199,29 +198,22 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
searchSource().query(
functionScoreQuery(termFilter(TEXT_FIELD, "value"))
.add(gaussDecayFunction(GEO_POINT_FIELD, new GeoPoint(10, 20), "1000km").setWeight(2))
.add(fieldValueFactorFunction(FLOAT_FIELD).modifier(FieldValueFactorFunction.Modifier.LN).setWeight(2))
.add(fieldValueFactorFunction(DOUBLE_FIELD).modifier(FieldValueFactorFunction.Modifier.LN).setWeight(2))
.add(scriptFunction("_index['" + TEXT_FIELD + "']['value'].tf()").setWeight(2))
))).actionGet();
assertThat((double) response.getHits().getAt(0).getScore(), closeTo(1.0, 1.e-5));
assertThat((double) responseWithWeights.getHits().getAt(0).getScore(), closeTo(8.0, 1.e-5));
assertThat(response.getHits().getAt(0).getScore(), is(1.0f));
assertThat(responseWithWeights.getHits().getAt(0).getScore(), is(8.0f));
}
@Test
public void simpleWeightedFunctionsTestWithRandomWeightsAndRandomCombineMode() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate(INDEX).addMapping(
TYPE,
MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD));
MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD));
ensureYellow();
XContentBuilder doc = jsonBuilder().startObject()
.field(TEXT_FIELD, "value")
.startObject(GEO_POINT_FIELD)
.field("lat", 10)
.field("lon", 20)
.endObject()
.field(FLOAT_FIELD, 10)
.endObject();
XContentBuilder doc = SIMPLE_DOC;
index(INDEX, TYPE, "1", doc);
refresh();
ScoreFunctionBuilder[] scoreFunctionBuilders = getScoreFunctionBuilders();
@ -232,7 +224,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
FunctionScoreQueryBuilder withWeights = functionScoreQuery(termFilter(TEXT_FIELD, "value")).scoreMode(scoreMode);
int weightscounter = 0;
for (ScoreFunctionBuilder builder : scoreFunctionBuilders) {
withWeights.add(builder.setWeight((float) weights[weightscounter]));
withWeights.add(builder.setWeight(weights[weightscounter]));
weightscounter++;
}
SearchResponse responseWithWeights = client().search(
@ -240,8 +232,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
).actionGet();
double expectedScore = computeExpectedScore(weights, scores, scoreMode);
assertThat(expectedScore / responseWithWeights.getHits().getAt(0).getScore(), closeTo(1.0, 1.e-6));
assertThat((float) expectedScore / responseWithWeights.getHits().getAt(0).getScore(), is(1.0f));
}
protected double computeExpectedScore(float[] weights, float[] scores, String scoreMode) {
@ -273,7 +264,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
}
if ("avg".equals(scoreMode)) {
expectedScore /= (double) weights.length;
expectedScore /= weights.length;
}
return expectedScore;
}
@ -282,7 +273,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
public void simpleWeightedFunctionsTestSingleFunction() throws IOException, ExecutionException, InterruptedException {
assertAcked(prepareCreate(INDEX).addMapping(
TYPE,
MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD));
MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD));
ensureYellow();
XContentBuilder doc = jsonBuilder().startObject()
@ -291,7 +282,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
.field("lat", 12)
.field("lon", 21)
.endObject()
.field(FLOAT_FIELD, 10)
.field(DOUBLE_FIELD, 10)
.endObject();
index(INDEX, TYPE, "1", doc);
refresh();
@ -306,7 +297,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
searchRequest().source(searchSource().query(withWeights))
).actionGet();
assertThat((double) scores[0] * weights[0] / responseWithWeights.getHits().getAt(0).getScore(), closeTo(1.0, 1.e-6));
assertThat( (double) scores[0] * weights[0]/ responseWithWeights.getHits().getAt(0).getScore(), closeTo(1.0, 1.e-6));
}
@ -334,16 +325,16 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
private float[] createRandomWeights(int size) {
float[] weights = new float[size];
for (int i = 0; i < weights.length; i++) {
weights[i] = randomFloat() * (randomBoolean() ? 1.0f : -1.0f) * (float) randomInt(100) + 1.e-6f;
weights[i] = randomFloat() * (randomBoolean() ? 1.0f : -1.0f) * randomInt(100) + 1.e-6f;
}
return weights;
}
public ScoreFunctionBuilder[] getScoreFunctionBuilders() {
ScoreFunctionBuilder[] builders = new ScoreFunctionBuilder[4];
builders[0] = gaussDecayFunction(GEO_POINT_FIELD, new GeoPoint(11, 20), "1000km");
builders[0] = gaussDecayFunction(GEO_POINT_FIELD, new GeoPoint(10, 20), "1000km");
builders[1] = randomFunction(10);
builders[2] = fieldValueFactorFunction(FLOAT_FIELD).modifier(FieldValueFactorFunction.Modifier.LN);
builders[2] = fieldValueFactorFunction(DOUBLE_FIELD).modifier(FieldValueFactorFunction.Modifier.LN);
builders[3] = scriptFunction("_index['" + TEXT_FIELD + "']['value'].tf()");
return builders;
}
@ -352,7 +343,7 @@ public class FunctionScoreTests extends ElasticsearchIntegrationTest {
public void checkWeightOnlyCreatesBoostFunction() throws IOException {
assertAcked(prepareCreate(INDEX).addMapping(
TYPE,
MAPPING_WITH_FLOAT_AND_GEO_POINT_AND_TEST_FIELD));
MAPPING_WITH_DOUBLE_AND_GEO_POINT_AND_TEXT_FIELD));
ensureYellow();
index(INDEX, TYPE, "1", SIMPLE_DOC);