Require a field when a `seed` is provided to the `random_score` function. (#25594)

We currently use fielddata on the `_id` field which is trappy, especially as we
do it implicitly. This changes the `random_score` function to use doc ids when
no seed is provided and to suggest a field when a seed is provided.

For now the change only emits a deprecation warning when no field is supplied
but this should be replaced by a strict check on 7.0.

Closes #25240
This commit is contained in:
Adrien Grand 2017-07-19 14:11:15 +02:00 committed by GitHub
parent f69decf509
commit f1ff7f2454
12 changed files with 176 additions and 73 deletions

View File

@ -142,7 +142,7 @@ public class QueryDSLDocumentationTests extends ESTestCase {
FilterFunctionBuilder[] functions = {
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
matchQuery("name", "kimchy"), // <1>
randomFunction("ABCDEF")), // <2>
randomFunction()), // <2>
new FunctionScoreQueryBuilder.FilterFunctionBuilder(
exponentialDecayFunction("age", 0L, 1L)) // <3>
};

View File

@ -18,6 +18,8 @@
*/
package org.elasticsearch.common.lucene.search.function;
import com.carrotsearch.hppc.BitMixer;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.search.Explanation;
import org.apache.lucene.util.StringHelper;
@ -33,17 +35,9 @@ import java.util.Objects;
*/
public class RandomScoreFunction extends ScoreFunction {
private int originalSeed;
private int saltedSeed;
private final IndexFieldData<?> uidFieldData;
/**
* Default constructor. Only useful for constructing as a placeholder, but should not be used for actual scoring.
*/
public RandomScoreFunction() {
super(CombineFunction.MULTIPLY);
uidFieldData = null;
}
private final int originalSeed;
private final int saltedSeed;
private final IndexFieldData<?> fieldData;
/**
* Creates a RandomScoreFunction.
@ -55,33 +49,43 @@ public class RandomScoreFunction extends ScoreFunction {
public RandomScoreFunction(int seed, int salt, IndexFieldData<?> uidFieldData) {
super(CombineFunction.MULTIPLY);
this.originalSeed = seed;
this.saltedSeed = seed ^ salt;
this.uidFieldData = uidFieldData;
if (uidFieldData == null) throw new NullPointerException("uid missing");
this.saltedSeed = BitMixer.mix(seed, salt);
this.fieldData = uidFieldData;
}
@Override
public LeafScoreFunction getLeafScoreFunction(LeafReaderContext ctx) {
AtomicFieldData leafData = uidFieldData.load(ctx);
final SortedBinaryDocValues uidByteData = leafData.getBytesValues();
if (uidByteData == null) throw new NullPointerException("failed to get uid byte data");
final SortedBinaryDocValues values;
if (fieldData != null) {
AtomicFieldData leafData = fieldData.load(ctx);
values = leafData.getBytesValues();
if (values == null) throw new NullPointerException("failed to get fielddata");
} else {
values = null;
}
return new LeafScoreFunction() {
@Override
public double score(int docId, float subQueryScore) throws IOException {
if (uidByteData.advanceExact(docId) == false) {
throw new AssertionError("Document without a _uid");
int hash;
if (values == null) {
hash = BitMixer.mix(ctx.docBase + docId);
} else if (values.advanceExact(docId)) {
hash = StringHelper.murmurhash3_x86_32(values.nextValue(), saltedSeed);
} else {
// field has no value
hash = saltedSeed;
}
int hash = StringHelper.murmurhash3_x86_32(uidByteData.nextValue(), saltedSeed);
return (hash & 0x00FFFFFF) / (float)(1 << 24); // only use the lower 24 bits to construct a float from 0.0-1.0
}
@Override
public Explanation explainScore(int docId, Explanation subQueryScore) throws IOException {
String field = fieldData == null ? null : fieldData.getFieldName();
return Explanation.match(
CombineFunction.toFloat(score(docId, subQueryScore.getValue())),
"random score function (seed: " + originalSeed + ")");
"random score function (seed: " + originalSeed + ", field: " + field + ")");
}
};
}
@ -94,8 +98,8 @@ public class RandomScoreFunction extends ScoreFunction {
@Override
protected boolean doEquals(ScoreFunction other) {
RandomScoreFunction randomScoreFunction = (RandomScoreFunction) other;
return this.originalSeed == randomScoreFunction.originalSeed &&
this.saltedSeed == randomScoreFunction.saltedSeed;
return this.originalSeed == randomScoreFunction.originalSeed
&& this.saltedSeed == randomScoreFunction.saltedSeed;
}
@Override

View File

@ -315,7 +315,7 @@ public class QueryShardContext extends QueryRewriteContext {
}
}
public final Index index() {
public Index index() {
return indexSettings.getIndex();
}

View File

@ -18,14 +18,16 @@
*/
package org.elasticsearch.index.query.functionscore;
import org.elasticsearch.Version;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.StreamOutput;
import org.elasticsearch.common.logging.DeprecationLogger;
import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.lucene.search.function.RandomScoreFunction;
import org.elasticsearch.common.lucene.search.function.ScoreFunction;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.index.fielddata.IndexFieldData;
import org.elasticsearch.index.mapper.IdFieldMapper;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.UidFieldMapper;
@ -38,7 +40,11 @@ import java.util.Objects;
* A function that computes a random score for the matched documents
*/
public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScoreFunctionBuilder> {
private static final DeprecationLogger DEPRECATION_LOGGER = new DeprecationLogger(Loggers.getLogger(RandomScoreFunctionBuilder.class));
public static final String NAME = "random_score";
private String field;
private Integer seed;
public RandomScoreFunctionBuilder() {
@ -52,6 +58,9 @@ public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScore
if (in.readBoolean()) {
seed = in.readInt();
}
if (in.getVersion().onOrAfter(Version.V_6_0_0_alpha3)) {
field = in.readOptionalString();
}
}
@Override
@ -62,6 +71,9 @@ public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScore
} else {
out.writeBoolean(false);
}
if (out.getVersion().onOrAfter(Version.V_6_0_0_alpha3)) {
out.writeOptionalString(field);
}
}
@Override
@ -105,12 +117,33 @@ public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScore
return seed;
}
/**
* Set the field to be used for random number generation. This parameter is compulsory
* when a {@link #seed(int) seed} is set and ignored otherwise. Note that documents that
* have the same value for a field will get the same score.
*/
public RandomScoreFunctionBuilder setField(String field) {
this.field = field;
return this;
}
/**
* Get the field to use for random number generation.
* @see #setField(String)
*/
public String getField() {
return field;
}
@Override
public void doXContent(XContentBuilder builder, Params params) throws IOException {
builder.startObject(getName());
if (seed != null) {
builder.field("seed", seed);
}
if (field != null) {
builder.field("field", field);
}
builder.endObject();
}
@ -126,19 +159,39 @@ public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScore
@Override
protected ScoreFunction doToFunction(QueryShardContext context) {
final MappedFieldType fieldType;
if (context.getIndexSettings().isSingleType()) {
fieldType = context.getMapperService().fullName(IdFieldMapper.NAME);
} else {
fieldType = context.getMapperService().fullName(UidFieldMapper.NAME);
}
if (fieldType == null) {
// mapper could be null if we are on a shard with no docs yet, so this won't actually be used
return new RandomScoreFunction();
}
final int salt = (context.index().getName().hashCode() << 10) | context.getShardId();
final IndexFieldData<?> uidFieldData = context.getForField(fieldType);
return new RandomScoreFunction(this.seed == null ? hash(context.nowInMillis()) : seed, salt, uidFieldData);
if (seed == null) {
// DocID-based random score generation
return new RandomScoreFunction(hash(context.nowInMillis()), salt, null);
} else {
final MappedFieldType fieldType;
if (field != null) {
fieldType = context.getMapperService().fullName(field);
} else {
DEPRECATION_LOGGER.deprecated(
"As of version 7.0 Elasticsearch will require that a [field] parameter is provided when a [seed] is set");
if (context.getIndexSettings().isSingleType()) {
fieldType = context.getMapperService().fullName(IdFieldMapper.NAME);
} else {
fieldType = context.getMapperService().fullName(UidFieldMapper.NAME);
}
}
if (fieldType == null) {
if (context.getMapperService().types().isEmpty()) {
// no mappings: the index is empty anyway
return new RandomScoreFunction(hash(context.nowInMillis()), salt, null);
}
throw new IllegalArgumentException("Field [" + field + "] is not mapped on [" + context.index() +
"] and cannot be used as a source of random numbers.");
}
int seed;
if (this.seed != null) {
seed = this.seed;
} else {
seed = hash(context.nowInMillis());
}
return new RandomScoreFunction(seed, salt, context.getForField(fieldType));
}
}
private static int hash(long value) {
@ -170,6 +223,8 @@ public class RandomScoreFunctionBuilder extends ScoreFunctionBuilder<RandomScore
throw new ParsingException(parser.getTokenLocation(), "random_score seed must be an int/long or string, not '"
+ token.toString() + "'");
}
} else if ("field".equals(currentFieldName)) {
randomScoreFunctionBuilder.setField(parser.text());
} else {
throw new ParsingException(parser.getTokenLocation(), NAME + " query does not support [" + currentFieldName + "]");
}

