Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/changelog/138695.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
pr: 138695
summary: Add `time_zone` request param support to KQL and QSTR functions
area: ES|QL
type: feature
issues: []
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,40 @@ FROM logs
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;

kqlWithTimeZoneSetting
required_capability: kql_function
required_capability: kql_function_options
required_capability: kql_qstr_timezone_support

SET time_zone = "America/New_York"\;
FROM logs
| WHERE KQL("@timestamp > \"2023-10-23T09:56:00\" AND @timestamp < \"2023-10-23T09:57:00\"")
| KEEP @timestamp, message
| SORT @timestamp ASC
;

@timestamp:date | message:text
2023-10-23T13:56:01.543Z | No response
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;

kqlWithTimeZoneSettingWithOptionOverride
required_capability: kql_function
required_capability: kql_function_options
required_capability: kql_qstr_timezone_support

SET time_zone = "Europe/Madrid"\;
FROM logs
| WHERE KQL("@timestamp > \"2023-10-23T09:56:00\" AND @timestamp < \"2023-10-23T09:57:00\"", {"time_zone": "America/New_York"})
| KEEP @timestamp, message
| SORT @timestamp ASC
;

@timestamp:date | message:text
2023-10-23T13:56:01.543Z | No response
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;

kqlWithDefaultFieldOption
required_capability: kql_function
required_capability: kql_function_options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,52 @@ c: long | scalerank: long
10 | 3
15 | 2
;

qstrWithTimeZoneOption
required_capability: qstr_function
required_capability: query_string_function_options

FROM logs
| WHERE QSTR("@timestamp:[2023-10-23T09:56:01 TO 2023-10-23T09:56:02]", {"time_zone": "America/New_York"})
| KEEP @timestamp, message
| SORT @timestamp ASC
;

@timestamp:date | message:text
2023-10-23T13:56:01.543Z | No response
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;

qstrWithTimeZoneSetting
required_capability: qstr_function
required_capability: query_string_function_options
required_capability: kql_qstr_timezone_support

SET time_zone = "America/New_York"\;
FROM logs
| WHERE QSTR("@timestamp:[2023-10-23T09:56:01 TO 2023-10-23T09:56:02]")
| KEEP @timestamp, message
| SORT @timestamp ASC
;

@timestamp:date | message:text
2023-10-23T13:56:01.543Z | No response
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;

qstrWithTimeZoneSettingWithOptionOverride
required_capability: qstr_function
required_capability: query_string_function_options
required_capability: kql_qstr_timezone_support

SET time_zone = "Europe/Madrid"\;
FROM logs
| WHERE QSTR("@timestamp:[2023-10-23T09:56:01 TO 2023-10-23T09:56:02]", {"time_zone": "America/New_York"})
| KEEP @timestamp, message
| SORT @timestamp ASC
;

@timestamp:date | message:text
2023-10-23T13:56:01.543Z | No response
2023-10-23T13:56:01.544Z | Running cats (cycle 2)
;
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,11 @@ public enum Cap {
*/
DATE_DIFF_TIMEZONE_SUPPORT(Build.current().isSnapshot()),

/**
* Support timezones in KQL and QSTR.
*/
KQL_QSTR_TIMEZONE_SUPPORT(Build.current().isSnapshot()),

/**
* (Re)Added EXPLAIN command
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -524,11 +524,11 @@ private static FunctionDefinition[][] functions() {
// fulltext functions
new FunctionDefinition[] {
def(Decay.class, quad(Decay::new), "decay"),
def(Kql.class, bi(Kql::new), "kql"),
def(Kql.class, bic(Kql::new), "kql"),
def(Knn.class, tri(Knn::new), "knn"),
def(Match.class, tri(Match::new), "match"),
def(MultiMatch.class, MultiMatch::new, "multi_match"),
def(QueryString.class, bi(QueryString::new), "qstr"),
def(QueryString.class, bic(QueryString::new), "qstr"),
def(MatchPhrase.class, tri(MatchPhrase::new), "match_phrase"),
def(Score.class, uni(Score::new), "score") },
// time-series functions
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

package org.elasticsearch.xpack.esql.expression.function;

import org.apache.lucene.util.BytesRef;
import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
import org.elasticsearch.xpack.esql.core.expression.EntryExpression;
import org.elasticsearch.xpack.esql.core.expression.Expression;
Expand Down Expand Up @@ -124,7 +124,7 @@ public static void populateMap(
}

Object optionExprLiteral = ((Literal) optionExpr).value();
String optionName = optionExprLiteral instanceof BytesRef br ? br.utf8ToString() : optionExprLiteral.toString();
String optionName = BytesRefs.toString(optionExprLiteral);
DataType dataType = allowedOptions.get(optionName);

// valueExpr could be a MapExpression, but for now functions only accept literal values in options
Expand All @@ -135,7 +135,7 @@ public static void populateMap(
}

Object valueExprLiteral = ((Literal) valueExpr).value();
String optionValue = valueExprLiteral instanceof BytesRef br ? br.utf8ToString() : valueExprLiteral.toString();
String optionValue = BytesRefs.toString(valueExprLiteral);
// validate the optionExpr is supported
if (dataType == null) {
throw new InvalidArgumentException(
Expand Down Expand Up @@ -173,7 +173,7 @@ public static void populateMapWithExpressionsMultipleDataTypesAllowed(
}

Object optionExprLiteral = ((Literal) optionExpr).value();
String optionName = optionExprLiteral instanceof BytesRef br ? br.utf8ToString() : optionExprLiteral.toString();
String optionName = BytesRefs.toString(optionExprLiteral);
Collection<DataType> allowedDataTypes = allowedOptions.get(optionName);

// valueExpr could be a MapExpression, but for now functions only accept literal values in options
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import org.elasticsearch.xpack.esql.core.tree.Source;
import org.elasticsearch.xpack.esql.core.type.DataType;
import org.elasticsearch.xpack.esql.expression.Foldables;
import org.elasticsearch.xpack.esql.expression.function.ConfigurationFunction;
import org.elasticsearch.xpack.esql.expression.function.Example;
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesTo;
import org.elasticsearch.xpack.esql.expression.function.FunctionAppliesToLifecycle;
Expand All @@ -32,8 +33,10 @@
import org.elasticsearch.xpack.esql.optimizer.rules.physical.local.LucenePushdownPredicates;
import org.elasticsearch.xpack.esql.planner.TranslatorHandler;
import org.elasticsearch.xpack.esql.querydsl.query.KqlQuery;
import org.elasticsearch.xpack.esql.session.Configuration;

import java.io.IOException;
import java.time.ZoneOffset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -58,9 +61,11 @@
/**
* Full text function that performs a {@link KqlQuery} .
*/
public class Kql extends FullTextFunction implements OptionalArgument {
public class Kql extends FullTextFunction implements OptionalArgument, ConfigurationFunction {
public static final NamedWriteableRegistry.Entry ENTRY = new NamedWriteableRegistry.Entry(Expression.class, "Kql", Kql::readFrom);

private final Configuration configuration;

// Options for KQL function. They don't need to be serialized as the data nodes will retrieve them from the query builder
private final transient Expression options;

Expand Down Expand Up @@ -123,13 +128,15 @@ public Kql(
description = "Floating point number used to decrease or increase the relevance scores of the query. Defaults to 1.0."
) },
optional = true
) Expression options
) Expression options,
Configuration configuration
) {
this(source, queryString, options, null);
this(source, queryString, options, null, configuration);
}

