inner_hits: Ensure that that InnerHitBuilder uses rewritten queries

If a nested, has_child or has_parent query's inner query gets rewritten then the InnerHitBuilder should use that rewritten form too, otherwise this can cause exceptions in a later phase.

Also fixes a bug that HasChildQueryBuilder's rewrite method overwrites max_children with min_children value.

Closes #19353
This commit is contained in:
Martijn van Groningen 2016-07-11 12:30:53 +02:00
parent fed6b72460
commit 075cb970c0
7 changed files with 129 additions and 63 deletions

View File

@ -472,9 +472,10 @@ public class HasChildQueryBuilder extends AbstractQueryBuilder<HasChildQueryBuil
@Override @Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException { protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext); QueryBuilder rewrittenQuery = query.rewrite(queryRewriteContext);
if (rewrite != query) { if (rewrittenQuery != query) {
return new HasChildQueryBuilder(type, rewrite, minChildren, minChildren, scoreMode, innerHitBuilder); InnerHitBuilder rewrittenInnerHit = InnerHitBuilder.rewrite(innerHitBuilder, rewrittenQuery);
return new HasChildQueryBuilder(type, rewrittenQuery, minChildren, maxChildren, scoreMode, rewrittenInnerHit);
} }
return this; return this;
} }

View File

@ -309,9 +309,10 @@ public class HasParentQueryBuilder extends AbstractQueryBuilder<HasParentQueryBu
@Override @Override
protected QueryBuilder doRewrite(QueryRewriteContext queryShardContext) throws IOException { protected QueryBuilder doRewrite(QueryRewriteContext queryShardContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryShardContext); QueryBuilder rewrittenQuery = query.rewrite(queryShardContext);
if (rewrite != query) { if (rewrittenQuery != query) {
return new HasParentQueryBuilder(type, rewrite, score, innerHit); InnerHitBuilder rewrittenInnerHit = InnerHitBuilder.rewrite(innerHit, rewrittenQuery);
return new HasParentQueryBuilder(type, rewrittenQuery, score, rewrittenInnerHit);
} }
return this; return this;
} }

View File

@ -722,4 +722,16 @@ public final class InnerHitBuilder extends ToXContentToBytes implements Writeabl
} }
} }
static InnerHitBuilder rewrite(InnerHitBuilder original, QueryBuilder rewrittenQuery) {
if (original == null) {
return null;
}
InnerHitBuilder copy = new InnerHitBuilder(original);
copy.query = rewrittenQuery;
copy.parentChildType = original.parentChildType;
copy.nestedPath = original.nestedPath;
return copy;
}
} }

View File

@ -263,9 +263,10 @@ public class NestedQueryBuilder extends AbstractQueryBuilder<NestedQueryBuilder>
@Override @Override
protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException { protected QueryBuilder doRewrite(QueryRewriteContext queryRewriteContext) throws IOException {
QueryBuilder rewrite = query.rewrite(queryRewriteContext); QueryBuilder rewrittenQuery = query.rewrite(queryRewriteContext);
if (rewrite != query) { if (rewrittenQuery != query) {
return new NestedQueryBuilder(path, rewrite, scoreMode, innerHitBuilder); InnerHitBuilder rewrittenInnerHit = InnerHitBuilder.rewrite(innerHitBuilder, rewrittenQuery);
return new NestedQueryBuilder(path, rewrittenQuery, scoreMode, rewrittenInnerHit);
} }
return this; return this;
} }

View File

