Skip to content

Commit 9bf6112

Browse files
jimczidbevacqua
andcommitted
Fix multi level nested sort (#32204)
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 <[email protected]>
1 parent 73c4c7f commit 9bf6112

File tree

3 files changed

+544
-21
lines changed

3 files changed

+544
-21
lines changed

server/src/main/java/org/elasticsearch/search/sort/SortBuilder.java

+18-21
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,7 @@
2222
import org.apache.lucene.search.Query;
2323
import org.apache.lucene.search.Sort;
2424
import org.apache.lucene.search.SortField;
25-
import org.apache.lucene.search.join.ScoreMode;
2625
import org.apache.lucene.search.join.ToChildBlockJoinQuery;
27-
import org.apache.lucene.search.join.ToParentBlockJoinQuery;
2826
import org.elasticsearch.common.ParseField;
2927
import org.elasticsearch.common.ParsingException;
3028
import org.elasticsearch.common.Strings;
@@ -186,10 +184,21 @@ protected static Nested resolveNested(QueryShardContext context, String nestedPa
186184
}
187185

188186
protected static Nested resolveNested(QueryShardContext context, NestedSortBuilder nestedSort) throws IOException {
189-
return resolveNested(context, nestedSort, null);
187+
final Query childQuery = resolveNestedQuery(context, nestedSort, null);
188+
if (childQuery == null) {
189+
return null;
190+
}
191+
final ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
192+
final Query parentQuery;
193+
if (objectMapper == null) {
194+
parentQuery = Queries.newNonNestedFilter(context.indexVersionCreated());
195+
} else {
196+
parentQuery = objectMapper.nestedTypeFilter();
197+
}
198+
return new Nested(context.bitsetFilter(parentQuery), childQuery);
190199
}
191200

192-
private static Nested resolveNested(QueryShardContext context, NestedSortBuilder nestedSort, Nested nested) throws IOException {
201+
private static Query resolveNestedQuery(QueryShardContext context, NestedSortBuilder nestedSort, Query parentQuery) throws IOException {
193202
if (nestedSort == null || nestedSort.getPath() == null) {
194203
return null;
195204
}
@@ -207,23 +216,15 @@ private static Nested resolveNested(QueryShardContext context, NestedSortBuilder
207216
if (!nestedObjectMapper.nested().isNested()) {
208217
throw new QueryShardException(context, "[nested] nested object under path [" + nestedPath + "] is not of nested type");
209218
}
210-
211-
// get our parent query which will determines our parent documents
212-
Query parentQuery;
213219
ObjectMapper objectMapper = context.nestedScope().getObjectMapper();
214-
if (objectMapper == null) {
215-
parentQuery = Queries.newNonNestedFilter(context.indexVersionCreated());
216-
} else {
217-
parentQuery = objectMapper.nestedTypeFilter();
218-
}
219220

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

239240
// apply filters from the previous nested level
240-
if (nested != null) {
241-
parentQuery = Queries.filtered(parentQuery,
242-
new ToParentBlockJoinQuery(nested.getInnerQuery(), nested.getRootFilter(), ScoreMode.None));
243-
241+
if (parentQuery != null) {
244242
if (objectMapper != null) {
245243
childQuery = Queries.filtered(childQuery,
246-
new ToChildBlockJoinQuery(nested.getInnerQuery(), context.bitsetFilter(objectMapper.nestedTypeFilter())));
244+
new ToChildBlockJoinQuery(parentQuery, context.bitsetFilter(objectMapper.nestedTypeFilter())));
247245
}
248246
}
249247

250248
// wrap up our parent and child and either process the next level of nesting or return
251-
final Nested innerNested = new Nested(context.bitsetFilter(parentQuery), childQuery);
252249
if (nestedNestedSort != null) {
253250
try {
254251
context.nestedScope().nextLevel(nestedObjectMapper);
255-
return resolveNested(context, nestedNestedSort, innerNested);
252+
return resolveNestedQuery(context, nestedNestedSort, childQuery);
256253
} finally {
257254
context.nestedScope().previousLevel();
258255
}
259256
} else {
260-
return innerNested;
257+
return childQuery;
261258
}
262259
}
263260

0 commit comments

Comments
 (0)