Skip to content

Commit e0029a2

Browse files
Merge from main
Signed-off-by: Andy Kwok <[email protected]>
1 parent 7da0dec commit e0029a2

File tree

49 files changed

+1137
-98
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+1137
-98
lines changed

core/src/main/java/org/opensearch/sql/data/type/ExprType.java

+14
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
import java.util.Arrays;
1111
import java.util.List;
12+
import java.util.Optional;
1213
import org.opensearch.sql.data.model.ExprValue;
1314
import org.opensearch.sql.expression.Expression;
1415

@@ -54,4 +55,17 @@ default List<ExprType> getParent() {
5455
default String legacyTypeName() {
5556
return typeName();
5657
}
58+
59+
/** Get the original path. Types like alias type will set the actual path in field property. */
60+
default Optional<String> getOriginalPath() {
61+
return Optional.empty();
62+
}
63+
64+
/**
65+
* Get the original path. Types like alias type should be derived from the type of the original
66+
* field.
67+
*/
68+
default ExprType getOriginalExprType() {
69+
return this;
70+
}
5771
}

core/src/main/java/org/opensearch/sql/expression/DSL.java

+4
Original file line numberDiff line numberDiff line change
@@ -985,6 +985,10 @@ public static FunctionExpression utc_timestamp(
985985
return compile(functionProperties, BuiltinFunctionName.UTC_TIMESTAMP, args);
986986
}
987987

988+
public static FunctionExpression geoip(Expression... args) {
989+
return compile(FunctionProperties.None, BuiltinFunctionName.GEOIP, args);
990+
}
991+
988992
public static FunctionExpression json_function(Expression value) {
989993
return compile(FunctionProperties.None, BuiltinFunctionName.JSON, value);
990994
}

core/src/main/java/org/opensearch/sql/expression/ReferenceExpression.java