public Kql(Source source, Expression queryString, Expression options, QueryBuilder queryBuilder) {
public Kql(Source source, Expression queryString, Expression options, QueryBuilder queryBuilder, Configuration configuration) {
super(source, queryString, options == null ? List.of(queryString) : List.of(queryString, options), queryBuilder);
this.configuration = configuration;
this.options = options;
}

Expand All @@ -141,7 +148,7 @@ private static Kql readFrom(StreamInput in) throws IOException {
queryBuilder = in.readOptionalNamedWriteable(QueryBuilder.class);
}
// Options are not serialized - they're embedded in the QueryBuilder
return new Kql(source, query, null, queryBuilder);
return new Kql(source, query, null, queryBuilder, ((PlanStreamInput) in).configuration());
}

@Override
Expand Down Expand Up @@ -183,23 +190,26 @@ protected TypeResolution resolveParams() {
}

private Map<String, Object> kqlQueryOptions() throws InvalidArgumentException {
if (options() == null) {
if (options() == null && configuration.zoneId().equals(ZoneOffset.UTC)) {
return null;
}

Map<String, Object> kqlOptions = new HashMap<>();
Options.populateMap((MapExpression) options(), kqlOptions, source(), SECOND, ALLOWED_OPTIONS);
if (options() != null) {
Options.populateMap((MapExpression) options(), kqlOptions, source(), SECOND, ALLOWED_OPTIONS);
}
kqlOptions.putIfAbsent(TIME_ZONE_FIELD.getPreferredName(), configuration.zoneId().getId());
return kqlOptions;
}

@Override
public Expression replaceChildren(List<Expression> newChildren) {
return new Kql(source(), newChildren.get(0), newChildren.size() > 1 ? newChildren.get(1) : null, queryBuilder());
return new Kql(source(), newChildren.get(0), newChildren.size() > 1 ? newChildren.get(1) : null, queryBuilder(), configuration);
}

@Override
protected NodeInfo<? extends Expression> info() {
return NodeInfo.create(this, Kql::new, query(), options(), queryBuilder());
return NodeInfo.create(this, Kql::new, query(), options(), queryBuilder(), configuration);
}

@Override
Expand All @@ -209,7 +219,7 @@ protected Query translate(LucenePushdownPredicates pushdownPredicates, Translato

@Override
public Expression replaceQueryBuilder(QueryBuilder queryBuilder) {
return new Kql(source(), query(), options(), queryBuilder);
return new Kql(source(), query(), options(), queryBuilder, configuration);
}

@Override
Expand All @@ -218,12 +228,14 @@ public boolean equals(Object o) {
// ignore options when comparing.
if (o == null || getClass() != o.getClass()) return false;
var kql = (Kql) o;
return Objects.equals(query(), kql.query()) && Objects.equals(queryBuilder(), kql.queryBuilder());
return Objects.equals(query(), kql.query())
&& Objects.equals(queryBuilder(), kql.queryBuilder())
&& Objects.equals(configuration, kql.configuration);
}

@Override
public int hashCode() {
return Objects.hash(query(), queryBuilder());
return Objects.hash(query(), queryBuilder(), configuration);
}

@Override
Expand Down
Loading