/*
* 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.search.functionscore;
import org.elasticsearch.Version;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchPhaseExecutionException;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.search.SearchType;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.functionscore.DecayFunctionBuilder;
import org.elasticsearch.index.query.functionscore.gauss.GaussDecayFunctionBuilder;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.test.ESIntegTestCase;
import org.elasticsearch.test.VersionUtils;
import org.joda.time.DateTime;
import org.joda.time.DateTimeZone;
import org.junit.Test;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import static org.elasticsearch.client.Requests.indexRequest;
import static org.elasticsearch.client.Requests.searchRequest;
import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;
import static org.elasticsearch.index.query.QueryBuilders.*;
import static org.elasticsearch.index.query.functionscore.ScoreFunctionBuilders.*;
import static org.elasticsearch.search.builder.SearchSourceBuilder.searchSource;
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.*;
import static org.hamcrest.Matchers.*;
public class DecayFunctionScoreIT extends ESIntegTestCase {
@Test
public void testDistanceScoreGeoLinGaussExp() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("loc").field("type", "geo_point").endObject().endObject().endObject().endObject()));
ensureYellow();
List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
indexBuilders.add(client().prepareIndex()
.setType("type1")
.setId("1")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 10).field("lon", 20).endObject()
.endObject()));
indexBuilders.add(client().prepareIndex()
.setType("type1")
.setId("2")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 11).field("lon", 22).endObject()
.endObject()));
int numDummyDocs = 20;
for (int i = 1; i <= numDummyDocs; i++) {
indexBuilders.add(client().prepareIndex()
.setType("type1")
.setId(Integer.toString(i + 3))
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 11 + i).field("lon", 22 + i)
.endObject().endObject()));
}
indexRandom(true, indexBuilders);
// Test Gauss
List<Float> lonlat = new ArrayList<>();
lonlat.add(20f);
lonlat.add(11f);
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(constantScoreQuery(termQuery("test", "value")))));
SearchResponse sr = response.actionGet();
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), gaussDecayFunction("loc", lonlat, "1000km")))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat(sh.getAt(1).getId(), equalTo("2"));
// Test Exp
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(constantScoreQuery(termQuery("test", "value")))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), linearDecayFunction("loc", lonlat, "1000km")))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat(sh.getAt(1).getId(), equalTo("2"));
// Test Lin
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(constantScoreQuery(termQuery("test", "value")))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), exponentialDecayFunction("loc", lonlat, "1000km")))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat(sh.getAt(1).getId(), equalTo("2"));
}
@Test
public void testDistanceScoreGeoLinGaussExpWithOffset() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num").field("type", "double").endObject().endObject().endObject().endObject()));
ensureYellow();
// add tw docs within offset
List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
indexBuilders.add(client().prepareIndex().setType("type1").setId("1").setIndex("test")
.setSource(jsonBuilder().startObject().field("test", "value").field("num", 0.5).endObject()));
indexBuilders.add(client().prepareIndex().setType("type1").setId("2").setIndex("test")
.setSource(jsonBuilder().startObject().field("test", "value").field("num", 1.7).endObject()));
// add docs outside offset
int numDummyDocs = 20;
for (int i = 0; i < numDummyDocs; i++) {
indexBuilders.add(client().prepareIndex().setType("type1").setId(Integer.toString(i + 3)).setIndex("test")
.setSource(jsonBuilder().startObject().field("test", "value").field("num", 3.0 + i).endObject()));
}
indexRandom(true, indexBuilders);
// Test Gauss
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource()
.size(numDummyDocs + 2)
.query(functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 1.0, 5.0).setOffset(1.0))
.boostMode(CombineFunction.REPLACE.getName()))));
SearchResponse sr = response.actionGet();
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).score(), equalTo(sh.getAt(0).score()));
for (int i = 0; i < numDummyDocs; i++) {
assertThat(sh.getAt(i + 2).getId(), equalTo(Integer.toString(i + 3)));
}
// Test Exp
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource()
.size(numDummyDocs + 2)
.query(functionScoreQuery(termQuery("test", "value"),
exponentialDecayFunction("num", 1.0, 5.0).setOffset(1.0)).boostMode(
CombineFunction.REPLACE.getName()))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).score(), equalTo(sh.getAt(0).score()));
for (int i = 0; i < numDummyDocs; i++) {
assertThat(sh.getAt(i + 2).getId(), equalTo(Integer.toString(i + 3)));
}
// Test Lin
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource()
.size(numDummyDocs + 2)
.query(functionScoreQuery(termQuery("test", "value"), linearDecayFunction("num", 1.0, 20.0).setOffset(1.0))
.boostMode(CombineFunction.REPLACE.getName()))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (numDummyDocs + 2)));
assertThat(sh.getAt(0).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).getId(), anyOf(equalTo("1"), equalTo("2")));
assertThat(sh.getAt(1).score(), equalTo(sh.getAt(0).score()));
}
@Test
public void testBoostModeSettingWorks() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("loc").field("type", "geo_point").endObject().endObject().endObject().endObject()));
ensureYellow();
List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
indexBuilders.add(client().prepareIndex()
.setType("type1")
.setId("1")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 11).field("lon", 21).endObject()
.endObject()));
indexBuilders.add(client().prepareIndex()
.setType("type1")
.setId("2")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value value").startObject("loc").field("lat", 11).field("lon", 20)
.endObject().endObject()));
indexRandom(true, false, indexBuilders); // force no dummy docs
// Test Gauss
List<Float> lonlat = new ArrayList<>();
lonlat.add(20f);
lonlat.add(11f);
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("loc", lonlat, "1000km")).boostMode(
CombineFunction.MULT.getName()))));
SearchResponse sr = response.actionGet();
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (2)));
assertThat(sh.getAt(0).getId(), isOneOf("1"));
assertThat(sh.getAt(1).getId(), equalTo("2"));
// Test Exp
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("loc", lonlat, "1000km")).boostMode(
CombineFunction.REPLACE.getName()))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (2)));
assertThat(sh.getAt(0).getId(), equalTo("2"));
assertThat(sh.getAt(1).getId(), equalTo("1"));
}
@Test
public void testParseGeoPoint() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("loc").field("type", "geo_point").endObject().endObject().endObject().endObject()));
ensureYellow();
client().prepareIndex()
.setType("type1")
.setId("1")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 20).field("lon", 11).endObject()
.endObject()).setRefresh(true).get();
GeoPoint point = new GeoPoint(20, 11);
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("loc", point, "1000km")).boostMode(
CombineFunction.MULT.getName()))));
SearchResponse sr = response.actionGet();
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.30685282, 1.e-5));
float[] coords = { 11, 20 };
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("loc", coords, "1000km")).boostMode(
CombineFunction.MULT.getName()))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.30685282, 1.e-5));
}
@Test
public void testCombineModes() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num").field("type", "double").endObject().endObject().endObject().endObject()));
ensureYellow();
client().prepareIndex().setType("type1").setId("1").setIndex("test")
.setSource(jsonBuilder().startObject().field("test", "value").field("num", 1.0).endObject()).setRefresh(true).get();
// function score should return 0.5 for this function
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.MULT))));
SearchResponse sr = response.actionGet();
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.153426408, 1.e-5));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.REPLACE))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.5, 1.e-5));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.SUM))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.30685282 + 0.5, 1.e-5));
logger.info("--> Hit[0] {} Explanation:\n {}", sr.getHits().getAt(0).id(), sr.getHits().getAt(0).explanation());
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.AVG))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo((0.30685282 + 0.5) / 2, 1.e-5));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.MIN))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.30685282, 1.e-5));
response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num", 0.0, 1.0).setDecay(0.5)).boost(
2.0f).boostMode(CombineFunction.MAX))));
sr = response.actionGet();
sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (1)));
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat((double) sh.getAt(0).score(), closeTo(0.5, 1.e-5));
}
@Test(expected = SearchPhaseExecutionException.class)
public void testExceptionThrownIfScaleLE0() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num1").field("type", "date").endObject().endObject().endObject().endObject()));
ensureYellow();
client().index(
indexRequest("test").type("type1").id("1")
.source(jsonBuilder().startObject().field("test", "value").field("num1", "2013-05-27").endObject())).actionGet();
client().index(
indexRequest("test").type("type1").id("2")
.source(jsonBuilder().startObject().field("test", "value").field("num1", "2013-05-28").endObject())).actionGet();
refresh();
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num1", "2013-05-28", "-1d")))));
SearchResponse sr = response.actionGet();
assertOrderedSearchHits(sr, "2", "1");
}
@Test
public void testParseDateMath() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num1").field("type", "date").field("format", "epoch_millis").endObject().endObject().endObject().endObject()));
ensureYellow();
client().index(
indexRequest("test").type("type1").id("1")
.source(jsonBuilder().startObject().field("test", "value").field("num1", System.currentTimeMillis()).endObject())).actionGet();
client().index(
indexRequest("test").type("type1").id("2")
.source(jsonBuilder().startObject().field("test", "value").field("num1", System.currentTimeMillis() - (1000 * 60 * 60 * 24)).endObject())).actionGet();
refresh();
SearchResponse sr = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num1", "now", "2d"))))).get();
assertNoFailures(sr);
assertOrderedSearchHits(sr, "1", "2");
sr = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(termQuery("test", "value"), gaussDecayFunction("num1", "now-1d", "2d"))))).get();
assertNoFailures(sr);
assertOrderedSearchHits(sr, "2", "1");
}
@Test(expected = IllegalStateException.class)
public void testExceptionThrownIfScaleRefNotBetween0And1() throws Exception {
DecayFunctionBuilder gfb = new GaussDecayFunctionBuilder("num1", "2013-05-28", "1d").setDecay(100);
}
@Test
public void testValueMissingLin() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num1").field("type", "date").endObject().startObject("num2").field("type", "double")
.endObject().endObject().endObject().endObject())
);
ensureYellow();
client().index(
indexRequest("test").type("type1").id("1")
.source(jsonBuilder().startObject().field("test", "value").field("num1", "2013-05-27").field("num2", "1.0")
.endObject())).actionGet();
client().index(
indexRequest("test").type("type1").id("2")
.source(jsonBuilder().startObject().field("test", "value").field("num2", "1.0").endObject())).actionGet();
client().index(
indexRequest("test").type("type1").id("3")
.source(jsonBuilder().startObject().field("test", "value").field("num1", "2013-05-30").field("num2", "1.0")
.endObject())).actionGet();
client().index(
indexRequest("test").type("type1").id("4")
.source(jsonBuilder().startObject().field("test", "value").field("num1", "2013-05-30").endObject())).actionGet();
refresh();
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value"))).add(linearDecayFunction("num1", "2013-05-28", "+3d"))
.add(linearDecayFunction("num2", "0.0", "1")).scoreMode("multiply"))));
SearchResponse sr = response.actionGet();
assertNoFailures(sr);
SearchHits sh = sr.getHits();
assertThat(sh.hits().length, equalTo(4));
double[] scores = new double[4];
for (int i = 0; i < sh.hits().length; i++) {
scores[Integer.parseInt(sh.getAt(i).getId()) - 1] = sh.getAt(i).getScore();
}
assertThat(scores[0], lessThan(scores[1]));
assertThat(scores[2], lessThan(scores[3]));
}
@Test
public void testDateWithoutOrigin() throws Exception {
DateTime dt = new DateTime(DateTimeZone.UTC);
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num1").field("type", "date").endObject().endObject().endObject().endObject()));
ensureYellow();
DateTime docDate = dt.minusDays(1);
String docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth());
client().index(
indexRequest("test").type("type1").id("1")
.source(jsonBuilder().startObject().field("test", "value").field("num1", docDateString).endObject())).actionGet();
docDate = dt.minusDays(2);
docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth());
client().index(
indexRequest("test").type("type1").id("2")
.source(jsonBuilder().startObject().field("test", "value").field("num1", docDateString).endObject())).actionGet();
docDate = dt.minusDays(3);
docDateString = docDate.getYear() + "-" + String.format(Locale.ROOT, "%02d", docDate.getMonthOfYear()) + "-" + String.format(Locale.ROOT, "%02d", docDate.getDayOfMonth());
client().index(
indexRequest("test").type("type1").id("3")
.source(jsonBuilder().startObject().field("test", "value").field("num1", docDateString).endObject())).actionGet();
refresh();
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(QueryBuilders.matchAllQuery()).add(linearDecayFunction("num1", "1000w"))
.add(gaussDecayFunction("num1", "1d")).add(exponentialDecayFunction("num1", "1000w"))
.scoreMode("multiply"))));
SearchResponse sr = response.actionGet();
assertNoFailures(sr);
SearchHits sh = sr.getHits();
assertThat(sh.hits().length, equalTo(3));
double[] scores = new double[4];
for (int i = 0; i < sh.hits().length; i++) {
scores[Integer.parseInt(sh.getAt(i).getId()) - 1] = sh.getAt(i).getScore();
}
assertThat(scores[1], lessThan(scores[0]));
assertThat(scores[2], lessThan(scores[1]));
}
@Test
public void testManyDocsLin() throws Exception {
Version version = VersionUtils.randomVersionBetween(random(), Version.V_1_0_0, Version.CURRENT);
Settings settings = Settings.settingsBuilder().put(IndexMetaData.SETTING_VERSION_CREATED, version).build();
XContentBuilder mapping = jsonBuilder().startObject().startObject("type").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("date").field("type", "date").endObject().startObject("num").field("type", "double")
.endObject().startObject("geo").field("type", "geo_point").field("ignore_malformed", true);
if (version.before(Version.V_2_2_0)) {
mapping.field("coerce", true);
}
mapping.endObject().endObject().endObject().endObject();
assertAcked(prepareCreate("test").setSettings(settings).addMapping("type", mapping));
ensureYellow();
int numDocs = 200;
List<IndexRequestBuilder> indexBuilders = new ArrayList<>();
for (int i = 0; i < numDocs; i++) {
double lat = 100 + (int) (10.0 * (float) (i) / (float) (numDocs));
double lon = 100;
int day = (int) (29.0 * (float) (i) / (float) (numDocs)) + 1;
String dayString = day < 10 ? "0" + Integer.toString(day) : Integer.toString(day);
String date = "2013-05-" + dayString;
indexBuilders.add(client().prepareIndex()
.setType("type")
.setId(Integer.toString(i))
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").field("date", date).field("num", i).startObject("geo")
.field("lat", lat).field("lon", lon).endObject().endObject()));
}
indexRandom(true, indexBuilders);
List<Float> lonlat = new ArrayList<>();
lonlat.add(100f);
lonlat.add(110f);
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().size(numDocs).query(
functionScoreQuery(termQuery("test", "value"))
.add(new MatchAllQueryBuilder(), linearDecayFunction("date", "2013-05-30", "+15d"))
.add(new MatchAllQueryBuilder(), linearDecayFunction("geo", lonlat, "1000km"))
.add(new MatchAllQueryBuilder(), linearDecayFunction("num", numDocs, numDocs / 2.0))
.scoreMode("multiply").boostMode(CombineFunction.REPLACE.getName()))));
SearchResponse sr = response.actionGet();
assertNoFailures(sr);
SearchHits sh = sr.getHits();
assertThat(sh.hits().length, equalTo(numDocs));
double[] scores = new double[numDocs];
for (int i = 0; i < numDocs; i++) {
scores[Integer.parseInt(sh.getAt(i).getId())] = sh.getAt(i).getScore();
}
for (int i = 0; i < numDocs - 1; i++) {
assertThat(scores[i], lessThan(scores[i + 1]));
}
}
@Test(expected = SearchPhaseExecutionException.class)
public void testParsingExceptionIfFieldDoesNotExist() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type",
jsonBuilder().startObject().startObject("type").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("geo").field("type", "geo_point").endObject().endObject().endObject().endObject()));
ensureYellow();
int numDocs = 2;
client().index(
indexRequest("test").type("type").source(
jsonBuilder().startObject().field("test", "value").startObject("geo").field("lat", 1).field("lon", 2).endObject()
.endObject())).actionGet();
refresh();
List<Float> lonlat = new ArrayList<>();
lonlat.add(100f);
lonlat.add(110f);
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource()
.size(numDocs)
.query(functionScoreQuery(termQuery("test", "value")).add(new MatchAllQueryBuilder(),
linearDecayFunction("type.geo", lonlat, "1000km")).scoreMode("multiply"))));
SearchResponse sr = response.actionGet();
}
@Test(expected = SearchPhaseExecutionException.class)
public void testParsingExceptionIfFieldTypeDoesNotMatch() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type",
jsonBuilder().startObject().startObject("type").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num").field("type", "string").endObject().endObject().endObject().endObject()));
ensureYellow();
client().index(
indexRequest("test").type("type").source(
jsonBuilder().startObject().field("test", "value").field("num", Integer.toString(1)).endObject())).actionGet();
refresh();
// so, we indexed a string field, but now we try to score a num field
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery(termQuery("test", "value")).add(new MatchAllQueryBuilder(),
linearDecayFunction("num", 1.0, 0.5)).scoreMode("multiply"))));
response.actionGet();
}
@Test
public void testNoQueryGiven() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type",
jsonBuilder().startObject().startObject("type").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num").field("type", "double").endObject().endObject().endObject().endObject()));
ensureYellow();
client().index(
indexRequest("test").type("type").source(jsonBuilder().startObject().field("test", "value").field("num", 1.0).endObject()))
.actionGet();
refresh();
// so, we indexed a string field, but now we try to score a num field
ActionFuture<SearchResponse> response = client().search(
searchRequest().searchType(SearchType.QUERY_THEN_FETCH).source(
searchSource().query(
functionScoreQuery().add(new MatchAllQueryBuilder(), linearDecayFunction("num", 1, 0.5)).scoreMode(
"multiply"))));
response.actionGet();
}
@Test
public void testMultiFieldOptions() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type1",
jsonBuilder().startObject().startObject("type1").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("loc").field("type", "geo_point").endObject().startObject("num").field("type", "float").endObject().endObject().endObject().endObject()));
ensureYellow();
// Index for testing MIN and MAX
IndexRequestBuilder doc1 = client().prepareIndex()
.setType("type1")
.setId("1")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startArray("loc").startObject().field("lat", 10).field("lon", 20).endObject().startObject().field("lat", 12).field("lon", 23).endObject().endArray()
.endObject());
IndexRequestBuilder doc2 = client().prepareIndex()
.setType("type1")
.setId("2")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startObject("loc").field("lat", 11).field("lon", 22).endObject()
.endObject());
indexRandom(true, doc1, doc2);
ActionFuture<SearchResponse> response = client().search(
searchRequest().source(
searchSource().query(constantScoreQuery(termQuery("test", "value")))));
SearchResponse sr = response.actionGet();
assertSearchHits(sr, "1", "2");
SearchHits sh = sr.getHits();
assertThat(sh.getTotalHits(), equalTo((long) (2)));
List<Float> lonlat = new ArrayList<>();
lonlat.add(20f);
lonlat.add(10f);
response = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), gaussDecayFunction("loc", lonlat, "1000km").setMultiValueMode("min")))));
sr = response.actionGet();
assertSearchHits(sr, "1", "2");
sh = sr.getHits();
assertThat(sh.getAt(0).getId(), equalTo("1"));
assertThat(sh.getAt(1).getId(), equalTo("2"));
response = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), gaussDecayFunction("loc", lonlat, "1000km").setMultiValueMode("max")))));
sr = response.actionGet();
assertSearchHits(sr, "1", "2");
sh = sr.getHits();
assertThat(sh.getAt(0).getId(), equalTo("2"));
assertThat(sh.getAt(1).getId(), equalTo("1"));
// Now test AVG and SUM
doc1 = client().prepareIndex()
.setType("type1")
.setId("1")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").startArray("num").value(0.0).value(1.0).value(2.0).endArray()
.endObject());
doc2 = client().prepareIndex()
.setType("type1")
.setId("2")
.setIndex("test")
.setSource(
jsonBuilder().startObject().field("test", "value").field("num", 1.0)
.endObject());
indexRandom(true, doc1, doc2);
response = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), linearDecayFunction("num", "0", "10").setMultiValueMode("sum")))));
sr = response.actionGet();
assertSearchHits(sr, "1", "2");
sh = sr.getHits();
assertThat(sh.getAt(0).getId(), equalTo("2"));
assertThat(sh.getAt(1).getId(), equalTo("1"));
assertThat((double)(1.0 - sh.getAt(0).getScore()), closeTo((double)((1.0 - sh.getAt(1).getScore())/3.0), 1.e-6d));
response = client().search(
searchRequest().source(
searchSource().query(
functionScoreQuery(constantScoreQuery(termQuery("test", "value")), linearDecayFunction("num", "0", "10").setMultiValueMode("avg")))));
sr = response.actionGet();
assertSearchHits(sr, "1", "2");
sh = sr.getHits();
assertThat((double) (sh.getAt(0).getScore()), closeTo((double) (sh.getAt(1).getScore()), 1.e-6d));
}
@Test
public void errorMessageForFaultyFunctionScoreBody() throws Exception {
assertAcked(prepareCreate("test").addMapping(
"type",
jsonBuilder().startObject().startObject("type").startObject("properties").startObject("test").field("type", "string")
.endObject().startObject("num").field("type", "double").endObject().endObject().endObject().endObject()));
ensureYellow();
client().index(
indexRequest("test").type("type").source(jsonBuilder().startObject().field("test", "value").field("num", 1.0).endObject()))
.actionGet();
refresh();
XContentBuilder query = XContentFactory.jsonBuilder();
// query that contains a functions[] array but also a single function
query.startObject().startObject("function_score").startArray("functions").startObject().field("boost_factor", "1.3").endObject().endArray().field("boost_factor", "1").endObject().endObject();
try {
client().search(
searchRequest().source(
searchSource().query(query))).actionGet();
fail("Search should result in SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException e) {
logger.info(e.shardFailures()[0].reason());
assertThat(e.shardFailures()[0].reason(), containsString("already found [functions] array, now encountering [boost_factor]. did you mean [boost] instead?"));
}
query = XContentFactory.jsonBuilder();
// query that contains a single function and a functions[] array
query.startObject().startObject("function_score").field("boost_factor", "1").startArray("functions").startObject().field("boost_factor", "1.3").endObject().endArray().endObject().endObject();
try {
client().search(
searchRequest().source(
searchSource().query(query))).actionGet();
fail("Search should result in SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException e) {
logger.info(e.shardFailures()[0].reason());
assertThat(e.shardFailures()[0].reason(), containsString("already found [boost_factor], now encountering [functions]. did you mean [boost] instead?"));
}
query = XContentFactory.jsonBuilder();
// query that contains a single function (but not boost factor) and a functions[] array
query.startObject().startObject("function_score").startObject("random_score").field("seed", 3).endObject().startArray("functions").startObject().startObject("random_score").field("seed", 3).endObject().endObject().endArray().endObject().endObject();
try {
client().search(
searchRequest().source(
searchSource().query(query))).actionGet();
fail("Search should result in SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException e) {
logger.info(e.shardFailures()[0].reason());
assertThat(e.shardFailures()[0].reason(), containsString("already found [random_score], now encountering [functions]"));
assertThat(e.shardFailures()[0].reason(), not(containsString("did you mean [boost] instead?")));
}
}
// issue https://github.com/elasticsearch/elasticsearch/issues/6292
@Test
public void testMissingFunctionThrowsElasticsearchParseException() throws IOException {
// example from issue https://github.com/elasticsearch/elasticsearch/issues/6292
String doc = "{\n" +
" \"text\": \"baseball bats\"\n" +
"}\n";
String query = "{\n" +
" \"function_score\": {\n" +
" \"score_mode\": \"sum\",\n" +
" \"boost_mode\": \"replace\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"filter\": {\n" +
" \"term\": {\n" +
" \"text\": \"baseball\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
"}\n";
client().prepareIndex("t", "test").setSource(doc).get();
refresh();
ensureYellow("t");
try {
client().search(
searchRequest().source(
searchSource().query(query))).actionGet();
fail("Should fail with SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException failure) {
assertThat(failure.toString(), containsString("SearchParseException"));
assertThat(failure.toString(), not(containsString("NullPointerException")));
}
query = "{\n" +
" \"function_score\": {\n" +
" \"score_mode\": \"sum\",\n" +
" \"boost_mode\": \"replace\",\n" +
" \"functions\": [\n" +
" {\n" +
" \"filter\": {\n" +
" \"term\": {\n" +
" \"text\": \"baseball\"\n" +
" }\n" +
" },\n" +
" \"boost_factor\": 2\n" +
" },\n" +
" {\n" +
" \"filter\": {\n" +
" \"term\": {\n" +
" \"text\": \"baseball\"\n" +
" }\n" +
" }\n" +
" }\n" +
" ]\n" +
" }\n" +
"}";
try {
client().search(
searchRequest().source(
searchSource().query(query))).actionGet();
fail("Should fail with SearchPhaseExecutionException");
} catch (SearchPhaseExecutionException failure) {
assertThat(failure.toString(), containsString("SearchParseException"));
assertThat(failure.toString(), not(containsString("NullPointerException")));
assertThat(failure.toString(), containsString("an entry in functions list is missing a function"));
}
// next test java client
try {
client().prepareSearch("t").setQuery(QueryBuilders.functionScoreQuery(QueryBuilders.matchAllQuery(), null)).get();
} catch (IllegalArgumentException failure) {
assertThat(failure.toString(), containsString("function must not be null"));
}
try {
client().prepareSearch("t").setQuery(QueryBuilders.functionScoreQuery().add(QueryBuilders.matchAllQuery(), null)).get();
} catch (IllegalArgumentException failure) {
assertThat(failure.toString(), containsString("function must not be null"));
}
try {
client().prepareSearch("t").setQuery(QueryBuilders.functionScoreQuery().add(null)).get();
} catch (IllegalArgumentException failure) {
assertThat(failure.toString(), containsString("function must not be null"));
}
}
}