View File

@ -75,16 +75,8 @@ public class ScoreFunctionBuilders {
return (new ScriptScoreFunctionBuilder(new Script(ScriptType.INLINE, Script.DEFAULT_SCRIPT_LANG, script, emptyMap())));
}
public static RandomScoreFunctionBuilder randomFunction(int seed) {
return (new RandomScoreFunctionBuilder()).seed(seed);
}
public static RandomScoreFunctionBuilder randomFunction(long seed) {
return (new RandomScoreFunctionBuilder()).seed(seed);
}
public static RandomScoreFunctionBuilder randomFunction(String seed) {
return (new RandomScoreFunctionBuilder()).seed(seed);
public static RandomScoreFunctionBuilder randomFunction() {
return new RandomScoreFunctionBuilder();
}
public static WeightBuilder weightFactorFunction(float weight) {

View File

@ -38,6 +38,7 @@ import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.common.xcontent.XContent;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
import org.elasticsearch.index.query.AbstractQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
@ -191,6 +192,7 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
} else {
randomScoreFunctionBuilder.seed(randomAlphaOfLengthBetween(1, 10));
}
randomScoreFunctionBuilder.setField(SeqNoFieldMapper.NAME); // guaranteed to exist
}
functionBuilder = randomScoreFunctionBuilder;
break;
@ -270,14 +272,14 @@ public class FunctionScoreQueryBuilderTests extends AbstractQueryTestCase<Functi
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder((QueryBuilder) null));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder((ScoreFunctionBuilder<?>) null));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder((FilterFunctionBuilder[]) null));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder(null, randomFunction(123)));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder(null, randomFunction()));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder(matchAllQuery(), (ScoreFunctionBuilder<?>) null));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder(matchAllQuery(), (FilterFunctionBuilder[]) null));
expectThrows(IllegalArgumentException.class, () -> new FunctionScoreQueryBuilder(null, new FilterFunctionBuilder[0]));
expectThrows(IllegalArgumentException.class,
() -> new FunctionScoreQueryBuilder(matchAllQuery(), new FilterFunctionBuilder[] { null }));
expectThrows(IllegalArgumentException.class, () -> new FilterFunctionBuilder((ScoreFunctionBuilder<?>) null));
expectThrows(IllegalArgumentException.class, () -> new FilterFunctionBuilder(null, randomFunction(123)));
expectThrows(IllegalArgumentException.class, () -> new FilterFunctionBuilder(null, randomFunction()));
expectThrows(IllegalArgumentException.class, () -> new FilterFunctionBuilder(matchAllQuery(), null));
FunctionScoreQueryBuilder builder = new FunctionScoreQueryBuilder(matchAllQuery());
expectThrows(IllegalArgumentException.class, () -> builder.scoreMode(null));

