Made sure that wrapped child query / parent query gets rewritten only once.
This commit is contained in:
parent
9e89323ad2
commit
19295280d9
|
@ -55,8 +55,9 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
private final String childType;
|
||||
private final Filter parentFilter;
|
||||
private final ScoreType scoreType;
|
||||
private final Query childQuery;
|
||||
private final Query originalChildQuery;
|
||||
|
||||
private Query rewrittenChildQuery;
|
||||
private TObjectFloatHashMap<HashedBytesArray> uidToScore;
|
||||
private TObjectIntHashMap<HashedBytesArray> uidToCount;
|
||||
|
||||
|
@ -65,7 +66,7 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
this.parentType = parentType;
|
||||
this.childType = childType;
|
||||
this.parentFilter = parentFilter;
|
||||
this.childQuery = childQuery;
|
||||
this.originalChildQuery = childQuery;
|
||||
this.scoreType = scoreType;
|
||||
}
|
||||
|
||||
|
@ -75,7 +76,8 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
this.childType = unProcessedQuery.childType;
|
||||
this.parentFilter = unProcessedQuery.parentFilter;
|
||||
this.scoreType = unProcessedQuery.scoreType;
|
||||
this.childQuery = rewrittenChildQuery;
|
||||
this.originalChildQuery = unProcessedQuery.originalChildQuery;
|
||||
this.rewrittenChildQuery = rewrittenChildQuery;
|
||||
|
||||
this.uidToScore = unProcessedQuery.uidToScore;
|
||||
this.uidToCount = unProcessedQuery.uidToCount;
|
||||
|
@ -84,27 +86,33 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
@Override
|
||||
public String toString(String field) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ChildrenQuery[").append(childType).append("/").append(parentType).append("](").append(childQuery
|
||||
sb.append("ChildrenQuery[").append(childType).append("/").append(parentType).append("](").append(originalChildQuery
|
||||
.toString(field)).append(')').append(ToStringUtils.boost(getBoost()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query rewrittenChildQuery = childQuery.rewrite(reader);
|
||||
if (rewrittenChildQuery == childQuery) {
|
||||
Query rewritten;
|
||||
if (rewrittenChildQuery == null) {
|
||||
rewritten = originalChildQuery.rewrite(reader);
|
||||
} else {
|
||||
rewritten = rewrittenChildQuery;
|
||||
}
|
||||
if (rewritten == rewrittenChildQuery) {
|
||||
return this;
|
||||
}
|
||||
|
||||
// See TopChildrenQuery#rewrite
|
||||
int index = searchContext.rewrites().indexOf(this);
|
||||
ChildrenQuery rewrite = new ChildrenQuery(this, rewrittenChildQuery);
|
||||
ChildrenQuery rewrite = new ChildrenQuery(this, rewritten);
|
||||
searchContext.rewrites().set(index, rewrite);
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extractTerms(Set<Term> terms) {
|
||||
childQuery.extractTerms(terms);
|
||||
rewrittenChildQuery.extractTerms(terms);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -121,6 +129,12 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
default:
|
||||
collector = new ChildUidCollector(scoreType, searchContext, parentType, uidToScore);
|
||||
}
|
||||
Query childQuery;
|
||||
if (rewrittenChildQuery == null) {
|
||||
childQuery = rewrittenChildQuery = searchContext.searcher().rewrite(originalChildQuery);
|
||||
} else {
|
||||
childQuery = rewrittenChildQuery;
|
||||
}
|
||||
searchContext.searcher().search(childQuery, collector);
|
||||
}
|
||||
|
||||
|
@ -142,7 +156,7 @@ public class ChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
throw new ElasticSearchIllegalStateException("has_child query hasn't executed properly");
|
||||
}
|
||||
|
||||
return new ParentWeight(childQuery.createWeight(searcher));
|
||||
return new ParentWeight(rewrittenChildQuery.createWeight(searcher));
|
||||
}
|
||||
|
||||
class ParentWeight extends Weight {
|
||||
|
|
|
@ -46,16 +46,17 @@ import java.util.Set;
|
|||
public class ParentQuery extends Query implements SearchContext.Rewrite {
|
||||
|
||||
private final SearchContext searchContext;
|
||||
private final Query parentQuery;
|
||||
private final Query originalParentQuery;
|
||||
private final String parentType;
|
||||
private final Filter childrenFilter;
|
||||
private final List<String> childTypes;
|
||||
|
||||
private Query rewrittenParentQuery;
|
||||
private TObjectFloatHashMap<HashedBytesArray> uidToScore;
|
||||
|
||||
public ParentQuery(SearchContext searchContext, Query parentQuery, String parentType, List<String> childTypes, Filter childrenFilter) {
|
||||
this.searchContext = searchContext;
|
||||
this.parentQuery = parentQuery;
|
||||
this.originalParentQuery = parentQuery;
|
||||
this.parentType = parentType;
|
||||
this.childTypes = childTypes;
|
||||
this.childrenFilter = childrenFilter;
|
||||
|
@ -63,11 +64,12 @@ public class ParentQuery extends Query implements SearchContext.Rewrite {
|
|||
|
||||
private ParentQuery(ParentQuery unwritten, Query rewrittenParentQuery) {
|
||||
this.searchContext = unwritten.searchContext;
|
||||
this.parentQuery = rewrittenParentQuery;
|
||||
this.originalParentQuery = unwritten.originalParentQuery;
|
||||
this.parentType = unwritten.parentType;
|
||||
this.childrenFilter = unwritten.childrenFilter;
|
||||
this.childTypes = unwritten.childTypes;
|
||||
|
||||
this.rewrittenParentQuery = rewrittenParentQuery;
|
||||
this.uidToScore = unwritten.uidToScore;
|
||||
}
|
||||
|
||||
|
@ -76,6 +78,12 @@ public class ParentQuery extends Query implements SearchContext.Rewrite {
|
|||
searchContext.idCache().refresh(searchContext.searcher().getTopReaderContext().leaves());
|
||||
uidToScore = CacheRecycler.popObjectFloatMap();
|
||||
ParentUidCollector collector = new ParentUidCollector(uidToScore, searchContext, parentType);
|
||||
Query parentQuery;
|
||||
if (rewrittenParentQuery == null) {
|
||||
parentQuery = rewrittenParentQuery = searchContext.searcher().rewrite(originalParentQuery);
|
||||
} else {
|
||||
parentQuery = rewrittenParentQuery;
|
||||
}
|
||||
searchContext.searcher().search(parentQuery, collector);
|
||||
}
|
||||
|
||||
|
@ -91,18 +99,25 @@ public class ParentQuery extends Query implements SearchContext.Rewrite {
|
|||
public String toString(String field) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("ParentQuery[").append(parentType).append("/").append(childTypes)
|
||||
.append("](").append(parentQuery.toString(field)).append(')')
|
||||
.append("](").append(originalParentQuery.toString(field)).append(')')
|
||||
.append(ToStringUtils.boost(getBoost()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query rewrittenChildQuery = parentQuery.rewrite(reader);
|
||||
if (rewrittenChildQuery == parentQuery) {
|
||||
Query rewritten;
|
||||
if (rewrittenParentQuery == null) {
|
||||
rewritten = originalParentQuery.rewrite(reader);
|
||||
} else {
|
||||
rewritten = rewrittenParentQuery;
|
||||
}
|
||||
if (rewritten == rewrittenParentQuery) {
|
||||
return this;
|
||||
}
|
||||
ParentQuery rewrite = new ParentQuery(this, rewrittenChildQuery);
|
||||
|
||||
// See TopChildrenQuery#rewrite
|
||||
ParentQuery rewrite = new ParentQuery(this, rewritten);
|
||||
int index = searchContext.rewrites().indexOf(this);
|
||||
searchContext.rewrites().set(index, rewrite);
|
||||
return rewrite;
|
||||
|
@ -110,7 +125,7 @@ public class ParentQuery extends Query implements SearchContext.Rewrite {
|
|||
|
||||
@Override
|
||||
public void extractTerms(Set<Term> terms) {
|
||||
parentQuery.extractTerms(terms);
|
||||
rewrittenParentQuery.extractTerms(terms);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -118,7 +133,7 @@ public class ParentQuery extends Query implements SearchContext.Rewrite {
|
|||
if (uidToScore == null) {
|
||||
throw new ElasticSearchIllegalStateException("has_parent query hasn't executed properly");
|
||||
}
|
||||
return new ChildWeight(parentQuery.createWeight(searcher));
|
||||
return new ChildWeight(rewrittenParentQuery.createWeight(searcher));
|
||||
}
|
||||
|
||||
static class ParentUidCollector extends NoopCollector {
|
||||
|
|
|
@ -54,19 +54,20 @@ import java.util.Set;
|
|||
public class TopChildrenQuery extends Query implements SearchContext.Rewrite {
|
||||
|
||||
private final SearchContext searchContext;
|
||||
private final Query childQuery;
|
||||
private final String parentType;
|
||||
private final String childType;
|
||||
private final ScoreType scoreType;
|
||||
private final int factor;
|
||||
private final int incrementalFactor;
|
||||
private final Query originalChildQuery;
|
||||
|
||||
private Query rewrittenChildQuery;
|
||||
private ExtTHashMap<Object, ParentDoc[]> parentDocs;
|
||||
|
||||
// Note, the query is expected to already be filtered to only child type docs
|
||||
public TopChildrenQuery(SearchContext searchContext, Query childQuery, String childType, String parentType, ScoreType scoreType, int factor, int incrementalFactor) {
|
||||
this.searchContext = searchContext;
|
||||
this.childQuery = childQuery;
|
||||
this.originalChildQuery = childQuery;
|
||||
this.childType = childType;
|
||||
this.parentType = parentType;
|
||||
this.scoreType = scoreType;
|
||||
|
@ -76,13 +77,44 @@ public class TopChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
|
||||
private TopChildrenQuery(TopChildrenQuery existing, Query rewrittenChildQuery) {
|
||||
this.searchContext = existing.searchContext;
|
||||
this.childQuery = rewrittenChildQuery;
|
||||
this.originalChildQuery = existing.originalChildQuery;
|
||||
this.parentType = existing.parentType;
|
||||
this.childType = existing.childType;
|
||||
this.scoreType = existing.scoreType;
|
||||
this.factor = existing.factor;
|
||||
this.incrementalFactor = existing.incrementalFactor;
|
||||
this.parentDocs = existing.parentDocs;
|
||||
this.rewrittenChildQuery = rewrittenChildQuery;
|
||||
}
|
||||
|
||||
|
||||
// Rewrite logic:
|
||||
// 1) query_then_fetch (default): First contextRewrite and then rewrite is executed
|
||||
// 2) dfs_query_then_fetch:: First rewrite and then contextRewrite is executed. During query phase rewrite isn't
|
||||
// executed any more because searchContext#queryRewritten() returns true.
|
||||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query rewritten;
|
||||
if (rewrittenChildQuery == null) {
|
||||
rewritten = originalChildQuery.rewrite(reader);
|
||||
} else {
|
||||
rewritten = rewrittenChildQuery;
|
||||
}
|
||||
if (rewritten == rewrittenChildQuery) {
|
||||
return this;
|
||||
}
|
||||
// We need to update the rewritten query also in the SearchContext#rewrites b/c we can run into this situation:
|
||||
// 1) During parsing we set SearchContext#rewrites with queries that implement Rewrite.
|
||||
// 2) Then during the dfs phase, the main query (which included this query and its child query) gets rewritten
|
||||
// and updated in SearchContext. So different TopChildrenQuery instances are in SearchContext#rewrites and in the main query.
|
||||
// 3) Then during the query phase first the queries that impl. Rewrite are executed, which will update their own data
|
||||
// parentDocs Map. Then when the main query is executed, 0 results are found, b/c the main query holds a different
|
||||
// TopChildrenQuery instance then in SearchContext#rewrites
|
||||
int index = searchContext.rewrites().indexOf(this);
|
||||
TopChildrenQuery rewrite = new TopChildrenQuery(this, rewritten);
|
||||
searchContext.rewrites().set(index, rewrite);
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -96,6 +128,13 @@ public class TopChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
numChildDocs = 1;
|
||||
}
|
||||
numChildDocs *= factor;
|
||||
|
||||
Query childQuery;
|
||||
if (rewrittenChildQuery == null) {
|
||||
childQuery = rewrittenChildQuery = searchContext.searcher().rewrite(originalChildQuery);
|
||||
} else {
|
||||
childQuery = rewrittenChildQuery;
|
||||
}
|
||||
while (true) {
|
||||
parentDocs.clear();
|
||||
TopDocs topChildDocs = searchContext.searcher().search(childQuery, numChildDocs);
|
||||
|
@ -199,21 +238,9 @@ public class TopChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
public float sumScores = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Query rewrite(IndexReader reader) throws IOException {
|
||||
Query rewrittenChildQuery = childQuery.rewrite(reader);
|
||||
if (rewrittenChildQuery == childQuery) {
|
||||
return this;
|
||||
}
|
||||
int index = searchContext.rewrites().indexOf(this);
|
||||
TopChildrenQuery rewrite = new TopChildrenQuery(this, rewrittenChildQuery);
|
||||
searchContext.rewrites().set(index, rewrite);
|
||||
return rewrite;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void extractTerms(Set<Term> terms) {
|
||||
childQuery.extractTerms(terms);
|
||||
rewrittenChildQuery.extractTerms(terms);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -222,12 +249,12 @@ public class TopChildrenQuery extends Query implements SearchContext.Rewrite {
|
|||
throw new ElasticSearchIllegalStateException("top_children query hasn't executed properly");
|
||||
}
|
||||
|
||||
return new ParentWeight(searcher, childQuery.createWeight(searcher));
|
||||
return new ParentWeight(searcher, rewrittenChildQuery.createWeight(searcher));
|
||||
}
|
||||
|
||||
public String toString(String field) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("score_child[").append(childType).append("/").append(parentType).append("](").append(childQuery.toString(field)).append(')');
|
||||
sb.append("score_child[").append(childType).append("/").append(parentType).append("](").append(originalChildQuery.toString(field)).append(')');
|
||||
sb.append(ToStringUtils.boost(getBoost()));
|
||||
return sb.toString();
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import org.elasticsearch.common.settings.ImmutableSettings;
|
|||
import org.elasticsearch.index.query.QueryBuilders;
|
||||
import org.elasticsearch.search.facet.terms.TermsFacet;
|
||||
import org.elasticsearch.search.sort.SortBuilders;
|
||||
import org.elasticsearch.search.sort.SortOrder;
|
||||
import org.elasticsearch.test.integration.AbstractNodesTests;
|
||||
import org.testng.annotations.AfterClass;
|
||||
import org.testng.annotations.BeforeClass;
|
||||
|
@ -1185,4 +1186,80 @@ public class SimpleChildQuerySearchTests extends AbstractNodesTests {
|
|||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleQueryRewrite() throws Exception {
|
||||
client.admin().indices().prepareDelete().execute().actionGet();
|
||||
|
||||
client.admin().indices().prepareCreate("test").setSettings(
|
||||
ImmutableSettings.settingsBuilder()
|
||||
.put("index.number_of_shards", 2)
|
||||
.put("index.number_of_replicas", 0)
|
||||
).execute().actionGet();
|
||||
client.admin().cluster().prepareHealth().setWaitForGreenStatus().execute().actionGet();
|
||||
client.admin().indices().preparePutMapping("test").setType("child").setSource(jsonBuilder().startObject().startObject("type")
|
||||
.startObject("_parent").field("type", "parent").endObject()
|
||||
.endObject().endObject()).execute().actionGet();
|
||||
|
||||
// index simple data
|
||||
int childId = 0;
|
||||
for (int i = 0; i < 10; i++) {
|
||||
String parentId = String.format("p%03d", i);
|
||||
client.prepareIndex("test", "parent", parentId)
|
||||
.setSource("p_field", parentId)
|
||||
.execute().actionGet();
|
||||
int j = childId;
|
||||
for (; j < childId + 50; j++) {
|
||||
String childUid = String.format("c%03d", j);
|
||||
client.prepareIndex("test", "child", childUid)
|
||||
.setSource("c_field", childUid)
|
||||
.setParent(parentId)
|
||||
.execute().actionGet();
|
||||
}
|
||||
childId = j;
|
||||
}
|
||||
client.admin().indices().prepareRefresh().execute().actionGet();
|
||||
|
||||
SearchType[] searchTypes = new SearchType[]{SearchType.QUERY_THEN_FETCH, SearchType.DFS_QUERY_THEN_FETCH};
|
||||
for (SearchType searchType : searchTypes) {
|
||||
SearchResponse searchResponse = client.prepareSearch("test").setSearchType(searchType)
|
||||
.setQuery(hasChildQuery("child", prefixQuery("c_field", "c")).scoreType("max"))
|
||||
.addSort("p_field", SortOrder.ASC)
|
||||
.setSize(5)
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(10L));
|
||||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("p000"));
|
||||
assertThat(searchResponse.hits().hits()[1].id(), equalTo("p001"));
|
||||
assertThat(searchResponse.hits().hits()[2].id(), equalTo("p002"));
|
||||
assertThat(searchResponse.hits().hits()[3].id(), equalTo("p003"));
|
||||
assertThat(searchResponse.hits().hits()[4].id(), equalTo("p004"));
|
||||
|
||||
searchResponse = client.prepareSearch("test").setSearchType(searchType)
|
||||
.setQuery(hasParentQuery("parent", prefixQuery("p_field", "p")).scoreType("score"))
|
||||
.addSort("c_field", SortOrder.ASC)
|
||||
.setSize(5)
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(500L));
|
||||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("c000"));
|
||||
assertThat(searchResponse.hits().hits()[1].id(), equalTo("c001"));
|
||||
assertThat(searchResponse.hits().hits()[2].id(), equalTo("c002"));
|
||||
assertThat(searchResponse.hits().hits()[3].id(), equalTo("c003"));
|
||||
assertThat(searchResponse.hits().hits()[4].id(), equalTo("c004"));
|
||||
|
||||
searchResponse = client.prepareSearch("test").setSearchType(searchType)
|
||||
.setQuery(topChildrenQuery("child", prefixQuery("c_field", "c")))
|
||||
.addSort("p_field", SortOrder.ASC)
|
||||
.setSize(5)
|
||||
.execute().actionGet();
|
||||
assertThat("Failures " + Arrays.toString(searchResponse.shardFailures()), searchResponse.shardFailures().length, equalTo(0));
|
||||
assertThat(searchResponse.hits().totalHits(), equalTo(10L));
|
||||
assertThat(searchResponse.hits().hits()[0].id(), equalTo("p000"));
|
||||
assertThat(searchResponse.hits().hits()[1].id(), equalTo("p001"));
|
||||
assertThat(searchResponse.hits().hits()[2].id(), equalTo("p002"));
|
||||
assertThat(searchResponse.hits().hits()[3].id(), equalTo("p003"));
|
||||
assertThat(searchResponse.hits().hits()[4].id(), equalTo("p004"));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue