Fix nested query highlighting (#26305)

This commit extracts the inner query in the ESToParentBlockJoinQuery for highlighting.
This query has been added in 5.4 and breaks plain highlighting on nested queries.
Highlighters that use postings or term vectors are not affected because they can't highlight nested documents correctly.

Fixes #26230
This commit is contained in:
Jim Ferenczi 2017-08-22 11:36:45 +02:00 committed by GitHub
parent 3d8feff66e
commit 4756c9a884
4 changed files with 88 additions and 1 deletions

View File

@ -38,6 +38,7 @@ import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.lucene.all.AllTermQuery;
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.search.ESToParentBlockJoinQuery;
import java.io.IOException;
import java.text.BreakIterator;
@ -210,6 +211,8 @@ public class CustomUnifiedHighlighter extends UnifiedHighlighter {
return Collections.singletonList(new TermQuery(atq.getTerm()));
} else if (query instanceof FunctionScoreQuery) {
return Collections.singletonList(((FunctionScoreQuery) query).getSubQuery());
} else if (query instanceof ESToParentBlockJoinQuery) {
return Collections.singletonList(((ESToParentBlockJoinQuery) query).getChildQuery());
} else {
return null;
}

View File

@ -30,6 +30,7 @@ import org.apache.lucene.search.PhraseQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SynonymQuery;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
import org.apache.lucene.search.spans.SpanTermQuery;
import org.elasticsearch.common.lucene.search.MultiPhrasePrefixQuery;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
@ -90,6 +91,11 @@ public class CustomFieldQuery extends FieldQuery {
for (Term term : synQuery.getTerms()) {
flatten(new TermQuery(term), reader, flatQueries, boost);
}
} else if (sourceQuery instanceof ESToParentBlockJoinQuery) {
Query childQuery = ((ESToParentBlockJoinQuery) sourceQuery).getChildQuery();
if (childQuery != null) {
flatten(childQuery, reader, flatQueries, boost);
}
} else {
super.flatten(sourceQuery, reader, flatQueries, boost);
}

View File

@ -25,6 +25,7 @@ import org.apache.lucene.search.highlight.QueryScorer;
import org.apache.lucene.search.highlight.WeightedSpanTerm;
import org.apache.lucene.search.highlight.WeightedSpanTermExtractor;
import org.elasticsearch.common.lucene.search.function.FunctionScoreQuery;
import org.elasticsearch.index.search.ESToParentBlockJoinQuery;
import java.io.IOException;
import java.util.Map;
@ -86,6 +87,8 @@ public final class CustomQueryScorer extends QueryScorer {
return;
} else if (query instanceof FunctionScoreQuery) {
super.extract(((FunctionScoreQuery) query).getSubQuery(), boost, terms);
} else if (query instanceof ESToParentBlockJoinQuery) {
super.extract(((ESToParentBlockJoinQuery) query).getChildQuery(), boost, terms);
} else {
super.extract(query, boost, terms);
}

View File

@ -20,7 +20,6 @@ package org.elasticsearch.search.fetch.subphase.highlight;
import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.Version;
import org.elasticsearch.action.index.IndexRequestBuilder;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
@ -2841,4 +2840,80 @@ public class HighlighterSearchIT extends ESIntegTestCase {
equalTo("<x>hello</x> world"));
}
}
public void testWithNestedQuery() throws Exception {
String mapping = jsonBuilder().startObject().startObject("type").startObject("properties")
.startObject("text")
.field("type", "text")
.field("index_options", "offsets")
.field("term_vector", "with_positions_offsets")
.endObject()
.startObject("foo")
.field("type", "nested")
.startObject("properties")
.startObject("text")
.field("type", "text")
.endObject()
.endObject()
.endObject()
.endObject().endObject().endObject().string();
prepareCreate("test").addMapping("type", mapping, XContentType.JSON).get();
client().prepareIndex("test", "type", "1").setSource(jsonBuilder().startObject()
.startArray("foo")
.startObject().field("text", "brown").endObject()
.startObject().field("text", "cow").endObject()
.endArray()
.field("text", "brown")
.endObject()).setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE)
.get();
for (String type : new String[] {"unified", "plain"}) {
SearchResponse searchResponse = client().prepareSearch()
.setQuery(nestedQuery("foo", matchQuery("foo.text", "brown cow"), ScoreMode.None))
.highlighter(new HighlightBuilder()
.field(new Field("foo.text").highlighterType(type)))
.get();
assertHitCount(searchResponse, 1);
HighlightField field = searchResponse.getHits().getAt(0).getHighlightFields().get("foo.text");
assertThat(field.getFragments().length, equalTo(2));
assertThat(field.getFragments()[0].string(), equalTo("<em>brown</em>"));
assertThat(field.getFragments()[1].string(), equalTo("<em>cow</em>"));
searchResponse = client().prepareSearch()
.setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None))
.highlighter(new HighlightBuilder()
.field(new Field("foo.text").highlighterType(type)))
.get();
assertHitCount(searchResponse, 1);
field = searchResponse.getHits().getAt(0).getHighlightFields().get("foo.text");
assertThat(field.getFragments().length, equalTo(1));
assertThat(field.getFragments()[0].string(), equalTo("<em>brown</em>"));
searchResponse = client().prepareSearch()
.setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None))
.highlighter(new HighlightBuilder()
.field(new Field("foo.text").highlighterType("plain")))
.get();
assertHitCount(searchResponse, 1);
field = searchResponse.getHits().getAt(0).getHighlightFields().get("foo.text");
assertThat(field.getFragments().length, equalTo(1));
assertThat(field.getFragments()[0].string(), equalTo("<em>brown</em>"));
}
// For unified and fvh highlighters we just check that the nested query is correctly extracted
// but we highlight the root text field since nested documents cannot be highlighted with postings nor term vectors
// directly.
for (String type : ALL_TYPES) {
SearchResponse searchResponse = client().prepareSearch()
.setQuery(nestedQuery("foo", prefixQuery("foo.text", "bro"), ScoreMode.None))
.highlighter(new HighlightBuilder()
.field(new Field("text").highlighterType(type).requireFieldMatch(false)))
.get();
assertHitCount(searchResponse, 1);
HighlightField field = searchResponse.getHits().getAt(0).getHighlightFields().get("text");
assertThat(field.getFragments().length, equalTo(1));
assertThat(field.getFragments()[0].string(), equalTo("<em>brown</em>"));
}
}
}