diff --git a/core/src/main/java/org/opensearch/sql/ast/tree/Chart.java b/core/src/main/java/org/opensearch/sql/ast/tree/Chart.java index ada20cbde74..2118d90117a 100644 --- a/core/src/main/java/org/opensearch/sql/ast/tree/Chart.java +++ b/core/src/main/java/org/opensearch/sql/ast/tree/Chart.java @@ -97,16 +97,14 @@ private UnresolvedPlan transformPerFunction() { PerFunction perFunc = perFuncOpt.get(); // For chart, the rowSplit should contain the span information - UnresolvedExpression spanExpr = rowSplit; - if (rowSplit instanceof Alias) { - spanExpr = ((Alias) rowSplit).getDelegated(); - } + UnresolvedExpression spanExpr = + rowSplit instanceof Alias ? ((Alias) rowSplit).getDelegated() : rowSplit; if (!(spanExpr instanceof Span)) { return this; // Cannot transform without span information } Span span = (Span) spanExpr; - Field spanStartTime = AstDSL.implicitTimestampField(); + Field spanStartTime = (Field) span.getField(); Function spanEndTime = timestampadd(span.getUnit(), span.getValue(), spanStartTime); Function spanMillis = timestampdiff(MILLISECOND, spanStartTime, spanEndTime); final int SECOND_IN_MILLISECOND = 1000; diff --git a/docs/user/ppl/cmd/timechart.rst b/docs/user/ppl/cmd/timechart.rst index f336007d8fc..21ac980d46a 100644 --- a/docs/user/ppl/cmd/timechart.rst +++ b/docs/user/ppl/cmd/timechart.rst @@ -16,7 +16,9 @@ Description Syntax ====== -timechart [span=] [limit=] [useother=] [by ] +timechart [timefield=] [span=] [limit=] [useother=] [by ] + +* timefield: optional. Specifies the timestamp field to use for time interval grouping. **Default**: ``@timestamp``. * span: optional. Specifies the time interval for grouping data. **Default:** 1m (1 minute). @@ -92,7 +94,7 @@ Return type: DOUBLE Notes ===== -* The ``timechart`` command requires a timestamp field named ``@timestamp`` in the data. +* The ``timechart`` command requires a timestamp field in the data. By default, it uses the ``@timestamp`` field, but you can specify a different field using the ``timefield`` parameter. * Results are returned in an unpivoted format with separate rows for each time-field combination that has data. * Only combinations with actual data are included in the results - empty combinations are omitted rather than showing null or zero values. * The "top N" values for the ``limit`` parameter are selected based on the sum of values across all time intervals for each distinct field value. diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartCommandIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartCommandIT.java index 3b5c5f55475..73396ab31b9 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartCommandIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartCommandIT.java @@ -13,6 +13,7 @@ import org.json.JSONObject; import org.junit.jupiter.api.Test; import org.opensearch.client.ResponseException; +import org.opensearch.sql.common.utils.StringUtils; import org.opensearch.sql.ppl.PPLIntegTestCase; public class CalciteTimechartCommandIT extends PPLIntegTestCase { @@ -64,7 +65,7 @@ public void testTimechartWithMinuteSpanAndGroupBy() throws IOException { } @Test - public void testTimechartWithoutTimestampField() throws IOException { + public void testTimechartWithoutTimestampField() { Throwable exception = assertThrows( ResponseException.class, @@ -74,6 +75,16 @@ public void testTimechartWithoutTimestampField() throws IOException { verifyErrorMessageContains(exception, "Field [@timestamp] not found."); } + @Test + public void testTimechartWithCustomTimeField() throws IOException { + JSONObject result = + executeQuery( + StringUtils.format( + "source=%s | timechart timefield=birthdate span=1year count()", TEST_INDEX_BANK)); + verifySchema(result, schema("birthdate", "timestamp"), schema("count()", "bigint")); + verifyDataRows(result, rows("2017-01-01 00:00:00", 2), rows("2018-01-01 00:00:00", 5)); + } + @Test public void testTimechartWithMinuteSpanNoGroupBy() throws IOException { JSONObject result = executeQuery("source=events | timechart span=1m avg(cpu_usage)"); diff --git a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartPerFunctionIT.java b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartPerFunctionIT.java index 41751376424..b7d072ba6d5 100644 --- a/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartPerFunctionIT.java +++ b/integ-test/src/test/java/org/opensearch/sql/calcite/remote/CalciteTimechartPerFunctionIT.java @@ -5,6 +5,7 @@ package org.opensearch.sql.calcite.remote; +import static org.opensearch.sql.util.MatcherUtils.closeTo; import static org.opensearch.sql.util.MatcherUtils.rows; import static org.opensearch.sql.util.MatcherUtils.schema; import static org.opensearch.sql.util.MatcherUtils.verifyDataRows; @@ -13,6 +14,8 @@ import java.io.IOException; import org.json.JSONObject; import org.junit.jupiter.api.Test; +import org.opensearch.sql.common.utils.StringUtils; +import org.opensearch.sql.legacy.TestsConstants; import org.opensearch.sql.ppl.PPLIntegTestCase; public class CalciteTimechartPerFunctionIT extends PPLIntegTestCase { @@ -24,6 +27,7 @@ public void init() throws Exception { disallowCalciteFallback(); loadIndex(Index.EVENTS_TRAFFIC); + loadIndex(Index.BANK); } @Test @@ -208,4 +212,26 @@ public void testTimechartPerDayWithByClause() throws IOException { rows("2025-09-08 10:02:00", "server1", 43200.0), // 60 * 720 rows("2025-09-08 10:02:00", "server2", 129600.0)); // 180 * 720 } + + @Test + public void testTimechartPerMonthWithSpecifiedSpan() throws IOException { + JSONObject result = + executeQuery( + StringUtils.format( + "source=%s | timechart timefield=birthdate span=1month per_day(balance) by gender", + TestsConstants.TEST_INDEX_BANK)); + verifySchema( + result, + schema("birthdate", "timestamp"), + schema("gender", "string"), + schema("per_day(balance)", "double")); + verifyDataRows( + result, + closeTo("2017-10-01 00:00:00", "M", 1265.3225806451612), + closeTo("2017-11-01 00:00:00", "M", 189.53333333333333), + closeTo("2018-06-01 00:00:00", "F", 1094.6), + closeTo("2018-06-01 00:00:00", "M", 547.2666666666667), + closeTo("2018-08-01 00:00:00", "F", 2858.9032258064517), + closeTo("2018-11-01 00:00:00", "M", 139.33333333333334)); + } } diff --git a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 index 324be7d1cc1..58f0bfd8821 100644 --- a/ppl/src/main/antlr/OpenSearchPPLLexer.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLLexer.g4 @@ -145,6 +145,7 @@ LIMIT: 'LIMIT'; USEOTHER: 'USEOTHER'; OTHERSTR: 'OTHERSTR'; NULLSTR: 'NULLSTR'; +TIMEFIELD: 'TIMEFIELD'; INPUT: 'INPUT'; OUTPUT: 'OUTPUT'; PATH: 'PATH'; diff --git a/ppl/src/main/antlr/OpenSearchPPLParser.g4 b/ppl/src/main/antlr/OpenSearchPPLParser.g4 index da8b9546fec..8c115b655fd 100644 --- a/ppl/src/main/antlr/OpenSearchPPLParser.g4 +++ b/ppl/src/main/antlr/OpenSearchPPLParser.g4 @@ -328,6 +328,7 @@ timechartParameter : LIMIT EQUAL integerLiteral | SPAN EQUAL spanLiteral | USEOTHER EQUAL (booleanLiteral | ident) + | TIMEFIELD EQUAL (ident | stringLiteral) ; spanLiteral @@ -1570,6 +1571,7 @@ searchableKeyWord | SED | MAX_MATCH | OFFSET_FIELD + | TIMEFIELD | patternMethod | patternMode // AGGREGATIONS AND WINDOW diff --git a/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstBuilder.java b/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstBuilder.java index cf674131d92..1f55b1eb8f1 100644 --- a/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstBuilder.java +++ b/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstBuilder.java @@ -63,8 +63,6 @@ import org.opensearch.sql.ast.expression.SearchAnd; import org.opensearch.sql.ast.expression.SearchExpression; import org.opensearch.sql.ast.expression.SearchGroup; -import org.opensearch.sql.ast.expression.Span; -import org.opensearch.sql.ast.expression.SpanUnit; import org.opensearch.sql.ast.expression.UnresolvedArgument; import org.opensearch.sql.ast.expression.UnresolvedExpression; import org.opensearch.sql.ast.expression.WindowFrame; @@ -770,41 +768,28 @@ private List parseAggTerms( /** Timechart command. */ @Override public UnresolvedPlan visitTimechartCommand(OpenSearchPPLParser.TimechartCommandContext ctx) { - UnresolvedExpression binExpression = - AstDSL.span(AstDSL.implicitTimestampField(), AstDSL.intLiteral(1), SpanUnit.m); - Integer limit = 10; - Boolean useOther = true; - // Process timechart parameters - for (OpenSearchPPLParser.TimechartParameterContext paramCtx : ctx.timechartParameter()) { - UnresolvedExpression param = internalVisitExpression(paramCtx); - if (param instanceof Span) { - binExpression = param; - } else if (param instanceof Literal literal) { - if (DataType.BOOLEAN.equals(literal.getType())) { - useOther = (Boolean) literal.getValue(); - } else if (DataType.INTEGER.equals(literal.getType()) - || DataType.LONG.equals(literal.getType())) { - limit = (Integer) literal.getValue(); - } - } - } + List arguments = ArgumentFactory.getArgumentList(ctx, expressionBuilder); + ArgumentMap argMap = ArgumentMap.of(arguments); + Literal spanLiteral = argMap.getOrDefault("spanliteral", AstDSL.stringLiteral("1m")); + String timeFieldName = + Optional.ofNullable(argMap.get("timefield")) + .map(l -> (String) l.getValue()) + .orElse(OpenSearchConstants.IMPLICIT_FIELD_TIMESTAMP); + Field spanField = AstDSL.field(timeFieldName); + Alias span = + AstDSL.alias(timeFieldName, AstDSL.spanFromSpanLengthLiteral(spanField, spanLiteral)); UnresolvedExpression aggregateFunction = parseAggTerms(List.of(ctx.statsAggTerm())).getFirst(); - UnresolvedExpression byField = - ctx.fieldExpression() != null ? internalVisitExpression(ctx.fieldExpression()) : null; - List arguments = - List.of( - new Argument("limit", AstDSL.intLiteral(limit)), - new Argument("useother", AstDSL.booleanLiteral(useOther))); - binExpression = AstDSL.alias(OpenSearchConstants.IMPLICIT_FIELD_TIMESTAMP, binExpression); - if (byField != null) { - byField = - AstDSL.alias( - StringUtils.unquoteIdentifier(getTextInQuery(ctx.fieldExpression())), byField); - } + Optional.ofNullable(ctx.fieldExpression()) + .map( + f -> + AstDSL.alias( + StringUtils.unquoteIdentifier(getTextInQuery(f)), + internalVisitExpression(f))) + .orElse(null); return Chart.builder() .aggregationFunction(aggregateFunction) - .rowSplit(binExpression) + .rowSplit(span) .columnSplit(byField) .arguments(arguments) .build(); diff --git a/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java b/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java index 1a60ca5f2a3..fc4358a81e7 100644 --- a/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java +++ b/ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java @@ -757,7 +757,7 @@ public UnresolvedExpression visitMaxOption(OpenSearchPPLParser.MaxOptionContext return new Argument("max", (Literal) this.visit(ctx.integerLiteral())); } - private QualifiedName visitIdentifiers(List ctx) { + public QualifiedName visitIdentifiers(List ctx) { return new QualifiedName( ctx.stream() .map(RuleContext::getText) @@ -995,47 +995,6 @@ public UnresolvedExpression visitTimeModifierValue( return AstDSL.stringLiteral(osDateMathExpression); } - @Override - public UnresolvedExpression visitTimechartParameter( - OpenSearchPPLParser.TimechartParameterContext ctx) { - UnresolvedExpression timechartParameter; - if (ctx.SPAN() != null) { - // Convert span=1h to span(@timestamp, 1h) - Literal spanLiteral = (Literal) visit(ctx.spanLiteral()); - timechartParameter = - AstDSL.spanFromSpanLengthLiteral(AstDSL.implicitTimestampField(), spanLiteral); - } else if (ctx.LIMIT() != null) { - Literal limit = (Literal) visit(ctx.integerLiteral()); - if ((Integer) limit.getValue() < 0) { - throw new IllegalArgumentException("Limit must be a non-negative number"); - } - timechartParameter = limit; - } else if (ctx.USEOTHER() != null) { - UnresolvedExpression useOther; - if (ctx.booleanLiteral() != null) { - useOther = visit(ctx.booleanLiteral()); - } else if (ctx.ident() != null) { - QualifiedName ident = visitIdentifiers(List.of(ctx.ident())); - String useOtherValue = ident.toString(); - if ("true".equalsIgnoreCase(useOtherValue) || "t".equalsIgnoreCase(useOtherValue)) { - useOther = AstDSL.booleanLiteral(true); - } else if ("false".equalsIgnoreCase(useOtherValue) || "f".equalsIgnoreCase(useOtherValue)) { - useOther = AstDSL.booleanLiteral(false); - } else { - throw new IllegalArgumentException( - "Invalid useOther value: " + ctx.ident().getText() + ". Expected true/false or t/f"); - } - } else { - throw new IllegalArgumentException("value for useOther must be a boolean or identifier"); - } - timechartParameter = useOther; - } else { - throw new IllegalArgumentException( - String.format("A parameter of timechart must be a span, limit or useOther, got %s", ctx)); - } - return timechartParameter; - } - /** * Process time range expressions (EARLIEST='value' or LATEST='value') It creates a Comparison * filter like @timestamp >= timeModifierValue diff --git a/ppl/src/main/java/org/opensearch/sql/ppl/utils/ArgumentFactory.java b/ppl/src/main/java/org/opensearch/sql/ppl/utils/ArgumentFactory.java index 41b1d7c2490..a102565ec0f 100644 --- a/ppl/src/main/java/org/opensearch/sql/ppl/utils/ArgumentFactory.java +++ b/ppl/src/main/java/org/opensearch/sql/ppl/utils/ArgumentFactory.java @@ -33,6 +33,7 @@ import org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.SortFieldContext; import org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.StreamstatsCommandContext; import org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.SuffixSortFieldContext; +import org.opensearch.sql.ppl.parser.AstExpressionBuilder; /** Util class to get all arguments as a list from the PPL command. */ public class ArgumentFactory { @@ -245,6 +246,60 @@ public static List getArgumentList(ChartCommandContext ctx) { return arguments; } + public static List getArgumentList( + OpenSearchPPLParser.TimechartCommandContext timechartCtx, + AstExpressionBuilder expressionBuilder) { + List arguments = new ArrayList<>(); + for (OpenSearchPPLParser.TimechartParameterContext ctx : timechartCtx.timechartParameter()) { + if (ctx.SPAN() != null) { + arguments.add( + new Argument("spanliteral", (Literal) expressionBuilder.visit(ctx.spanLiteral()))); + } else if (ctx.LIMIT() != null) { + Literal limit = getArgumentValue(ctx.integerLiteral()); + if ((Integer) limit.getValue() < 0) { + throw new IllegalArgumentException("Limit must be a non-negative number"); + } + arguments.add(new Argument("limit", limit)); + } else if (ctx.USEOTHER() != null) { + Literal useOther; + if (ctx.booleanLiteral() != null) { + useOther = getArgumentValue(ctx.booleanLiteral()); + } else if (ctx.ident() != null) { + String identLiteral = expressionBuilder.visitIdentifiers(List.of(ctx.ident())).toString(); + if ("true".equalsIgnoreCase(identLiteral) || "t".equalsIgnoreCase(identLiteral)) { + useOther = AstDSL.booleanLiteral(true); + } else if ("false".equalsIgnoreCase(identLiteral) || "f".equalsIgnoreCase(identLiteral)) { + useOther = AstDSL.booleanLiteral(false); + } else { + throw new IllegalArgumentException( + "Invalid useOther value: " + + ctx.ident().getText() + + ". Expected true/false or t/f"); + } + } else { + throw new IllegalArgumentException("value for useOther must be a boolean or identifier"); + } + arguments.add(new Argument("useother", useOther)); + } else if (ctx.TIMEFIELD() != null) { + Literal timeField; + if (ctx.ident() != null) { + timeField = + AstDSL.stringLiteral( + expressionBuilder.visitIdentifiers(List.of(ctx.ident())).toString()); + } else { + timeField = getArgumentValue(ctx.stringLiteral()); + } + arguments.add(new Argument("timefield", timeField)); + } else { + throw new IllegalArgumentException( + String.format( + "A parameter of timechart must be a span, limit, useother, or timefield, got %s", + ctx)); + } + } + return arguments; + } + /** * Get list of {@link Argument}. * diff --git a/ppl/src/main/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizer.java b/ppl/src/main/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizer.java index 271688776e6..04d55992385 100644 --- a/ppl/src/main/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizer.java +++ b/ppl/src/main/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizer.java @@ -9,6 +9,7 @@ import static org.opensearch.sql.calcite.utils.PlanUtils.transformPlanToAttachChild; import static org.opensearch.sql.utils.QueryStringUtils.MASK_COLUMN; import static org.opensearch.sql.utils.QueryStringUtils.MASK_LITERAL; +import static org.opensearch.sql.utils.QueryStringUtils.MASK_TIMESTAMP_COLUMN; import static org.opensearch.sql.utils.QueryStringUtils.maskField; import com.google.common.base.Strings; @@ -18,8 +19,10 @@ import java.util.List; import java.util.Locale; import java.util.Objects; +import java.util.Optional; import java.util.stream.Collectors; import lombok.Getter; +import org.apache.commons.lang3.NotImplementedException; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; import org.opensearch.sql.ast.AbstractNodeVisitor; @@ -98,6 +101,7 @@ import org.opensearch.sql.ast.tree.UnresolvedPlan; import org.opensearch.sql.ast.tree.Values; import org.opensearch.sql.ast.tree.Window; +import org.opensearch.sql.calcite.plan.OpenSearchConstants; import org.opensearch.sql.common.setting.Settings; import org.opensearch.sql.common.utils.StringUtils; import org.opensearch.sql.planner.logical.LogicalAggregation; @@ -513,22 +517,27 @@ public String visitChart(Chart node, String context) { if ("top".equals(argName)) { continue; } - if ("limit".equals(argName) || "useother".equals(argName) || "usenull".equals(argName)) { - chartCommand.append(" ").append(argName).append("=").append(MASK_LITERAL); - } else if ("otherstr".equals(argName) || "nullstr".equals(argName)) { - chartCommand.append(" ").append(argName).append("=").append(MASK_LITERAL); + + switch (argName) { + case "limit", "useother", "usenull", "otherstr", "nullstr" -> + chartCommand.append(" ").append(argName).append("=").append(MASK_LITERAL); + case "spanliteral" -> chartCommand.append(" span=").append(MASK_LITERAL); + case "timefield" -> + chartCommand.append(" ").append(argName).append("=").append(MASK_TIMESTAMP_COLUMN); + default -> + throw new NotImplementedException( + StringUtils.format("Please implement anonymizer for arg: %s", argName)); } } chartCommand.append(" ").append(visitExpression(node.getAggregationFunction())); if (node.getRowSplit() != null && node.getColumnSplit() != null) { - chartCommand - .append(" by ") - .append(visitExpression(node.getRowSplit())) - .append(" ") - .append(visitExpression(node.getColumnSplit())); - } else if (node.getRowSplit() != null) { + chartCommand.append(" by"); + // timechart command does not have to explicit the by-timestamp field clause + if (!isTimechart) chartCommand.append(" ").append(visitExpression(node.getRowSplit())); + chartCommand.append(" ").append(visitExpression(node.getColumnSplit())); + } else if (node.getRowSplit() != null && !isTimechart) { chartCommand.append(" by ").append(visitExpression(node.getRowSplit())); } else if (node.getColumnSplit() != null) { chartCommand.append(" by ").append(visitExpression(node.getColumnSplit())); @@ -544,8 +553,12 @@ private boolean isTimechartNode(Chart node) { Alias alias = (Alias) node.getRowSplit(); if (alias.getDelegated() instanceof Span) { Span span = (Span) alias.getDelegated(); + String timeFieldName = + Optional.ofNullable(ArgumentMap.of(node.getArguments()).get("timefield")) + .map(Literal::toString) + .orElse(OpenSearchConstants.IMPLICIT_FIELD_TIMESTAMP); return span.getField() instanceof Field - && "@timestamp".equals(((Field) span.getField()).getField().toString()); + && timeFieldName.equals(((Field) span.getField()).getField().toString()); } } return false; diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java index 808fbae9273..5f5a133d3fc 100644 --- a/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java +++ b/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstBuilderTest.java @@ -1249,10 +1249,7 @@ public void testTimechartWithPerSecondFunction() { alias("@timestamp", span(field("@timestamp"), intLiteral(1), SpanUnit.of("m")))) .columnSplit(null) .aggregationFunction(alias("per_second(a)", aggregate("sum", field("a")))) - .arguments( - exprList( - argument("limit", intLiteral(10)), - argument("useother", booleanLiteral(true)))) + .arguments(exprList()) .build(), let( field("per_second(a)"), @@ -1281,10 +1278,7 @@ public void testTimechartWithPerMinuteFunction() { alias("@timestamp", span(field("@timestamp"), intLiteral(1), SpanUnit.of("m")))) .columnSplit(null) .aggregationFunction(alias("per_minute(a)", aggregate("sum", field("a")))) - .arguments( - exprList( - argument("limit", intLiteral(10)), - argument("useother", booleanLiteral(true)))) + .arguments(exprList()) .build(), let( field("per_minute(a)"), @@ -1313,10 +1307,7 @@ public void testTimechartWithPerHourFunction() { alias("@timestamp", span(field("@timestamp"), intLiteral(1), SpanUnit.of("m")))) .columnSplit(null) .aggregationFunction(alias("per_hour(a)", aggregate("sum", field("a")))) - .arguments( - exprList( - argument("limit", intLiteral(10)), - argument("useother", booleanLiteral(true)))) + .arguments(exprList()) .build(), let( field("per_hour(a)"), @@ -1345,10 +1336,7 @@ public void testTimechartWithPerDayFunction() { alias("@timestamp", span(field("@timestamp"), intLiteral(1), SpanUnit.of("m")))) .columnSplit(null) .aggregationFunction(alias("per_day(a)", aggregate("sum", field("a")))) - .arguments( - exprList( - argument("limit", intLiteral(10)), - argument("useother", booleanLiteral(true)))) + .arguments(exprList()) .build(), let( field("per_day(a)"), diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java index 2176e51acbf..b316e461889 100644 --- a/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java +++ b/ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java @@ -1423,9 +1423,7 @@ public void testTimechartSpanParameter() { intLiteral(30), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("spanliteral", stringLiteral("30m")))) .build()); } @@ -1443,9 +1441,7 @@ public void testTimechartLimitParameter() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(100)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("limit", intLiteral(100)))) .build()); } @@ -1470,9 +1466,7 @@ public void testTimechartUseOtherWithBooleanLiteral() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("useother", booleanLiteral(true)))) .build()); assertEqual( @@ -1487,9 +1481,7 @@ public void testTimechartUseOtherWithBooleanLiteral() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(false)))) + .arguments(exprList(argument("useother", booleanLiteral(false)))) .build()); } @@ -1507,9 +1499,7 @@ public void testTimechartUseOtherWithIdentifier() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("useother", booleanLiteral(true)))) .build()); assertEqual( @@ -1524,9 +1514,7 @@ public void testTimechartUseOtherWithIdentifier() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(false)))) + .arguments(exprList(argument("useother", booleanLiteral(false)))) .build()); assertEqual( @@ -1541,9 +1529,7 @@ public void testTimechartUseOtherWithIdentifier() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("useother", booleanLiteral(true)))) .build()); assertEqual( @@ -1558,9 +1544,7 @@ public void testTimechartUseOtherWithIdentifier() { intLiteral(1), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(false)))) + .arguments(exprList(argument("useother", booleanLiteral(false)))) .build()); } @@ -1634,9 +1618,7 @@ public void testVisitSpanLiteral() { intLiteral(1), SpanUnit.H))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("spanliteral", stringLiteral("1h")))) .build()); // Test span literal with decimal value and minute unit @@ -1652,9 +1634,7 @@ public void testVisitSpanLiteral() { intLiteral(2), SpanUnit.m))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("spanliteral", stringLiteral("2m")))) .build()); // Test span literal without unit (should use NONE unit) @@ -1670,9 +1650,7 @@ public void testVisitSpanLiteral() { intLiteral(10), SpanUnit.NONE))) .aggregationFunction(alias("count()", aggregate("count", allFields()))) - .arguments( - exprList( - argument("limit", intLiteral(10)), argument("useother", booleanLiteral(true)))) + .arguments(exprList(argument("spanliteral", intLiteral(10)))) .build()); // Test span literal with decimal value diff --git a/ppl/src/test/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizerTest.java b/ppl/src/test/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizerTest.java index 45f7611db17..ede8c4e4a5a 100644 --- a/ppl/src/test/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizerTest.java +++ b/ppl/src/test/java/org/opensearch/sql/ppl/utils/PPLQueryDataAnonymizerTest.java @@ -268,9 +268,12 @@ public void testReverseCommand() { @Test public void testTimechartCommand() { assertEquals( - "source=table | timechart limit=*** useother=*** count() by span(time_identifier, ***" - + " m) identifier", + "source=table | timechart count() by identifier", anonymize("source=t | timechart count() by host")); + + assertEquals( + "source=table | timechart timefield=time_identifier max(identifier)", + anonymize("source=t | timechart timefield=month max(revenue)")); } @Test