+7-2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
public class ReferenceExpression implements Expression {
2424
@Getter private final String attr;
2525

26+
@Getter private final String rawPath;
27+
2628
@Getter private final List<String> paths;
2729

2830
private final ExprType type;
@@ -36,8 +38,11 @@ public class ReferenceExpression implements Expression {
3638
public ReferenceExpression(String ref, ExprType type) {
3739
this.attr = ref;
3840
// Todo. the define of paths need to be redefined after adding multiple index/variable support.
39-
this.paths = Arrays.asList(ref.split("\\."));
40-
this.type = type;
41+
// For AliasType, the actual path is set in the property of `path` and the type is derived
42+
// from the type of field on that path; Otherwise, use ref itself as the path
43+
this.rawPath = type.getOriginalPath().orElse(ref);
44+
this.paths = Arrays.asList(rawPath.split("\\."));
45+
this.type = type.getOriginalExprType();
4146
}
4247

4348
@Override

core/src/main/java/org/opensearch/sql/expression/datetime/DateTimeFunctions.java

+9-1
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@
6363
import java.time.format.DateTimeParseException;
6464
import java.time.format.TextStyle;
6565
import java.time.temporal.ChronoUnit;
66+
import java.time.temporal.IsoFields;
6667
import java.time.temporal.TemporalAmount;
6768
import java.util.Locale;
6869
import java.util.Map;
@@ -123,7 +124,8 @@ public class DateTimeFunctions {
123124
.put("MINUTE", "mm")
124125
.put("HOUR", "HH")
125126
.put("DAY", "dd")
126-
.put("WEEK", "w")
127+
// removing "WEEK" to standardize the extract
128+
// .put("WEEK", "w")
127129
.put("MONTH", "MM")
128130
.put("YEAR", "yyyy")
129131
.put("SECOND_MICROSECOND", "ssSSSSSS")
@@ -1436,6 +1438,12 @@ private ExprValue exprDayOfYear(ExprValue date) {
14361438
public ExprLongValue formatExtractFunction(ExprValue part, ExprValue timestamp) {
14371439
String partName = part.stringValue().toUpperCase();
14381440
LocalDateTime arg = timestamp.timestampValue().atZone(ZoneOffset.UTC).toLocalDateTime();
1441+
1442+
// Override "Week" to use the IsoFields week-of-week-based-year format
1443+
if (partName.equals("WEEK")) {
1444+
return new ExprLongValue(arg.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR));
1445+
}
1446+
14391447
String text =
14401448
arg.format(DateTimeFormatter.ofPattern(extract_formats.get(partName), Locale.ENGLISH));
14411449

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

+3
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,9 @@ public enum BuiltinFunctionName {
210210
JSON_EXTRACT(FunctionName.of("json_extract")),
211211
JSON_SET(FunctionName.of("json_set")),
212212

213+
/** GEOSPATIAL Functions. */
214+
GEOIP(FunctionName.of("geoip")),
215+
213216
/** NULL Test. */
214217
IS_NULL(FunctionName.of("is null")),
215218
IS_NOT_NULL(FunctionName.of("is not null")),

core/src/main/java/org/opensearch/sql/expression/function/OpenSearchFunctions.java

+50
Original file line numberDiff line numberDiff line change
@@ -173,4 +173,54 @@ public String toString() {
173173
return String.format("%s(%s)", functionName, String.join(", ", args));
174174
}
175175
}
176+
177+
/**
178+
* Static class to identify functional Expression which specifically designed for OpenSearch
179+
* storage runtime
180+
*/
181+
public static class OpenSearchExecutableFunction extends FunctionExpression {
182+
private final FunctionName functionName;
183+
private final List<Expression> arguments;
184+
private final ExprType returnType;
185+
186+
public OpenSearchExecutableFunction(
187+
FunctionName functionName, List<Expression> arguments, ExprType returnType) {
188+
super(functionName, arguments);
189+
this.functionName = functionName;
190+
this.arguments = arguments;
191+
this.returnType = returnType;
192+
}
193+
194+
@Override
195+
public ExprValue valueOf(Environment<Expression, ExprValue> valueEnv) {
196+
throw new UnsupportedOperationException(
197+
String.format(
198+
"OpenSearch defined function [%s] is only supported in Eval operation.",
199+
functionName));
200+
}
201+
202+
@Override
203+
public ExprType type() {
204+
return returnType;
205+
}
206+
207+
/**
208+
* Util method to generate probe implementation with given list of argument types, with marker
209+
* class `OpenSearchFunction` to annotate this is an OpenSearch specific expression.
210+
*
211+
* @param returnType return type.
212+
* @return Binary Function Implementation.
213+
*/
214+
public static SerializableFunction<FunctionName, Pair<FunctionSignature, FunctionBuilder>>
215+
openSearchImpl(ExprType returnType, List<ExprType> args) {
216+
return functionName -> {
217+
FunctionSignature functionSignature = new FunctionSignature(functionName, args);
218+
FunctionBuilder functionBuilder =
219+
(functionProperties, arguments) ->
220+
new OpenSearchFunctions.OpenSearchExecutableFunction(
221+
functionName, arguments, returnType);
222+
return Pair.of(functionSignature, functionBuilder);
223+
};
224+
}
225+
}
176226
}

core/src/main/java/org/opensearch/sql/expression/ip/IPFunctions.java

+15
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
import static org.opensearch.sql.expression.function.FunctionDSL.define;
1212
import static org.opensearch.sql.expression.function.FunctionDSL.impl;
1313
import static org.opensearch.sql.expression.function.FunctionDSL.nullMissingHandling;
14+
import static org.opensearch.sql.expression.function.OpenSearchFunctions.OpenSearchExecutableFunction.openSearchImpl;
1415

1516
import inet.ipaddr.IPAddress;
17+
import java.util.Arrays;
1618
import lombok.experimental.UtilityClass;
1719
import org.opensearch.sql.data.model.ExprValue;
1820
import org.opensearch.sql.data.model.ExprValueUtils;
@@ -28,6 +30,7 @@ public class IPFunctions {
2830

2931
public void register(BuiltinFunctionRepository repository) {
3032
repository.register(cidrmatch());
33+
repository.register(geoIp());
3134
}
3235

3336
private DefaultFunctionResolver cidrmatch() {
@@ -57,4 +60,16 @@ private ExprValue exprCidrMatch(ExprValue addressExprValue, ExprValue rangeExprV
5760
? ExprValueUtils.LITERAL_FALSE
5861
: ExprValueUtils.LITERAL_TRUE;
5962
}
63+
64+
/**
65+
* To register all method signatures related to geoip( ) expression under eval.
66+
*
67+
* @return Resolver for geoip( ) expression.
68+
*/
69+
private DefaultFunctionResolver geoIp() {
70+
return define(
71+
BuiltinFunctionName.GEOIP.getName(),
72+
openSearchImpl(BOOLEAN, Arrays.asList(STRING, STRING)),
73+
openSearchImpl(BOOLEAN, Arrays.asList(STRING, STRING, STRING)));
74+
}
6075
}

core/src/main/java/org/opensearch/sql/planner/physical/EvalOperator.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public ExprValue next() {
8787
* @param env {@link Environment}
8888
* @return The mapping of reference and {@link ExprValue} for each expression.
8989
*/
90-
private Map<String, ExprValue> eval(Environment<Expression, ExprValue> env) {
90+
protected Map<String, ExprValue> eval(Environment<Expression, ExprValue> env) {
9191
Map<String, ExprValue> evalResultMap = new LinkedHashMap<>();
9292
for (Pair<ReferenceExpression, Expression> pair : expressionList) {
9393
ReferenceExpression var = pair.getKey();

core/src/main/java/org/opensearch/sql/utils/JsonUtils.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@ public static ExprValue isValidJson(ExprValue jsonExprValue) {
4646

4747
try {
4848
objectMapper.readTree(jsonExprValue.stringValue());
49-
return LITERAL_TRUE;
49+
return ExprValueUtils.LITERAL_TRUE;
5050
} catch (JsonProcessingException e) {
51-
return LITERAL_FALSE;
51+
return ExprValueUtils.LITERAL_FALSE;
5252
}
5353
}
5454

core/src/test/java/org/opensearch/sql/data/type/ExprTypeTest.java

+13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import static org.opensearch.sql.data.type.ExprCoreType.UNDEFINED;
2525
import static org.opensearch.sql.data.type.ExprCoreType.UNKNOWN;
2626

27+
import java.util.Optional;
2728
import org.hamcrest.Matchers;
2829
import org.junit.jupiter.api.Test;
2930

@@ -85,4 +86,16 @@ void defaultLegacyTypeName() {
8586
final ExprType exprType = () -> "dummy";
8687
assertEquals("dummy", exprType.legacyTypeName());
8788
}
89+
90+
@Test
91+
void getOriginalPath() {
92+
final ExprType exprType = () -> "dummy";
93+
assertEquals(Optional.empty(), exprType.getOriginalPath());
94+
}
95+
96+
@Test
97+
void getOriginalExprType() {
98+
final ExprType exprType = () -> "dummy";
99+
assertEquals(exprType, exprType.getOriginalExprType());
100+
}
88101
}

core/src/test/java/org/opensearch/sql/expression/datetime/ExtractTest.java

+51-12
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,21 @@
55

66
package org.opensearch.sql.expression.datetime;
77

8-
import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_YEAR;
98
import static org.junit.jupiter.api.Assertions.assertEquals;
109
import static org.opensearch.sql.data.type.ExprCoreType.LONG;
1110

11+
import java.time.Instant;
1212
import java.time.LocalDate;
13+
import java.time.LocalDateTime;
14+
import java.time.LocalTime;
15+
import java.time.ZoneId;
16+
import java.time.temporal.IsoFields;
1317
import java.util.stream.Stream;
1418
import org.junit.jupiter.api.Test;
1519
import org.junit.jupiter.params.ParameterizedTest;
1620
import org.junit.jupiter.params.provider.Arguments;
1721
import org.junit.jupiter.params.provider.MethodSource;
22+
import org.junit.jupiter.params.provider.ValueSource;
1823
import org.opensearch.sql.data.model.ExprDateValue;
1924
import org.opensearch.sql.data.model.ExprTimeValue;
2025
import org.opensearch.sql.data.model.ExprTimestampValue;
@@ -23,6 +28,7 @@
2328
import org.opensearch.sql.expression.Expression;
2429
import org.opensearch.sql.expression.ExpressionTestBase;
2530
import org.opensearch.sql.expression.FunctionExpression;
31+
import org.opensearch.sql.expression.function.FunctionProperties;
2632

2733
class ExtractTest extends ExpressionTestBase {
2834

@@ -82,9 +88,14 @@ public void testExtractWithDatetime(String part, long expected) {
8288
}
8389

8490
private void datePartWithTimeArgQuery(String part, String time, long expected) {
91+
datePartWithTimeArgQuery(functionProperties, part, time, expected);
92+
}
93+
94+
private void datePartWithTimeArgQuery(
95+
FunctionProperties properties, String part, String time, long expected) {
8596
ExprTimeValue timeValue = new ExprTimeValue(time);
8697
FunctionExpression datetimeExpression =
87-
DSL.extract(functionProperties, DSL.literal(part), DSL.literal(timeValue));
98+
DSL.extract(properties, DSL.literal(part), DSL.literal(timeValue));
8899

89100
assertEquals(LONG, datetimeExpression.type());
90101
assertEquals(expected, eval(datetimeExpression).longValue());
@@ -96,21 +107,49 @@ public void testExtractDatePartWithTimeType() {
96107

97108
datePartWithTimeArgQuery("DAY", timeInput, now.getDayOfMonth());
98109

99-
// To avoid flaky test, skip the testing in December and January because the WEEK is ISO 8601
100-
// week-of-week-based-year which is considered to start on a Monday and week 1 is the first week
101-
// with >3 days. it is possible for early-January dates to be part of the 52nd or 53rd week of
102-
// the previous year, and for late-December dates to be part of the first week of the next year.
103-
// For example, 2005-01-02 is part of the 53rd week of year 2004, while 2012-12-31 is part of
104-
// the first week of 2013
105-
if (now.getMonthValue() != 1 && now.getMonthValue() != 12) {
106-
datePartWithTimeArgQuery("WEEK", datetimeInput, now.get(ALIGNED_WEEK_OF_YEAR));
107-
}
108-
109110
datePartWithTimeArgQuery("MONTH", timeInput, now.getMonthValue());
110111

111112
datePartWithTimeArgQuery("YEAR", timeInput, now.getYear());
112113
}
113114

115+
@ParameterizedTest(name = "{0}")
116+
@ValueSource(
117+
strings = {
118+
"2009-12-26",
119+
"2009-12-27",
120+
"2008-12-28", // Week 52 of week-based-year 2008
121+
"2009-12-29",
122+
"2008-12-29", // Week 1 of week-based-year 2009
123+
"2008-12-31", // Week 1 of week-based-year 2009
124+
"2009-01-01", // Week 1 of week-based-year 2009
125+
"2009-01-04", // Week 1 of week-based-year 2009
126+
"2009-01-05", // Week 2 of week-based-year 2009
127+
"2025-12-27", // year with 52 weeks
128+
"2026-01-01", // year starts on a THURSDAY
129+
"2028-12-30", // year with 53 weeks
130+
"2028-12-31", // year starts in December
131+
"2029-01-01",
132+
"2033-12-31", // year with 53 weeks
133+
"2034-01-01", // January 1st on a SUNDAY
134+
"2034-12-30", // year with 52 weeks
135+
"2034-12-31"
136+
})
137+
public void testExtractWeekPartWithTimeType(String arg) {
138+
139+
// setup default date/time properties for the extract function
140+
ZoneId currentZoneId = ZoneId.systemDefault();
141+
Instant nowInstant =
142+
LocalDate.parse(arg).atTime(LocalTime.parse(timeInput)).atZone(currentZoneId).toInstant();
143+
FunctionProperties properties = new FunctionProperties(nowInstant, currentZoneId);
144+
145+
// Expected WEEK value should be formated from week-of-week-based-year
146+
LocalDateTime localDateTime = LocalDateTime.ofInstant(nowInstant, currentZoneId);
147+
int expected = localDateTime.get(IsoFields.WEEK_OF_WEEK_BASED_YEAR);
148+
149+
// verify
150+
datePartWithTimeArgQuery(properties, "WEEK", timeInput, expected);
151+
}
152+
114153
@ParameterizedTest(name = "{0}")
115154
@MethodSource("getDateResultsForExtractFunction")
116155
public void testExtractWithDate(String part, long expected) {

core/src/test/java/org/opensearch/sql/expression/function/OpenSearchFunctionsTest.java

+14
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import org.opensearch.sql.expression.ExpressionTestBase;
2323
import org.opensearch.sql.expression.FunctionExpression;
2424
import org.opensearch.sql.expression.NamedArgumentExpression;
25+
import org.opensearch.sql.expression.ReferenceExpression;
2526
import org.opensearch.sql.expression.env.Environment;
2627

2728
public class OpenSearchFunctionsTest extends ExpressionTestBase {
@@ -309,4 +310,17 @@ void nested_query() {
309310
assertEquals(expr.valueOf(nestedTuple), ExprValueUtils.stringValue("result"));
310311
assertEquals(expr.type(), STRING);
311312
}
313+
314+
@Test
315+
void opensearchExecutableFunction_valueOf() {
316+
var ipInStr =
317+
new OpenSearchFunctions.OpenSearchExecutableFunction(
318+
BuiltinFunctionName.GEOIP.getName(),
319+
List.of(DSL.literal("my-datasource"), new ReferenceExpression("ipInStr", STRING)),
320+
BOOLEAN);
321+
assertThrows(
322+
UnsupportedOperationException.class,
323+
() -> ipInStr.valueOf(valueEnv()),
324+
"OpenSearch defined function [geoip] is only supported in Eval operation.");
325+
}
312326
}

0 commit comments

Comments
 (0)