@ -52,8 +52,6 @@ import org.elasticsearch.search.internal.SearchContext;
import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.AbstractQueryTestCase; import org.elasticsearch.test.AbstractQueryTestCase;
import org.junit.Before;
import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import java.util.Collections; import java.util.Collections;
@ -74,6 +72,8 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
private static String similarity; private static String similarity;
boolean requiresRewrite = false;
@Override @Override
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
similarity = randomFrom("classic", "BM25"); similarity = randomFrom("classic", "BM25");
@ -105,8 +105,14 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
protected HasChildQueryBuilder doCreateTestQueryBuilder() { protected HasChildQueryBuilder doCreateTestQueryBuilder() {
int min = randomIntBetween(0, Integer.MAX_VALUE / 2); int min = randomIntBetween(0, Integer.MAX_VALUE / 2);
int max = randomIntBetween(min, Integer.MAX_VALUE); int max = randomIntBetween(min, Integer.MAX_VALUE);
HasChildQueryBuilder hqb = new HasChildQueryBuilder(CHILD_TYPE,
RandomQueryBuilder.createQuery(random()), QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random());
if (randomBoolean()) {
requiresRewrite = true;
innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString());
}
HasChildQueryBuilder hqb = new HasChildQueryBuilder(CHILD_TYPE, innerQueryBuilder,
RandomPicks.randomFrom(random(), ScoreMode.values())); RandomPicks.randomFrom(random(), ScoreMode.values()));
hqb.minMaxChildren(min, max); hqb.minMaxChildren(min, max);
if (randomBoolean()) { if (randomBoolean()) {
@ -127,25 +133,24 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
assertEquals(queryBuilder.maxChildren(), lpq.getMaxChildren()); assertEquals(queryBuilder.maxChildren(), lpq.getMaxChildren());
assertEquals(queryBuilder.scoreMode(), lpq.getScoreMode()); // WTF is this why do we have two? assertEquals(queryBuilder.scoreMode(), lpq.getScoreMode()); // WTF is this why do we have two?
if (queryBuilder.innerHit() != null) { if (queryBuilder.innerHit() != null) {
// have to rewrite again because the provided queryBuilder hasn't been rewritten (directly returned from
// doCreateTestQueryBuilder)
queryBuilder = (HasChildQueryBuilder) queryBuilder.rewrite(context);
SearchContext searchContext = SearchContext.current(); SearchContext searchContext = SearchContext.current();
assertNotNull(searchContext); assertNotNull(searchContext);
if (query != null) { Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>(); InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); for (InnerHitBuilder builder : innerHitBuilders.values()) {
for (InnerHitBuilder builder : innerHitBuilders.values()) { builder.build(searchContext, searchContext.innerHits());
builder.build(searchContext, searchContext.innerHits());
}
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits =
searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), STRING_FIELD_NAME_2);
} else {
assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0));
} }
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits =
searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), STRING_FIELD_NAME_2);
} }
} }
@ -315,6 +320,17 @@ public class HasChildQueryBuilderTests extends AbstractQueryTestCase<HasChildQue
} }
} }
@Override
public void testMustRewrite() throws IOException {
try {
super.testMustRewrite();
} catch (UnsupportedOperationException e) {
if (requiresRewrite == false) {
throw e;
}
}
}
public void testNonDefaultSimilarity() throws Exception { public void testNonDefaultSimilarity() throws Exception {
QueryShardContext shardContext = createShardContext(); QueryShardContext shardContext = createShardContext();
HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None); HasChildQueryBuilder hasChildQueryBuilder = QueryBuilders.hasChildQuery(CHILD_TYPE, new TermQueryBuilder("custom_string", "value"), ScoreMode.None);

View File

@ -58,6 +58,8 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
protected static final String PARENT_TYPE = "parent"; protected static final String PARENT_TYPE = "parent";
protected static final String CHILD_TYPE = "child"; protected static final String CHILD_TYPE = "child";
boolean requiresRewrite = false;
@Override @Override
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE, mapperService.merge(PARENT_TYPE, new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef(PARENT_TYPE,
@ -88,8 +90,12 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
*/ */
@Override @Override
protected HasParentQueryBuilder doCreateTestQueryBuilder() { protected HasParentQueryBuilder doCreateTestQueryBuilder() {
HasParentQueryBuilder hqb = new HasParentQueryBuilder(PARENT_TYPE, QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random());
RandomQueryBuilder.createQuery(random()),randomBoolean()); if (randomBoolean()) {
requiresRewrite = true;
innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString());
}
HasParentQueryBuilder hqb = new HasParentQueryBuilder(PARENT_TYPE, innerQueryBuilder, randomBoolean());
if (randomBoolean()) { if (randomBoolean()) {
hqb.innerHit(new InnerHitBuilder() hqb.innerHit(new InnerHitBuilder()
.setName(randomAsciiOfLengthBetween(1, 10)) .setName(randomAsciiOfLengthBetween(1, 10))
@ -107,25 +113,25 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode()); assertEquals(queryBuilder.score() ? ScoreMode.Max : ScoreMode.None, lpq.getScoreMode());
if (queryBuilder.innerHit() != null) { if (queryBuilder.innerHit() != null) {
// have to rewrite again because the provided queryBuilder hasn't been rewritten (directly returned from
// doCreateTestQueryBuilder)
queryBuilder = (HasParentQueryBuilder) queryBuilder.rewrite(context);
SearchContext searchContext = SearchContext.current(); SearchContext searchContext = SearchContext.current();
assertNotNull(searchContext); assertNotNull(searchContext);
if (query != null) { Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>(); InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); for (InnerHitBuilder builder : innerHitBuilders.values()) {
for (InnerHitBuilder builder : innerHitBuilders.values()) { builder.build(searchContext, searchContext.innerHits());
builder.build(searchContext, searchContext.innerHits());
}
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits()
.getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), STRING_FIELD_NAME_2);
} else {
assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0));
} }
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits()
.getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), STRING_FIELD_NAME_2);
} }
} }
@ -206,6 +212,17 @@ public class HasParentQueryBuilderTests extends AbstractQueryTestCase<HasParentQ
} }
} }
@Override
public void testMustRewrite() throws IOException {
try {
super.testMustRewrite();
} catch (UnsupportedOperationException e) {
if (requiresRewrite == false) {
throw e;
}
}
}
public void testFromJson() throws IOException { public void testFromJson() throws IOException {
String json = String json =
"{\n" + "{\n" +

View File

@ -49,6 +49,8 @@ import static org.hamcrest.CoreMatchers.notNullValue;
public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> { public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBuilder> {
boolean requiresRewrite = false;
@Override @Override
protected void initializeAdditionalMappings(MapperService mapperService) throws IOException { protected void initializeAdditionalMappings(MapperService mapperService) throws IOException {
mapperService.merge("nested_doc", new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef("nested_doc", mapperService.merge("nested_doc", new CompressedXContent(PutMappingRequest.buildFromSimplifiedDef("nested_doc",
@ -68,7 +70,12 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
*/ */
@Override @Override
protected NestedQueryBuilder doCreateTestQueryBuilder() { protected NestedQueryBuilder doCreateTestQueryBuilder() {
NestedQueryBuilder nqb = new NestedQueryBuilder("nested1", RandomQueryBuilder.createQuery(random()), QueryBuilder innerQueryBuilder = RandomQueryBuilder.createQuery(random());
if (randomBoolean()) {
requiresRewrite = true;
innerQueryBuilder = new WrapperQueryBuilder(innerQueryBuilder.toString());
}
NestedQueryBuilder nqb = new NestedQueryBuilder("nested1", innerQueryBuilder,
RandomPicks.randomFrom(random(), ScoreMode.values())); RandomPicks.randomFrom(random(), ScoreMode.values()));
if (randomBoolean()) { if (randomBoolean()) {
nqb.innerHit(new InnerHitBuilder() nqb.innerHit(new InnerHitBuilder()
@ -87,24 +94,24 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
ToParentBlockJoinQuery parentBlockJoinQuery = (ToParentBlockJoinQuery) query; ToParentBlockJoinQuery parentBlockJoinQuery = (ToParentBlockJoinQuery) query;
// TODO how to assert this? // TODO how to assert this?
if (queryBuilder.innerHit() != null) { if (queryBuilder.innerHit() != null) {
// have to rewrite again because the provided queryBuilder hasn't been rewritten (directly returned from
// doCreateTestQueryBuilder)
queryBuilder = (NestedQueryBuilder) queryBuilder.rewrite(context);
SearchContext searchContext = SearchContext.current(); SearchContext searchContext = SearchContext.current();
assertNotNull(searchContext); assertNotNull(searchContext);
if (query != null) { Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>();
Map<String, InnerHitBuilder> innerHitBuilders = new HashMap<>(); InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders);
InnerHitBuilder.extractInnerHits(queryBuilder, innerHitBuilders); for (InnerHitBuilder builder : innerHitBuilders.values()) {
for (InnerHitBuilder builder : innerHitBuilders.values()) { builder.build(searchContext, searchContext.innerHits());
builder.build(searchContext, searchContext.innerHits());
}
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), INT_FIELD_NAME);
} else {
assertThat(searchContext.innerHits().getInnerHits().size(), equalTo(0));
} }
assertNotNull(searchContext.innerHits());
assertEquals(1, searchContext.innerHits().getInnerHits().size());
assertTrue(searchContext.innerHits().getInnerHits().containsKey(queryBuilder.innerHit().getName()));
InnerHitsContext.BaseInnerHits innerHits = searchContext.innerHits().getInnerHits().get(queryBuilder.innerHit().getName());
assertEquals(innerHits.size(), queryBuilder.innerHit().getSize());
assertEquals(innerHits.sort().sort.getSort().length, 1);
assertEquals(innerHits.sort().sort.getSort()[0].getField(), INT_FIELD_NAME);
} }
} }
@ -199,6 +206,17 @@ public class NestedQueryBuilderTests extends AbstractQueryTestCase<NestedQueryBu
} }
} }
@Override
public void testMustRewrite() throws IOException {
try {
super.testMustRewrite();
} catch (UnsupportedOperationException e) {
if (requiresRewrite == false) {
throw e;
}
}
}
public void testIgnoreUnmapped() throws IOException { public void testIgnoreUnmapped() throws IOException {
final NestedQueryBuilder queryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None); final NestedQueryBuilder queryBuilder = new NestedQueryBuilder("unmapped", new MatchAllQueryBuilder(), ScoreMode.None);
queryBuilder.ignoreUnmapped(true); queryBuilder.ignoreUnmapped(true);