inner hits: Fix bug that resolves parent docs properly as inner hit when inner hit is defined on has_parent query.
This commit is contained in:
parent
d038f372d4
commit
3ce05b6919
|
@ -29,7 +29,9 @@ import org.apache.lucene.search.join.BitDocIdSetFilter;
|
||||||
import org.apache.lucene.util.BitSet;
|
import org.apache.lucene.util.BitSet;
|
||||||
import org.apache.lucene.util.Bits;
|
import org.apache.lucene.util.Bits;
|
||||||
import org.elasticsearch.ExceptionsHelper;
|
import org.elasticsearch.ExceptionsHelper;
|
||||||
|
import org.elasticsearch.common.lucene.Lucene;
|
||||||
import org.elasticsearch.common.lucene.search.AndFilter;
|
import org.elasticsearch.common.lucene.search.AndFilter;
|
||||||
|
import org.elasticsearch.index.fieldvisitor.SingleFieldsVisitor;
|
||||||
import org.elasticsearch.index.mapper.DocumentMapper;
|
import org.elasticsearch.index.mapper.DocumentMapper;
|
||||||
import org.elasticsearch.index.mapper.Uid;
|
import org.elasticsearch.index.mapper.Uid;
|
||||||
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
import org.elasticsearch.index.mapper.internal.ParentFieldMapper;
|
||||||
|
@ -37,6 +39,7 @@ import org.elasticsearch.index.mapper.internal.UidFieldMapper;
|
||||||
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
import org.elasticsearch.index.mapper.object.ObjectMapper;
|
||||||
import org.elasticsearch.index.query.ParsedQuery;
|
import org.elasticsearch.index.query.ParsedQuery;
|
||||||
import org.elasticsearch.index.search.nested.NonNestedDocsFilter;
|
import org.elasticsearch.index.search.nested.NonNestedDocsFilter;
|
||||||
|
import org.elasticsearch.search.SearchHitField;
|
||||||
import org.elasticsearch.search.fetch.FetchSubPhase;
|
import org.elasticsearch.search.fetch.FetchSubPhase;
|
||||||
import org.elasticsearch.search.internal.FilteredSearchContext;
|
import org.elasticsearch.search.internal.FilteredSearchContext;
|
||||||
import org.elasticsearch.search.internal.SearchContext;
|
import org.elasticsearch.search.internal.SearchContext;
|
||||||
|
@ -249,16 +252,27 @@ public final class InnerHitsContext {
|
||||||
topDocsCollector = TopScoreDocCollector.create(topN);
|
topDocsCollector = TopScoreDocCollector.create(topN);
|
||||||
}
|
}
|
||||||
|
|
||||||
String field;
|
final String term;
|
||||||
ParentFieldMapper hitParentFieldMapper = documentMapper.parentFieldMapper();
|
final String field;
|
||||||
if (hitParentFieldMapper.active()) {
|
if (documentMapper.parentFieldMapper().active()) {
|
||||||
// Hit has a active _parent field and it is a child doc, so we want a parent doc as inner hits.
|
// Active _parent field has been selected, so we want a children doc as inner hits.
|
||||||
field = ParentFieldMapper.NAME;
|
field = ParentFieldMapper.NAME;
|
||||||
|
term = Uid.createUid(hitContext.hit().type(), hitContext.hit().id());
|
||||||
} else {
|
} else {
|
||||||
// Hit has no active _parent field and it is a parent doc, so we want children docs as inner hits.
|
// No active _parent field has been selected, so we want parent docs as inner hits.
|
||||||
field = UidFieldMapper.NAME;
|
field = UidFieldMapper.NAME;
|
||||||
|
SearchHitField parentField = hitContext.hit().field(ParentFieldMapper.NAME);
|
||||||
|
if (parentField != null) {
|
||||||
|
term = parentField.getValue();
|
||||||
|
} else {
|
||||||
|
SingleFieldsVisitor fieldsVisitor = new SingleFieldsVisitor(ParentFieldMapper.NAME);
|
||||||
|
hitContext.reader().document(hitContext.docId(), fieldsVisitor);
|
||||||
|
if (fieldsVisitor.fields().isEmpty()) {
|
||||||
|
return Lucene.EMPTY_TOP_DOCS;
|
||||||
|
}
|
||||||
|
term = (String) fieldsVisitor.fields().get(ParentFieldMapper.NAME).get(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
String term = Uid.createUid(hitContext.hit().type(), hitContext.hit().id());
|
|
||||||
Filter filter = new TermFilter(new Term(field, term)); // Only include docs that have the current hit as parent
|
Filter filter = new TermFilter(new Term(field, term)); // Only include docs that have the current hit as parent
|
||||||
Filter typeFilter = documentMapper.typeFilter(); // Only include docs that have this inner hits type.
|
Filter typeFilter = documentMapper.typeFilter(); // Only include docs that have this inner hits type.
|
||||||
context.searcher().search(
|
context.searcher().search(
|
||||||
|
|
|
@ -444,6 +444,45 @@ public class InnerHitsTests extends ElasticsearchIntegrationTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testInnerHitsOnHasParent() throws Exception {
|
||||||
|
assertAcked(prepareCreate("stack")
|
||||||
|
.addMapping("question", "body", "type=string")
|
||||||
|
.addMapping("answer", "_parent", "type=question", "body", "type=string")
|
||||||
|
);
|
||||||
|
List<IndexRequestBuilder> requests = new ArrayList<>();
|
||||||
|
requests.add(client().prepareIndex("stack", "question", "1").setSource("body", "I'm using HTTPS + Basic authentication to protect a resource. How can I throttle authentication attempts to protect against brute force attacks?"));
|
||||||
|
requests.add(client().prepareIndex("stack", "answer", "1").setParent("1").setSource("body", "install fail2ban and enable rules for apache"));
|
||||||
|
requests.add(client().prepareIndex("stack", "question", "2").setSource("body", "I have firewall rules set up and also denyhosts installed.\\ndo I also need to install fail2ban?"));
|
||||||
|
requests.add(client().prepareIndex("stack", "answer", "2").setParent("2").setSource("body", "Denyhosts protects only ssh; Fail2Ban protects all daemons."));
|
||||||
|
indexRandom(true, requests);
|
||||||
|
|
||||||
|
SearchResponse response = client().prepareSearch("stack")
|
||||||
|
.setTypes("answer")
|
||||||
|
.addSort("_uid", SortOrder.ASC)
|
||||||
|
.setQuery(
|
||||||
|
boolQuery()
|
||||||
|
.must(matchQuery("body", "fail2ban"))
|
||||||
|
.must(hasParentQuery("question", matchAllQuery()).innerHit(new QueryInnerHitBuilder()))
|
||||||
|
).get();
|
||||||
|
assertNoFailures(response);
|
||||||
|
assertHitCount(response, 2);
|
||||||
|
|
||||||
|
SearchHit searchHit = response.getHits().getAt(0);
|
||||||
|
assertThat(searchHit.getId(), equalTo("1"));
|
||||||
|
assertThat(searchHit.getType(), equalTo("answer"));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1l));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question"));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getAt(0).id(), equalTo("1"));
|
||||||
|
|
||||||
|
searchHit = response.getHits().getAt(1);
|
||||||
|
assertThat(searchHit.getId(), equalTo("2"));
|
||||||
|
assertThat(searchHit.getType(), equalTo("answer"));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getTotalHits(), equalTo(1l));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getAt(0).getType(), equalTo("question"));
|
||||||
|
assertThat(searchHit.getInnerHits().get("question").getAt(0).id(), equalTo("2"));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testParentChildMultipleLayers() throws Exception {
|
public void testParentChildMultipleLayers() throws Exception {
|
||||||
assertAcked(prepareCreate("articles")
|
assertAcked(prepareCreate("articles")
|
||||||
|
|
Loading…
Reference in New Issue