Skip to content

Commit 082632d

Browse files
committed
aggs: fixed bug in children agg that prevented all child docs from being evaluated
Before we only evaluated segments that yielded matches in parent aggs, which caused us to miss to evaluate child docs in segments we didn't have parent matches for. The fix for this is stop remember in what segments we have matches for and simply evaluate all segments. This makes the code simpler and we can still quickly see if a segment doesn't hold child docs like we did before.
1 parent 015ead0 commit 082632d

File tree

2 files changed

+65
-13
lines changed

2 files changed

+65
-13
lines changed

core/src/main/java/org/elasticsearch/search/aggregations/bucket/children/ParentToChildrenAggregator.java

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
package org.elasticsearch.search.aggregations.bucket.children;
2020

21+
import org.apache.lucene.index.IndexReader;
2122
import org.apache.lucene.index.LeafReaderContext;
2223
import org.apache.lucene.index.SortedDocValues;
2324
import org.apache.lucene.search.*;
@@ -64,9 +65,6 @@ public class ParentToChildrenAggregator extends SingleBucketAggregator {
6465
private final LongObjectPagedHashMap<long[]> parentOrdToOtherBuckets;
6566
private boolean multipleBucketsPerParentOrd = false;
6667

67-
// This needs to be a Set to avoid duplicate reader context entries via (#setNextReader(...), it can get invoked multiple times with the same reader context)
68-
private Set<LeafReaderContext> replay = new LinkedHashSet<>();
69-
7068
public ParentToChildrenAggregator(String name, AggregatorFactories factories, AggregationContext aggregationContext,
7169
Aggregator parent, String parentType, Query childFilter, Query parentFilter,
7270
ValuesSource.Bytes.WithOrdinals.ParentChild valuesSource,
@@ -99,17 +97,11 @@ public LeafBucketCollector getLeafCollector(LeafReaderContext ctx,
9997
if (valuesSource == null) {
10098
return LeafBucketCollector.NO_OP_COLLECTOR;
10199
}
102-
if (replay == null) {
103-
throw new IllegalStateException();
104-
}
105100

106101
final SortedDocValues globalOrdinals = valuesSource.globalOrdinalsValues(parentType, ctx);
107102
assert globalOrdinals != null;
108103
Scorer parentScorer = parentFilter.scorer(ctx);
109104
final Bits parentDocs = Lucene.asSequentialAccessBits(ctx.reader().maxDoc(), parentScorer);
110-
if (childFilter.scorer(ctx) != null) {
111-
replay.add(ctx);
112-
}
113105
return new LeafBucketCollector() {
114106

115107
@Override
@@ -138,10 +130,8 @@ public void collect(int docId, long bucket) throws IOException {
138130

139131
@Override
140132
protected void doPostCollection() throws IOException {
141-
final Set<LeafReaderContext> replay = this.replay;
142-
this.replay = null;
143-
144-
for (LeafReaderContext ctx : replay) {
133+
IndexReader indexReader = context().searchContext().searcher().getIndexReader();
134+
for (LeafReaderContext ctx : indexReader.leaves()) {
145135
DocIdSetIterator childDocsIter = childFilter.scorer(ctx);
146136
if (childDocsIter == null) {
147137
continue;

core/src/test/java/org/elasticsearch/search/aggregations/bucket/ChildrenIT.java

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,15 @@
1818
*/
1919
package org.elasticsearch.search.aggregations.bucket;
2020

21+
import org.elasticsearch.action.index.IndexRequest;
2122
import org.elasticsearch.action.index.IndexRequestBuilder;
2223
import org.elasticsearch.action.search.SearchResponse;
2324
import org.elasticsearch.action.update.UpdateResponse;
2425
import org.elasticsearch.cluster.metadata.IndexMetaData;
2526
import org.elasticsearch.common.settings.Settings;
2627
import org.elasticsearch.search.SearchHit;
28+
import org.elasticsearch.search.aggregations.AggregationBuilder;
29+
import org.elasticsearch.search.aggregations.AggregationBuilders;
2730
import org.elasticsearch.search.aggregations.bucket.children.Children;
2831
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
2932
import org.elasticsearch.search.aggregations.metrics.sum.Sum;
@@ -392,6 +395,65 @@ public void testHierarchicalChildrenAggs() {
392395
assertThat(terms.getBuckets().get(0).getDocCount(), equalTo(1l));
393396
}
394397

398+
public void testPostCollectAllLeafReaders() throws Exception {
399+
// The 'towns' and 'parent_names' aggs operate on parent docs and if child docs are in different segments we need
400+
// to ensure those segments which child docs are also evaluated to in the post collect phase.
401+
402+
// Before we only evaluated segments that yielded matches in 'towns' and 'parent_names' aggs, which caused
403+
// us to miss to evaluate child docs in segments we didn't have parent matches for.
404+
405+
assertAcked(
406+
prepareCreate("index")
407+
.addMapping("parentType", "name", "type=string,index=not_analyzed", "town", "type=string,index=not_analyzed")
408+
.addMapping("childType", "_parent", "type=parentType", "name", "type=string,index=not_analyzed", "age", "type=integer")
409+
);
410+
List<IndexRequestBuilder> requests = new ArrayList<>();
411+
requests.add(client().prepareIndex("index", "parentType", "1").setSource("name", "Bob", "town", "Memphis"));
412+
requests.add(client().prepareIndex("index", "parentType", "2").setSource("name", "Alice", "town", "Chicago"));
413+
requests.add(client().prepareIndex("index", "parentType", "3").setSource("name", "Bill", "town", "Chicago"));
414+
requests.add(client().prepareIndex("index", "childType", "1").setSource("name", "Jill", "age", 5).setParent("1"));
415+
requests.add(client().prepareIndex("index", "childType", "2").setSource("name", "Joey", "age", 3).setParent("1"));
416+
requests.add(client().prepareIndex("index", "childType", "3").setSource("name", "John", "age", 2).setParent("2"));
417+
requests.add(client().prepareIndex("index", "childType", "4").setSource("name", "Betty", "age", 6).setParent("3"));
418+
requests.add(client().prepareIndex("index", "childType", "5").setSource("name", "Dan", "age", 1).setParent("3"));
419+
indexRandom(true, requests);
420+
421+
SearchResponse response = client().prepareSearch("index")
422+
.setSize(0)
423+
.addAggregation(AggregationBuilders.terms("towns").field("town")
424+
.subAggregation(AggregationBuilders.terms("parent_names").field("name")
425+
.subAggregation(AggregationBuilders.children("child_docs").childType("childType"))
426+
)
427+
)
428+
.get();
429+
430+
Terms towns = response.getAggregations().get("towns");
431+
assertThat(towns.getBuckets().size(), equalTo(2));
432+
assertThat(towns.getBuckets().get(0).getKeyAsString(), equalTo("Chicago"));
433+
assertThat(towns.getBuckets().get(0).getDocCount(), equalTo(2L));
434+
435+
Terms parents = towns.getBuckets().get(0).getAggregations().get("parent_names");
436+
assertThat(parents.getBuckets().size(), equalTo(2));
437+
assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Alice"));
438+
assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L));
439+
Children children = parents.getBuckets().get(0).getAggregations().get("child_docs");
440+
assertThat(children.getDocCount(), equalTo(1L));
441+
442+
assertThat(parents.getBuckets().get(1).getKeyAsString(), equalTo("Bill"));
443+
assertThat(parents.getBuckets().get(1).getDocCount(), equalTo(1L));
444+
children = parents.getBuckets().get(1).getAggregations().get("child_docs");
445+
assertThat(children.getDocCount(), equalTo(2L));
446+
447+
assertThat(towns.getBuckets().get(1).getKeyAsString(), equalTo("Memphis"));
448+
assertThat(towns.getBuckets().get(1).getDocCount(), equalTo(1L));
449+
parents = towns.getBuckets().get(1).getAggregations().get("parent_names");
450+
assertThat(parents.getBuckets().size(), equalTo(1));
451+
assertThat(parents.getBuckets().get(0).getKeyAsString(), equalTo("Bob"));
452+
assertThat(parents.getBuckets().get(0).getDocCount(), equalTo(1L));
453+
children = parents.getBuckets().get(0).getAggregations().get("child_docs");
454+
assertThat(children.getDocCount(), equalTo(2L));
455+
}
456+
395457
private static final class Control {
396458

397459
final String category;

0 commit comments

Comments
 (0)