31
31
import org .opensearch .index .mapper .DocCountFieldMapper ;
32
32
import org .opensearch .index .mapper .MappedFieldType ;
33
33
import org .opensearch .index .query .DateRangeIncludingNowQuery ;
34
- import org .opensearch .search .aggregations .bucket .composite .CompositeAggregator ;
35
34
import org .opensearch .search .aggregations .bucket .composite .CompositeValuesSourceConfig ;
36
35
import org .opensearch .search .aggregations .bucket .composite .RoundingValuesSource ;
37
36
import org .opensearch .search .aggregations .bucket .histogram .LongBounds ;
@@ -90,11 +89,11 @@ private static Query unwrapIntoConcreteQuery(Query query) {
90
89
}
91
90
92
91
/**
93
- * Finds the global min and max bounds of the field for the shard from each segment
92
+ * Finds the global min and max bounds of the field for the shard across all segments
94
93
*
95
94
* @return null if the field is empty or not indexed
96
95
*/
97
- private static long [] getIndexBounds (final SearchContext context , final String fieldName ) throws IOException {
96
+ private static long [] getShardBounds (final SearchContext context , final String fieldName ) throws IOException {
98
97
final List <LeafReaderContext > leaves = context .searcher ().getIndexReader ().leaves ();
99
98
long min = Long .MAX_VALUE , max = Long .MIN_VALUE ;
100
99
for (LeafReaderContext leaf : leaves ) {
@@ -111,6 +110,25 @@ private static long[] getIndexBounds(final SearchContext context, final String f
111
110
return new long [] { min , max };
112
111
}
113
112
113
+ /**
114
+ * Finds the min and max bounds of the field for the segment
115
+ *
116
+ * @return null if the field is empty or not indexed
117
+ */
118
+ private static long [] getSegmentBounds (final LeafReaderContext context , final String fieldName ) throws IOException {
119
+ long min = Long .MAX_VALUE , max = Long .MIN_VALUE ;
120
+ final PointValues values = context .reader ().getPointValues (fieldName );
121
+ if (values != null ) {
122
+ min = Math .min (min , NumericUtils .sortableBytesToLong (values .getMinPackedValue (), 0 ));
123
+ max = Math .max (max , NumericUtils .sortableBytesToLong (values .getMaxPackedValue (), 0 ));
124
+ }
125
+
126
+ if (min == Long .MAX_VALUE || max == Long .MIN_VALUE ) {
127
+ return null ;
128
+ }
129
+ return new long [] { min , max };
130
+ }
131
+
114
132
/**
115
133
* This method also acts as a pre-condition check for the optimization
116
134
*
@@ -120,32 +138,21 @@ public static long[] getDateHistoAggBounds(final SearchContext context, final St
120
138
final Query cq = unwrapIntoConcreteQuery (context .query ());
121
139
if (cq instanceof PointRangeQuery ) {
122
140
final PointRangeQuery prq = (PointRangeQuery ) cq ;
123
- final long [] indexBounds = getIndexBounds (context , fieldName );
141
+ final long [] indexBounds = getShardBounds (context , fieldName );
124
142
if (indexBounds == null ) return null ;
125
143
return getBoundsWithRangeQuery (prq , fieldName , indexBounds );
126
144
} else if (cq instanceof MatchAllDocsQuery ) {
127
- return getIndexBounds (context , fieldName );
145
+ return getShardBounds (context , fieldName );
128
146
} else if (cq instanceof FieldExistsQuery ) {
129
147
// when a range query covers all values of a shard, it will be rewrite field exists query
130
148
if (((FieldExistsQuery ) cq ).getField ().equals (fieldName )) {
131
- return getIndexBounds (context , fieldName );
149
+ return getShardBounds (context , fieldName );
132
150
}
133
151
}
134
152
135
153
return null ;
136
154
}
137
155
138
- private static long [] getDateHistoAggBoundsSegLevel (final SearchContext context , final String fieldName ) throws IOException {
139
- final long [] indexBounds = getIndexBounds (context , fieldName );
140
- if (indexBounds == null ) return null ;
141
- final Query cq = unwrapIntoConcreteQuery (context .query ());
142
- if (cq instanceof PointRangeQuery ) {
143
- final PointRangeQuery prq = (PointRangeQuery ) cq ;
144
- return getBoundsWithRangeQuery (prq , fieldName , indexBounds );
145
- }
146
- return indexBounds ;
147
- }
148
-
149
156
private static long [] getBoundsWithRangeQuery (PointRangeQuery prq , String fieldName , long [] indexBounds ) {
150
157
// Ensure that the query and aggregation are on the same field
151
158
if (prq .getField ().equals (fieldName )) {
@@ -158,6 +165,7 @@ private static long[] getBoundsWithRangeQuery(PointRangeQuery prq, String fieldN
158
165
}
159
166
return new long [] { lower , upper };
160
167
}
168
+
161
169
return null ;
162
170
}
163
171
@@ -230,7 +238,6 @@ public static class FastFilterContext {
230
238
private boolean rewriteable = false ;
231
239
private Weight [] filters = null ;
232
240
private boolean filtersBuiltAtShardLevel = false ;
233
- private boolean shouldBuildFiltersAtSegmentLevel = true ;
234
241
235
242
private AggregationType aggregationType ;
236
243
private final SearchContext context ;
@@ -278,6 +285,10 @@ interface AggregationType {
278
285
279
286
Weight [] buildFastFilter (SearchContext ctx , CheckedBiFunction <SearchContext , String , long [], IOException > getBounds )
280
287
throws IOException ;
288
+
289
+ default int getSize () {
290
+ return Integer .MAX_VALUE ;
291
+ }
281
292
}
282
293
283
294
/**
@@ -314,8 +325,23 @@ public boolean isRewriteable(Object parent, int subAggLength) {
314
325
public Weight [] buildFastFilter (SearchContext context , CheckedBiFunction <SearchContext , String , long [], IOException > getBounds )
315
326
throws IOException {
316
327
long [] bounds = getBounds .apply (context , fieldType .name ());
317
- bounds = processHardBounds (bounds );
318
328
logger .debug ("Bounds are {} for shard {}" , bounds , context .indexShard ().shardId ());
329
+ return buildFastFilter (context , bounds );
330
+ }
331
+
332
+ private Weight [] buildFastFilterWithSegBounds (
333
+ SearchContext context ,
334
+ CheckedBiFunction <LeafReaderContext , String , long [], IOException > getBounds ,
335
+ LeafReaderContext leaf
336
+ ) throws IOException {
337
+ long [] bounds = getBounds .apply (leaf , fieldType .name ());
338
+ logger .debug ("Bounds are {} for shard {} segment {}" , bounds , context .indexShard ().shardId (), leaf .ord );
339
+ return buildFastFilter (context , bounds );
340
+ }
341
+
342
+ private Weight [] buildFastFilter (SearchContext context , long [] bounds ) throws IOException {
343
+ bounds = processHardBounds (bounds );
344
+ logger .debug ("Bounds are {} for shard {} with hard bound" , bounds , context .indexShard ().shardId ());
319
345
if (bounds == null ) {
320
346
return null ;
321
347
}
@@ -394,7 +420,7 @@ public static boolean tryFastFilterAggregation(
394
420
final BiConsumer <Long , Integer > incrementDocCount
395
421
) throws IOException {
396
422
if (fastFilterContext == null ) return false ;
397
- if (!fastFilterContext .rewriteable || ! fastFilterContext . shouldBuildFiltersAtSegmentLevel ) {
423
+ if (!fastFilterContext .rewriteable ) {
398
424
return false ;
399
425
}
400
426
@@ -420,11 +446,15 @@ public static boolean tryFastFilterAggregation(
420
446
fastFilterContext .context .indexShard ().shardId (),
421
447
ctx .ord
422
448
);
423
- filters = fastFilterContext .buildFastFilter (FastFilterRewriteHelper ::getDateHistoAggBoundsSegLevel );
449
+ if (fastFilterContext .aggregationType instanceof AbstractDateHistogramAggregationType ) {
450
+ filters = ((AbstractDateHistogramAggregationType ) fastFilterContext .aggregationType ).buildFastFilterWithSegBounds (
451
+ fastFilterContext .context ,
452
+ FastFilterRewriteHelper ::getSegmentBounds ,
453
+ ctx
454
+ );
455
+ }
424
456
if (filters == null ) {
425
- // At segment level, build filter should only be called once
426
- // since the conditions for build filter won't change for other segments
427
- fastFilterContext .shouldBuildFiltersAtSegmentLevel = false ;
457
+
428
458
return false ;
429
459
}
430
460
}
@@ -441,7 +471,7 @@ public static boolean tryFastFilterAggregation(
441
471
}
442
472
443
473
int s = 0 ;
444
- int size = Integer . MAX_VALUE ;
474
+ int size = fastFilterContext . aggregationType . getSize () ;
445
475
for (i = 0 ; i < filters .length ; i ++) {
446
476
if (counts [i ] > 0 ) {
447
477
long bucketKey = i ; // the index of filters is the key for filters aggregation
@@ -451,9 +481,6 @@ public static boolean tryFastFilterAggregation(
451
481
bucketKey = fieldType .convertNanosToMillis (
452
482
NumericUtils .sortableBytesToLong (((PointRangeQuery ) filters [i ].getQuery ()).getLowerPoint (), 0 )
453
483
);
454
- if (fastFilterContext .aggregationType instanceof CompositeAggregator .CompositeAggregationType ) {
455
- size = ((CompositeAggregator .CompositeAggregationType ) fastFilterContext .aggregationType ).getSize ();
456
- }
457
484
}
458
485
incrementDocCount .accept (bucketKey , counts [i ]);
459
486
s ++;
0 commit comments