|
8 | 8 | package org.elasticsearch.xpack.esql.expression.function.scalar.date; |
9 | 9 |
|
10 | 10 | import org.elasticsearch.common.Rounding; |
11 | | -import org.elasticsearch.common.TriFunction; |
12 | 11 | import org.elasticsearch.common.io.stream.NamedWriteableRegistry; |
13 | 12 | import org.elasticsearch.common.io.stream.StreamInput; |
14 | 13 | import org.elasticsearch.common.io.stream.StreamOutput; |
|
17 | 16 | import org.elasticsearch.compute.ann.Fixed; |
18 | 17 | import org.elasticsearch.compute.operator.EvalOperator.ExpressionEvaluator; |
19 | 18 | import org.elasticsearch.core.TimeValue; |
20 | | -import org.elasticsearch.core.Tuple; |
21 | | -import org.elasticsearch.logging.LogManager; |
22 | | -import org.elasticsearch.logging.Logger; |
23 | 19 | import org.elasticsearch.xpack.esql.core.expression.Expression; |
24 | | -import org.elasticsearch.xpack.esql.core.expression.FieldAttribute; |
25 | | -import org.elasticsearch.xpack.esql.core.expression.FoldContext; |
26 | | -import org.elasticsearch.xpack.esql.core.expression.Literal; |
27 | 20 | import org.elasticsearch.xpack.esql.core.tree.NodeInfo; |
28 | 21 | import org.elasticsearch.xpack.esql.core.tree.Source; |
29 | 22 | import org.elasticsearch.xpack.esql.core.type.DataType; |
30 | | -import org.elasticsearch.xpack.esql.core.type.MultiTypeEsField; |
31 | | -import org.elasticsearch.xpack.esql.core.util.Holder; |
32 | | -import org.elasticsearch.xpack.esql.expression.LocalSurrogateExpression; |
33 | 23 | import org.elasticsearch.xpack.esql.expression.function.Example; |
34 | 24 | import org.elasticsearch.xpack.esql.expression.function.FunctionInfo; |
35 | 25 | import org.elasticsearch.xpack.esql.expression.function.Param; |
36 | 26 | import org.elasticsearch.xpack.esql.expression.function.scalar.EsqlScalarFunction; |
37 | | -import org.elasticsearch.xpack.esql.expression.function.scalar.math.RoundTo; |
38 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.Equals; |
39 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.EsqlBinaryComparison; |
40 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThan; |
41 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.GreaterThanOrEqual; |
42 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThan; |
43 | | -import org.elasticsearch.xpack.esql.expression.predicate.operator.comparison.LessThanOrEqual; |
44 | 27 | import org.elasticsearch.xpack.esql.io.stream.PlanStreamInput; |
45 | | -import org.elasticsearch.xpack.esql.stats.SearchStats; |
46 | 28 |
|
47 | 29 | import java.io.IOException; |
48 | 30 | import java.time.Duration; |
49 | 31 | import java.time.Period; |
50 | 32 | import java.time.ZoneId; |
51 | | -import java.util.Arrays; |
52 | 33 | import java.util.List; |
53 | 34 | import java.util.Map; |
54 | 35 | import java.util.concurrent.TimeUnit; |
55 | | -import java.util.stream.Collectors; |
56 | 36 |
|
57 | 37 | import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.FIRST; |
58 | 38 | import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.ParamOrdinal.SECOND; |
59 | 39 | import static org.elasticsearch.xpack.esql.core.expression.TypeResolutions.isType; |
60 | 40 | import static org.elasticsearch.xpack.esql.core.type.DataType.DATETIME; |
61 | 41 | import static org.elasticsearch.xpack.esql.core.type.DataType.DATE_NANOS; |
62 | | -import static org.elasticsearch.xpack.esql.core.type.DataType.isDateTime; |
63 | 42 | import static org.elasticsearch.xpack.esql.session.Configuration.DEFAULT_TZ; |
64 | | -import static org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter.dateWithTypeToString; |
65 | 43 |
|
66 | | -public class DateTrunc extends EsqlScalarFunction implements LocalSurrogateExpression { |
| 44 | +public class DateTrunc extends EsqlScalarFunction { |
67 | 45 | public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry( |
68 | 46 | Expression.class, |
69 | 47 | "DateTrunc", |
70 | 48 | DateTrunc::new |
71 | 49 | ); |
72 | 50 |
|
73 | | - private static final Logger logger = LogManager.getLogger(DateTrunc.class); |
74 | | - |
75 | 51 | @FunctionalInterface |
76 | 52 | public interface DateTruncFactoryProvider { |
77 | 53 | ExpressionEvaluator.Factory apply(Source source, ExpressionEvaluator.Factory lhs, Rounding.Prepared rounding); |
@@ -129,7 +105,7 @@ public String getWriteableName() { |
129 | 105 | return ENTRY.name; |
130 | 106 | } |
131 | 107 |
|
132 | | - Expression interval() { |
| 108 | + public Expression interval() { |
133 | 109 | return interval; |
134 | 110 | } |
135 | 111 |
|
@@ -293,96 +269,4 @@ public static ExpressionEvaluator.Factory evaluator( |
293 | 269 | ) { |
294 | 270 | return evaluatorMap.get(forType).apply(source, fieldEvaluator, rounding); |
295 | 271 | } |
296 | | - |
297 | | - @Override |
298 | | - public Expression surrogate(SearchStats searchStats, List<EsqlBinaryComparison> binaryComparisons) { |
299 | | - // LocalSubstituteSurrogateExpressions should make sure this doesn't happen |
300 | | - assert searchStats != null : "SearchStats cannot be null"; |
301 | | - return maybeSubstituteWithRoundTo( |
302 | | - source(), |
303 | | - field(), |
304 | | - interval(), |
305 | | - searchStats, |
306 | | - binaryComparisons, |
307 | | - (interval, minValue, maxValue) -> createRounding(interval, DEFAULT_TZ, minValue, maxValue) |
308 | | - ); |
309 | | - } |
310 | | - |
311 | | - public static RoundTo maybeSubstituteWithRoundTo( |
312 | | - Source source, |
313 | | - Expression field, |
314 | | - Expression foldableTimeExpression, |
315 | | - SearchStats searchStats, |
316 | | - List<EsqlBinaryComparison> binaryComparisons, |
317 | | - TriFunction<Object, Long, Long, Rounding.Prepared> roundingFunction |
318 | | - ) { |
319 | | - if (field instanceof FieldAttribute fa && fa.field() instanceof MultiTypeEsField == false && isDateTime(fa.dataType())) { |
320 | | - // Extract min/max from SearchStats |
321 | | - DataType fieldType = fa.dataType(); |
322 | | - FieldAttribute.FieldName fieldName = fa.fieldName(); |
323 | | - var min = searchStats.min(fieldName); |
324 | | - var max = searchStats.max(fieldName); |
325 | | - // Extract min/max from query |
326 | | - Tuple<Long, Long> minMaxFromPredicates = minMaxFromPredicates(binaryComparisons); |
327 | | - Long minFromPredicates = minMaxFromPredicates.v1(); |
328 | | - Long maxFromPredicates = minMaxFromPredicates.v2(); |
329 | | - // Consolidate min/max from SearchStats and query |
330 | | - if (minFromPredicates instanceof Long minValue) { |
331 | | - min = min instanceof Long m ? Math.max(m, minValue) : minValue; |
332 | | - } |
333 | | - if (maxFromPredicates instanceof Long maxValue) { |
334 | | - max = max instanceof Long m ? Math.min(m, maxValue) : maxValue; |
335 | | - } |
336 | | - // If min/max is available create rounding with them |
337 | | - if (min instanceof Long minValue && max instanceof Long maxValue && foldableTimeExpression.foldable() && minValue <= maxValue) { |
338 | | - Object foldedInterval = foldableTimeExpression.fold(FoldContext.small() /* TODO remove me */); |
339 | | - Rounding.Prepared rounding = roundingFunction.apply(foldedInterval, minValue, maxValue); |
340 | | - long[] roundingPoints = rounding.fixedRoundingPoints(); |
341 | | - if (roundingPoints == null) { |
342 | | - logger.trace( |
343 | | - "Fixed rounding point is null for field {}, minValue {} in string format {} and maxValue {} in string format {}", |
344 | | - fieldName, |
345 | | - minValue, |
346 | | - dateWithTypeToString(minValue, fieldType), |
347 | | - maxValue, |
348 | | - dateWithTypeToString(maxValue, fieldType) |
349 | | - ); |
350 | | - return null; |
351 | | - } |
352 | | - // Convert to round_to function with the roundings |
353 | | - List<Expression> points = Arrays.stream(roundingPoints) |
354 | | - .mapToObj(l -> new Literal(Source.EMPTY, l, fieldType)) |
355 | | - .collect(Collectors.toList()); |
356 | | - return new RoundTo(source, field, points); |
357 | | - } |
358 | | - } |
359 | | - return null; |
360 | | - } |
361 | | - |
362 | | - private static Tuple<Long, Long> minMaxFromPredicates(List<EsqlBinaryComparison> binaryComparisons) { |
363 | | - long[] min = new long[] { Long.MIN_VALUE }; |
364 | | - long[] max = new long[] { Long.MAX_VALUE }; |
365 | | - Holder<Boolean> foundMinValue = new Holder<>(false); |
366 | | - Holder<Boolean> foundMaxValue = new Holder<>(false); |
367 | | - for (EsqlBinaryComparison binaryComparison : binaryComparisons) { |
368 | | - if (binaryComparison.right() instanceof Literal l) { |
369 | | - long value = Long.parseLong(l.value().toString()); |
370 | | - if (binaryComparison instanceof Equals) { |
371 | | - return new Tuple<>(value, value); |
372 | | - } |
373 | | - if (binaryComparison instanceof GreaterThan || binaryComparison instanceof GreaterThanOrEqual) { |
374 | | - if (value >= min[0]) { |
375 | | - min[0] = value; |
376 | | - foundMinValue.set(true); |
377 | | - } |
378 | | - } else if (binaryComparison instanceof LessThan || binaryComparison instanceof LessThanOrEqual) { |
379 | | - if (value <= max[0]) { |
380 | | - max[0] = value; |
381 | | - foundMaxValue.set(true); |
382 | | - } |
383 | | - } |
384 | | - } |
385 | | - } |
386 | | - return new Tuple<>(foundMinValue.get() ? min[0] : null, foundMaxValue.get() ? max[0] : null); |
387 | | - } |
388 | 272 | } |
0 commit comments