Skip to content

Commit

Permalink
Fix multi level nested sort (#32204)
Browse files Browse the repository at this point in the history
The parent filter for nested sort should always match **all** parents regardless
of the child queries. It is used to find the boundaries of a single parent and we use
the child query to match all the filters set in the nested tree so there is no need to
repeat the nested filters.
With this change we ensure that we build bitset filters
only to find the root docs (or the docs at the level where the sort applies) that can be reused
among queries.

Closes #31554
Closes #32130
Closes #31783

Co-authored-by: Dominic Bevacqua <bev@treatwell.com>
  • Loading branch information
jimczi and dbevacqua committed Jul 20, 2018
1 parent 74aa7b0 commit 6ed1ad0
Show file tree
Hide file tree
Showing 3 changed files with 544 additions and 21 deletions.
39 changes: 18 additions & 21 deletions server/src/main/java/org/elasticsearch/search/sort/SortBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,7 @@
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.join.ScoreMode;
import org.apache.lucene.search.join.ToChildBlockJoinQuery;
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
import org.elasticsearch.common.ParseField;
import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.Strings;
Expand Down Expand Up @@ -186,10 +184,21 @@ protected static Nested resolveNested(QueryShardContext context, String nestedPa
}

protected static Nested resolveNested(QueryShardContext context, NestedSortBuilder nestedSort) throws IOException {
return resolveNested(context, nestedSort, null);
final Query childQuery = resolveNestedQuery(context, nestedSort, null);
if (childQuery == null) {
return null;
}
final ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
final Query parentQuery;
if (objectMapper == null) {
parentQuery = Queries.newNonNestedFilter(context.indexVersionCreated());
} else {
parentQuery = objectMapper.nestedTypeFilter();
}
return new Nested(context.bitsetFilter(parentQuery), childQuery);
}

private static Nested resolveNested(QueryShardContext context, NestedSortBuilder nestedSort, Nested nested) throws IOException {
private static Query resolveNestedQuery(QueryShardContext context, NestedSortBuilder nestedSort, Query parentQuery) throws IOException {
if (nestedSort == null || nestedSort.getPath() == null) {
return null;
}
Expand All @@ -207,23 +216,15 @@ private static Nested resolveNested(QueryShardContext context, NestedSortBuilder
if (!nestedObjectMapper.nested().isNested()) {
throw new QueryShardException(context, "[nested] nested object under path [" + nestedPath + "] is not of nested type");
}

// get our parent query which will determines our parent documents
Query parentQuery;
ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
if (objectMapper == null) {
parentQuery = Queries.newNonNestedFilter(context.indexVersionCreated());
} else {
parentQuery = objectMapper.nestedTypeFilter();
}

// get our child query, potentially applying a users filter
Query childQuery;
try {
context.nestedScope().nextLevel(nestedObjectMapper);
if (nestedFilter != null) {
assert nestedFilter == Rewriteable.rewrite(nestedFilter, context) : "nested filter is not rewritten";
if (nested == null) {
if (parentQuery == null) {
// this is for back-compat, original single level nested sorting never applied a nested type filter
childQuery = nestedFilter.toFilter(context);
} else {
Expand All @@ -237,27 +238,23 @@ private static Nested resolveNested(QueryShardContext context, NestedSortBuilder
}

// apply filters from the previous nested level
if (nested != null) {
parentQuery = Queries.filtered(parentQuery,
new ToParentBlockJoinQuery(nested.getInnerQuery(), nested.getRootFilter(), ScoreMode.None));

if (parentQuery != null) {
if (objectMapper != null) {
childQuery = Queries.filtered(childQuery,
new ToChildBlockJoinQuery(nested.getInnerQuery(), context.bitsetFilter(objectMapper.nestedTypeFilter())));
new ToChildBlockJoinQuery(parentQuery, context.bitsetFilter(objectMapper.nestedTypeFilter())));
}
}

// wrap up our parent and child and either process the next level of nesting or return
final Nested innerNested = new Nested(context.bitsetFilter(parentQuery), childQuery);
if (nestedNestedSort != null) {
try {
context.nestedScope().nextLevel(nestedObjectMapper);
return resolveNested(context, nestedNestedSort, innerNested);
return resolveNestedQuery(context, nestedNestedSort, childQuery);
} finally {
context.nestedScope().previousLevel();
}
} else {
return innerNested;
return childQuery;
}
}

Expand Down
Loading

0 comments on commit 6ed1ad0

Please sign in to comment.