View File

@ -283,7 +283,7 @@ public class FunctionScoreTests extends ESTestCase {
public void testExplainFunctionScoreQuery() throws IOException {
Explanation functionExplanation = getFunctionScoreExplanation(searcher, RANDOM_SCORE_FUNCTION);
checkFunctionScoreExplanation(functionExplanation, "random score function (seed: 0)");
checkFunctionScoreExplanation(functionExplanation, "random score function (seed: 0, field: test)");
assertThat(functionExplanation.getDetails()[0].getDetails().length, equalTo(0));
functionExplanation = getFunctionScoreExplanation(searcher, FIELD_VALUE_FACTOR_FUNCTION);
@ -331,7 +331,7 @@ public class FunctionScoreTests extends ESTestCase {
public void testExplainFiltersFunctionScoreQuery() throws IOException {
Explanation functionExplanation = getFiltersFunctionScoreExplanation(searcher, RANDOM_SCORE_FUNCTION);
checkFiltersFunctionScoreExplanation(functionExplanation, "random score function (seed: 0)", 0);
checkFiltersFunctionScoreExplanation(functionExplanation, "random score function (seed: 0, field: test)", 0);
assertThat(functionExplanation.getDetails()[0].getDetails()[0].getDetails()[1].getDetails().length, equalTo(0));
functionExplanation = getFiltersFunctionScoreExplanation(searcher, FIELD_VALUE_FACTOR_FUNCTION);
@ -366,7 +366,7 @@ public class FunctionScoreTests extends ESTestCase {
, LIN_DECAY_FUNCTION
);
checkFiltersFunctionScoreExplanation(functionExplanation, "random score function (seed: 0)", 0);
checkFiltersFunctionScoreExplanation(functionExplanation, "random score function (seed: 0, field: test)", 0);
assertThat(functionExplanation.getDetails()[0].getDetails()[0].getDetails()[1].getDetails().length, equalTo(0));
checkFiltersFunctionScoreExplanation(functionExplanation, "field value function: ln(doc['test'].value?:1.0 * factor=1.0)", 1);

View File

@ -19,8 +19,18 @@
package org.elasticsearch.index.query.functionscore;
import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.index.IndexSettings;
import org.elasticsearch.index.mapper.MappedFieldType;
import org.elasticsearch.index.mapper.MapperService;
import org.elasticsearch.index.mapper.NumberFieldMapper;
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
import org.elasticsearch.index.query.QueryShardContext;
import org.elasticsearch.script.Script;
import org.elasticsearch.test.ESTestCase;
import org.mockito.Mockito;
public class ScoreFunctionBuilderTests extends ESTestCase {
@ -39,4 +49,23 @@ public class ScoreFunctionBuilderTests extends ESTestCase {
expectThrows(IllegalArgumentException.class, () -> new ExponentialDecayFunctionBuilder("", "", null, ""));
expectThrows(IllegalArgumentException.class, () -> new ExponentialDecayFunctionBuilder("", "", null, "", randomDouble()));
}
public void testRandomScoreFunctionWithSeed() throws Exception {
RandomScoreFunctionBuilder builder = new RandomScoreFunctionBuilder();
builder.seed(42);
QueryShardContext context = Mockito.mock(QueryShardContext.class);
Settings indexSettings = Settings.builder().put(IndexMetaData.SETTING_VERSION_CREATED, Version.CURRENT)
.put(IndexMetaData.SETTING_NUMBER_OF_SHARDS, 1).put(IndexMetaData.SETTING_NUMBER_OF_REPLICAS, 1).build();
IndexSettings settings = new IndexSettings(IndexMetaData.builder("index").settings(indexSettings).build(), Settings.EMPTY);
Mockito.when(context.index()).thenReturn(settings.getIndex());
Mockito.when(context.getShardId()).thenReturn(0);
Mockito.when(context.getIndexSettings()).thenReturn(settings);
MapperService mapperService = Mockito.mock(MapperService.class);
MappedFieldType ft = new NumberFieldMapper.NumberFieldType(NumberType.LONG);
ft.setName("foo");
Mockito.when(mapperService.fullName(Mockito.anyString())).thenReturn(ft);
Mockito.when(context.getMapperService()).thenReturn(mapperService);
builder.toFunction(context);
assertWarnings("As of version 7.0 Elasticsearch will require that a [field] parameter is provided when a [seed] is set");
}
}

View File

@ -21,6 +21,7 @@ package org.elasticsearch.search.functionscore;
import org.apache.lucene.util.ArrayUtil;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.index.fielddata.ScriptDocValues;
import org.elasticsearch.index.mapper.SeqNoFieldMapper;
import org.elasticsearch.index.query.functionscore.FunctionScoreQueryBuilder;
import org.elasticsearch.index.query.functionscore.RandomScoreFunctionBuilder;
import org.elasticsearch.plugins.Plugin;
@ -98,7 +99,7 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
ensureGreen(); // make sure we are done otherwise preference could change?
int docCount = randomIntBetween(100, 200);
for (int i = 0; i < docCount; i++) {
index("test", "type", "" + i, jsonBuilder().startObject().endObject());
index("test", "type", "" + i, jsonBuilder().startObject().field("foo", i).endObject());
}
flush();
refresh();
@ -116,7 +117,7 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
SearchResponse searchResponse = client().prepareSearch()
.setSize(docCount) // get all docs otherwise we are prone to tie-breaking
.setPreference(preference)
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(seed)))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(seed).setField("foo")))
.execute().actionGet();
assertThat("Failures " + Arrays.toString(searchResponse.getShardFailures()),
searchResponse.getShardFailures().length, CoreMatchers.equalTo(0));
@ -143,7 +144,7 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
int numDocsToChange = randomIntBetween(20, 50);
while (numDocsToChange > 0) {
int doc = randomInt(docCount-1);// watch out this is inclusive the max values!
index("test", "type", "" + doc, jsonBuilder().startObject().endObject());
index("test", "type", "" + doc, jsonBuilder().startObject().field("foo", doc).endObject());
--numDocsToChange;
}
flush();
@ -259,7 +260,7 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
int seed = 12345678;
SearchResponse resp = client().prepareSearch("test")
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(seed)))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(seed).setField(SeqNoFieldMapper.NAME)))
.setExplain(true)
.get();
assertNoFailures(resp);
@ -273,7 +274,13 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
ensureGreen();
SearchResponse resp = client().prepareSearch("test")
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(1234)))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(1234).setField(SeqNoFieldMapper.NAME)))
.get();
assertNoFailures(resp);
assertEquals(0, resp.getHits().getTotalHits());
resp = client().prepareSearch("test")
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction()))
.get();
assertNoFailures(resp);
assertEquals(0, resp.getHits().getTotalHits());
@ -292,9 +299,8 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
refresh();
int iters = scaledRandomIntBetween(10, 20);
for (int i = 0; i < iters; ++i) {
int seed = randomInt();
SearchResponse searchResponse = client().prepareSearch()
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(seed)))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction()))
.setSize(docCount)
.execute().actionGet();
@ -316,17 +322,18 @@ public class RandomScoreFunctionIT extends ESIntegTestCase {
assertNoFailures(client().prepareSearch()
.setSize(docCount) // get all docs otherwise we are prone to tie-breaking
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(randomInt())))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(randomInt()).setField(SeqNoFieldMapper.NAME)))
.execute().actionGet());
assertNoFailures(client().prepareSearch()
.setSize(docCount) // get all docs otherwise we are prone to tie-breaking
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(randomLong())))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction().seed(randomLong()).setField(SeqNoFieldMapper.NAME)))
.execute().actionGet());
assertNoFailures(client().prepareSearch()
.setSize(docCount) // get all docs otherwise we are prone to tie-breaking
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction(randomRealisticUnicodeOfLengthBetween(10, 20))))
.setQuery(functionScoreQuery(matchAllQuery(), randomFunction()
.seed(randomRealisticUnicodeOfLengthBetween(10, 20)).setField(SeqNoFieldMapper.NAME)))
.execute().actionGet());
}

