-
Notifications
You must be signed in to change notification settings - Fork 24.3k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
A ConjunctionScorer can add the approximation of the TwoPhaseIterator of a MinScoreScorer to its TwoPhaseIterator list after the main TwoPhaseIterator. This can lead to an undesired state, as the matches() method is called after the score() method. For example, if the matches() method of ToParentBlockJoinQuery is called after the score() method, then we return a wrong result or over-read DocValues. Here, we wrap the approximation to prevent it from unwrapping as a TwoPhaseIterator. Closes #79658
- Loading branch information
Showing
4 changed files
with
252 additions
and
21 deletions.
There are no files selected for viewing
121 changes: 121 additions & 0 deletions
121
...er/src/internalClusterTest/java/org/elasticsearch/search/nested/NestedWithMinScoreIT.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License | ||
* 2.0 and the Server Side Public License, v 1; you may not use this file except | ||
* in compliance with, at your election, the Elastic License 2.0 or the Server | ||
* Side Public License, v 1. | ||
*/ | ||
|
||
package org.elasticsearch.search.nested; | ||
|
||
import org.apache.lucene.search.join.ScoreMode; | ||
import org.elasticsearch.action.search.SearchRequest; | ||
import org.elasticsearch.action.search.SearchResponse; | ||
import org.elasticsearch.action.support.WriteRequest; | ||
import org.elasticsearch.index.query.BoolQueryBuilder; | ||
import org.elasticsearch.index.query.ConstantScoreQueryBuilder; | ||
import org.elasticsearch.index.query.MatchPhraseQueryBuilder; | ||
import org.elasticsearch.index.query.NestedQueryBuilder; | ||
import org.elasticsearch.index.query.RangeQueryBuilder; | ||
import org.elasticsearch.index.query.functionscore.ScriptScoreQueryBuilder; | ||
import org.elasticsearch.plugins.Plugin; | ||
import org.elasticsearch.script.MockScriptPlugin; | ||
import org.elasticsearch.script.Script; | ||
import org.elasticsearch.script.ScriptType; | ||
import org.elasticsearch.search.builder.SearchSourceBuilder; | ||
import org.elasticsearch.test.ESIntegTestCase; | ||
import org.elasticsearch.test.hamcrest.ElasticsearchAssertions; | ||
import org.elasticsearch.xcontent.XContentBuilder; | ||
import org.elasticsearch.xcontent.XContentFactory; | ||
|
||
import java.util.ArrayList; | ||
import java.util.Collection; | ||
import java.util.Collections; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.function.Function; | ||
|
||
public class NestedWithMinScoreIT extends ESIntegTestCase { | ||
|
||
public static class ScriptTestPlugin extends MockScriptPlugin { | ||
@Override | ||
protected Map<String, Function<Map<String, Object>, Object>> pluginScripts() { | ||
return Collections.singletonMap("score_script", params -> { | ||
final Object scoreAccessor = params.get("_score"); | ||
if (scoreAccessor instanceof Number) { | ||
return ((Number) scoreAccessor).doubleValue(); | ||
} else { | ||
return null; | ||
} | ||
}); | ||
} | ||
} | ||
|
||
@Override | ||
protected Collection<Class<? extends Plugin>> getMockPlugins() { | ||
final List<Class<? extends Plugin>> plugins = new ArrayList<>(super.getMockPlugins()); | ||
plugins.add(ScriptTestPlugin.class); | ||
return plugins; | ||
} | ||
|
||
public void testNestedWithMinScore() throws Exception { | ||
XContentBuilder mapping = XContentFactory.jsonBuilder(); | ||
mapping.startObject(); | ||
mapping.startObject("properties"); | ||
{ | ||
mapping.startObject("toolTracks"); | ||
{ | ||
mapping.field("type", "nested"); | ||
mapping.startObject("properties"); | ||
{ | ||
mapping.startObject("data"); | ||
mapping.field("type", "text"); | ||
mapping.endObject(); | ||
|
||
mapping.startObject("confidence"); | ||
mapping.field("type", "double"); | ||
mapping.endObject(); | ||
} | ||
mapping.endObject(); | ||
} | ||
mapping.endObject(); | ||
} | ||
mapping.endObject(); | ||
mapping.endObject(); | ||
|
||
client().admin().indices().prepareCreate("test").addMapping("_doc", mapping).get(); | ||
|
||
XContentBuilder doc = XContentFactory.jsonBuilder(); | ||
doc.startObject(); | ||
doc.startArray("toolTracks"); | ||
double[] confidence = new double[] { 0.3, 0.92, 0.7, 0.85, 0.2, 0.3, 0.75, 0.82, 0.1, 0.6, 0.3, 0.7 }; | ||
for (double v : confidence) { | ||
doc.startObject(); | ||
doc.field("confidence", v); | ||
doc.field("data", "cash dispenser, automated teller machine, automatic teller machine"); | ||
doc.endObject(); | ||
} | ||
doc.endArray(); | ||
doc.endObject(); | ||
|
||
client().prepareIndex("test", "_doc").setId("d1").setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE).setSource(doc).get(); | ||
final BoolQueryBuilder childQuery = new BoolQueryBuilder().filter( | ||
new MatchPhraseQueryBuilder("toolTracks.data", "cash dispenser, automated teller machine, automatic teller machine") | ||
).filter(new RangeQueryBuilder("toolTracks.confidence").from(0.8)); | ||
|
||
final ScriptScoreQueryBuilder scriptScoreQuery = new ScriptScoreQueryBuilder( | ||
new NestedQueryBuilder("toolTracks", new ConstantScoreQueryBuilder(childQuery), ScoreMode.Total), | ||
new Script(ScriptType.INLINE, MockScriptPlugin.NAME, "score_script", Collections.emptyMap()) | ||
); | ||
scriptScoreQuery.setMinScore(1.0f); | ||
SearchSourceBuilder source = new SearchSourceBuilder(); | ||
source.query(scriptScoreQuery); | ||
source.profile(randomBoolean()); | ||
if (randomBoolean()) { | ||
source.trackTotalHitsUpTo(randomBoolean() ? Integer.MAX_VALUE : randomIntBetween(1, 1000)); | ||
} | ||
SearchRequest searchRequest = new SearchRequest("test").source(source); | ||
final SearchResponse searchResponse = client().search(searchRequest).actionGet(); | ||
ElasticsearchAssertions.assertSearchHits(searchResponse, "d1"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58138ac
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dnhatn Is this included in the released 7.17.0? I can't see it in the release notes.
58138ac
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@marcreichman Yes, it is. It seems the release notes is outdated. I will fix it. Thanks again for reporting the issue.