Skip to content

Disable query cache for FunctionScoreQuery and ScriptScoreQuery #74060

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 16, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,8 @@ public Explanation explain(LeafReaderContext context, int doc) throws IOExceptio

@Override
public boolean isCacheable(LeafReaderContext ctx) {
// If minScore is not null, then matches depend on statistics of the
// top-level reader.
return minScore == null;
// the sub-query/filters should be cached independently when the score is not needed.
return false;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,8 @@ private ScoreScript makeScoreScript(LeafReaderContext context) throws IOExceptio

@Override
public boolean isCacheable(LeafReaderContext ctx) {
// If minScore is not null, then matches depend on statistics of the top-level reader.
return minScore == null;
// the sub-query should be cached independently when the score is not needed
return false;
}
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,15 @@

package org.elasticsearch.index.query;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.Weight;
import org.apache.lucene.store.Directory;
import org.elasticsearch.ElasticsearchException;
import org.elasticsearch.common.lucene.search.function.ScriptScoreQuery;
import org.elasticsearch.index.query.functionscore.ScriptScoreQueryBuilder;
Expand All @@ -23,6 +30,7 @@

import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
import static org.hamcrest.CoreMatchers.instanceOf;
import static org.hamcrest.Matchers.greaterThan;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

Expand Down Expand Up @@ -88,14 +96,33 @@ public void testIllegalArguments() {
*/
@Override
public void testCacheability() throws IOException {
Directory directory = newDirectory();
RandomIndexWriter iw = new RandomIndexWriter(random(), directory);
iw.addDocument(new Document());
final IndexSearcher searcher = new IndexSearcher(iw.getReader());
iw.close();
assertThat(searcher.getIndexReader().leaves().size(), greaterThan(0));

Script script = new Script(ScriptType.INLINE, MockScriptEngine.NAME, "1", Collections.emptyMap());
ScriptScoreQueryBuilder queryBuilder = new ScriptScoreQueryBuilder(
new TermQueryBuilder(KEYWORD_FIELD_NAME, "value"), script);

SearchExecutionContext context = createSearchExecutionContext();
SearchExecutionContext context = createSearchExecutionContext(searcher);
QueryBuilder rewriteQuery = rewriteQuery(queryBuilder, new SearchExecutionContext(context));
assertNotNull(rewriteQuery.toQuery(context));
Query luceneQuery = rewriteQuery.toQuery(context);
assertNotNull(luceneQuery);
assertTrue("query should be cacheable: " + queryBuilder.toString(), context.isCacheable());

// test query cache
if (rewriteQuery instanceof MatchNoneQueryBuilder == false) {
Weight queryWeight = context.searcher().createWeight(searcher.rewrite(luceneQuery), ScoreMode.COMPLETE, 1.0f);
for (LeafReaderContext ctx : context.getIndexReader().leaves()) {
assertFalse("" + searcher.rewrite(luceneQuery) + " " + rewriteQuery.toString(), queryWeight.isCacheable(ctx));
}
}

searcher.getIndexReader().close();
directory.close();
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,18 @@

import com.fasterxml.jackson.core.JsonParseException;

import org.apache.lucene.document.Document;
import org.apache.lucene.index.LeafReaderContext;
import org.apache.lucene.index.RandomIndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.MatchNoDocsQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreMode;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.Weight;
import org.apache.lucene.store.Directory;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
import org.elasticsearch.common.bytes.BytesReference;
Expand Down Expand Up @@ -70,6 +77,7 @@
import static org.hamcrest.Matchers.closeTo;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.nullValue;

Expand Down Expand Up @@ -815,34 +823,72 @@ public List<ScoreFunctionSpec<?>> getScoreFunctions() {
*/
@Override
public void testCacheability() throws IOException {
Directory directory = newDirectory();
RandomIndexWriter iw = new RandomIndexWriter(random(), directory);
iw.addDocument(new Document());
final IndexSearcher searcher = new IndexSearcher(iw.getReader());
iw.close();
assertThat(searcher.getIndexReader().leaves().size(), greaterThan(0));

FunctionScoreQueryBuilder queryBuilder = createTestQueryBuilder();
boolean isCacheable = isCacheable(queryBuilder);
SearchExecutionContext context = createSearchExecutionContext();
boolean requestCache = isCacheable(queryBuilder);
SearchExecutionContext context = createSearchExecutionContext(searcher);
QueryBuilder rewriteQuery = rewriteQuery(queryBuilder, new SearchExecutionContext(context));
assertNotNull(rewriteQuery.toQuery(context));
// we occasionally need to update the expected "isCacheable" flag after rewrite for MatchNoneQueryBuilder
// we occasionally need to update the expected request cache flag after rewrite to MatchNoneQueryBuilder
if (rewriteQuery instanceof MatchNoneQueryBuilder) {
isCacheable = true;
requestCache = true;
}
assertEquals("query should " + (requestCache ? "" : "not") + " be eligible for the request cache: " + queryBuilder.toString(),
requestCache, context.isCacheable());

// test query cache
if (rewriteQuery instanceof MatchNoneQueryBuilder == false) {
Query luceneQuery = rewriteQuery.toQuery(context);
Weight queryWeight = context.searcher().createWeight(searcher.rewrite(luceneQuery), ScoreMode.COMPLETE, 1.0f);
for (LeafReaderContext ctx : context.getIndexReader().leaves()) {
assertFalse(queryWeight.isCacheable(ctx));
}
}
assertEquals("query should " + (isCacheable ? "" : "not") + " be cacheable: " + queryBuilder.toString(), isCacheable,
context.isCacheable());

ScoreFunctionBuilder<?> scriptScoreFunction = new ScriptScoreFunctionBuilder(
new Script(ScriptType.INLINE, MockScriptEngine.NAME, "1", Collections.emptyMap()));
queryBuilder = new FunctionScoreQueryBuilder(new FilterFunctionBuilder[] {
new FilterFunctionBuilder(RandomQueryBuilder.createQuery(random()), scriptScoreFunction) });
context = createSearchExecutionContext();
context = createSearchExecutionContext(searcher);
rewriteQuery = rewriteQuery(queryBuilder, new SearchExecutionContext(context));
assertNotNull(rewriteQuery.toQuery(context));
assertTrue("function script query should be cacheable" + queryBuilder.toString(), context.isCacheable());
assertTrue("function script query should be eligible for the request cache: " + queryBuilder.toString(),
context.isCacheable());

// test query cache
if (rewriteQuery instanceof MatchNoneQueryBuilder == false) {
Query luceneQuery = rewriteQuery.toQuery(context);
Weight queryWeight = context.searcher().createWeight(searcher.rewrite(luceneQuery), ScoreMode.COMPLETE, 1.0f);
for (LeafReaderContext ctx : context.getIndexReader().leaves()) {
assertFalse(queryWeight.isCacheable(ctx));
}
}

RandomScoreFunctionBuilder randomScoreFunctionBuilder = new RandomScoreFunctionBuilderWithFixedSeed();
queryBuilder = new FunctionScoreQueryBuilder(new FilterFunctionBuilder[] {
new FilterFunctionBuilder(RandomQueryBuilder.createQuery(random()), randomScoreFunctionBuilder) });
context = createSearchExecutionContext();
context = createSearchExecutionContext(searcher);
rewriteQuery = rewriteQuery(queryBuilder, new SearchExecutionContext(context));
assertNotNull(rewriteQuery.toQuery(context));
assertFalse("function random query should not be cacheable: " + queryBuilder.toString(), context.isCacheable());
assertFalse("function random query should not be eligible for the request cache: " + queryBuilder.toString(),
context.isCacheable());

// test query cache
if (rewriteQuery instanceof MatchNoneQueryBuilder == false) {
Query luceneQuery = rewriteQuery.toQuery(context);
Weight queryWeight = context.searcher().createWeight(searcher.rewrite(luceneQuery), ScoreMode.COMPLETE, 1.0f);
for (LeafReaderContext ctx : context.getIndexReader().leaves()) {
assertFalse(queryWeight.isCacheable(ctx));
}
}
searcher.getIndexReader().close();
directory.close();
}

private boolean isCacheable(FunctionScoreQueryBuilder queryBuilder) {
Expand Down