View File

@ -208,12 +208,25 @@ not. The number value is of type float.
[[function-random]]
==== Random
The `random_score` generates scores using a hash of the `_uid` field,
with a `seed` for variation. If `seed` is not specified, the current
time is used.
The `random_score` generates scores that are uniformly distributed in [0, 1[.
By default, it uses the internal Lucene doc ids as a source of randomness,
which is very efficient but unfortunately not reproducible since documents might
be renumbered by merges.
NOTE: Using this feature will load field data for `_uid`, which can
be a memory intensive operation since the values are unique.
In case you want scores to be reproducible, it is possible to provide a `seed`
and `field`. The final score will then be computed based on this seed, the
minimum value of `field` for the considered document and a salt that is computed
based on the index name and shard id so that documents that have the same
value but are stored in different indexes get different scores. Note that
documents that are within the same shard and have the same value for `field`
will however get the same score, so it is usually desirable to use a field that
has unique values for all documents. A good default choice might be to use the
`_seq_no` field, whose only drawback is that scores will change if the document
is updated since update operations also update the value of the `_seq_no` field.
NOTE: It was possible to set a seed without setting a field, but this has been
deprecated as this requires loading fielddata on the `_id` field which consumes
a lot of memory.
[source,js]
--------------------------------------------------
@ -222,7 +235,8 @@ GET /_search
"query": {
"function_score": {
"random_score": {
"seed": 10
"seed": 10,
"field": "_seq_no"
}
}
}

View File

@ -80,9 +80,9 @@ public class PercolatorHighlightSubFetchPhaseTests extends ESTestCase {
boostQuery = new BoostQuery(percolateQuery, 1f);
assertThat(PercolatorHighlightSubFetchPhase.locatePercolatorQuery(boostQuery), sameInstance(percolateQuery));
FunctionScoreQuery functionScoreQuery = new FunctionScoreQuery(new MatchAllDocsQuery(), new RandomScoreFunction());
FunctionScoreQuery functionScoreQuery = new FunctionScoreQuery(new MatchAllDocsQuery(), new RandomScoreFunction(0, 0, null));
assertThat(PercolatorHighlightSubFetchPhase.locatePercolatorQuery(functionScoreQuery), nullValue());
functionScoreQuery = new FunctionScoreQuery(percolateQuery, new RandomScoreFunction());
functionScoreQuery = new FunctionScoreQuery(percolateQuery, new RandomScoreFunction(0, 0, null));
assertThat(PercolatorHighlightSubFetchPhase.locatePercolatorQuery(functionScoreQuery), sameInstance(percolateQuery));
DisjunctionMaxQuery disjunctionMaxQuery = new DisjunctionMaxQuery(Arrays.asList(new MatchAllDocsQuery()), 1f);

View File

@ -518,12 +518,12 @@ public class QueryAnalyzerTests extends ESTestCase {
public void testFunctionScoreQuery() {
TermQuery termQuery = new TermQuery(new Term("_field", "_value"));
FunctionScoreQuery functionScoreQuery = new FunctionScoreQuery(termQuery, new RandomScoreFunction());
FunctionScoreQuery functionScoreQuery = new FunctionScoreQuery(termQuery, new RandomScoreFunction(0, 0, null));
Result result = analyze(functionScoreQuery);
assertThat(result.verified, is(true));
assertTermsEqual(result.terms, new Term("_field", "_value"));
functionScoreQuery = new FunctionScoreQuery(termQuery, new RandomScoreFunction(), 1f, null, 10f);
functionScoreQuery = new FunctionScoreQuery(termQuery, new RandomScoreFunction(0, 0, null), 1f, null, 10f);
result = analyze(functionScoreQuery);
assertThat(result.verified, is(false));
assertTermsEqual(result.terms, new Term("_field", "_value"));