Skip to content

Commit 8ed26e6

Browse files
Add Week_Of_Year Function To OpenSearch (#1127)
Signed-off-by: GabeFernandez310 <[email protected]>
1 parent b304ddc commit 8ed26e6

File tree

8 files changed

+214
-13
lines changed

8 files changed

+214
-13
lines changed

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,10 @@ public static FunctionExpression week(Expression... expressions) {
394394
return compile(FunctionProperties.None, BuiltinFunctionName.WEEK, expressions);
395395
}
396396

397+
public static FunctionExpression week_of_year(Expression... expressions) {
398+
return compile(FunctionProperties.None, BuiltinFunctionName.WEEK_OF_YEAR, expressions);
399+
}
400+
397401
public static FunctionExpression year(Expression... expressions) {
398402
return compile(FunctionProperties.None, BuiltinFunctionName.YEAR, expressions);
399403
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,8 @@ public void register(BuiltinFunctionRepository repository) {
128128
repository.register(date_format());
129129
repository.register(to_days());
130130
repository.register(unix_timestamp());
131-
repository.register(week());
131+
repository.register(week(BuiltinFunctionName.WEEK));
132+
repository.register(week(BuiltinFunctionName.WEEK_OF_YEAR));
132133
repository.register(year());
133134
}
134135

@@ -567,8 +568,8 @@ private FunctionResolver unix_timestamp() {
567568
/**
568569
* WEEK(DATE[,mode]). return the week number for date.
569570
*/
570-
private DefaultFunctionResolver week() {
571-
return define(BuiltinFunctionName.WEEK.getName(),
571+
private DefaultFunctionResolver week(BuiltinFunctionName week) {
572+
return define(week.getName(),
572573
impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, DATE),
573574
impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, DATETIME),
574575
impl(nullMissingHandling(DateTimeFunction::exprWeekWithoutMode), INTEGER, TIMESTAMP),

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,7 @@ public enum BuiltinFunctionName {
9191
TO_DAYS(FunctionName.of("to_days")),
9292
UNIX_TIMESTAMP(FunctionName.of("unix_timestamp")),
9393
WEEK(FunctionName.of("week")),
94+
WEEK_OF_YEAR(FunctionName.of("week_of_year")),
9495
YEAR(FunctionName.of("year")),
9596
// `now`-like functions
9697
NOW(FunctionName.of("now")),

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

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1038,6 +1038,124 @@ public void modeInUnsupportedFormat() {
10381038
exception.getMessage());
10391039
}
10401040

1041+
private void testWeekOfYear(String date, int mode, int expectedResult) {
1042+
FunctionExpression expression = DSL
1043+
.week_of_year(DSL.literal(new ExprDateValue(date)), DSL.literal(mode));
1044+
assertEquals(INTEGER, expression.type());
1045+
assertEquals(String.format("week_of_year(DATE '%s', %d)", date, mode), expression.toString());
1046+
assertEquals(integerValue(expectedResult), eval(expression));
1047+
}
1048+
1049+
private void testNullMissingWeekOfYear(ExprCoreType date) {
1050+
when(nullRef.type()).thenReturn(date);
1051+
when(missingRef.type()).thenReturn(date);
1052+
assertEquals(nullValue(), eval(DSL.week_of_year(nullRef)));
1053+
assertEquals(missingValue(), eval(DSL.week_of_year(missingRef)));
1054+
}
1055+
1056+
@Test
1057+
public void testInvalidWeekOfYear() {
1058+
testNullMissingWeekOfYear(DATE);
1059+
testNullMissingWeekOfYear(DATETIME);
1060+
testNullMissingWeekOfYear(TIMESTAMP);
1061+
testNullMissingWeekOfYear(STRING);
1062+
1063+
when(nullRef.type()).thenReturn(INTEGER);
1064+
when(missingRef.type()).thenReturn(INTEGER);
1065+
assertEquals(nullValue(), eval(DSL.week_of_year(DSL.literal("2019-01-05"), nullRef)));
1066+
assertEquals(missingValue(), eval(DSL.week_of_year(DSL.literal("2019-01-05"), missingRef)));
1067+
1068+
when(nullRef.type()).thenReturn(DATE);
1069+
when(missingRef.type()).thenReturn(INTEGER);
1070+
assertEquals(missingValue(), eval(DSL.week_of_year(nullRef, missingRef)));
1071+
1072+
//test invalid month
1073+
assertThrows(SemanticCheckException.class, () -> testWeekOfYear("2019-13-05 01:02:03", 0, 0));
1074+
//test invalid day
1075+
assertThrows(SemanticCheckException.class, () -> testWeekOfYear("2019-01-50 01:02:03", 0, 0));
1076+
//test invalid leap year
1077+
assertThrows(SemanticCheckException.class, () -> testWeekOfYear("2019-02-29 01:02:03", 0, 0));
1078+
}
1079+
1080+
@Test
1081+
public void testWeekOfYearAlternateArgumentFormats() {
1082+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
1083+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
1084+
1085+
FunctionExpression expression = DSL
1086+
.week_of_year(DSL.literal(new ExprTimestampValue("2019-01-05 01:02:03")));
1087+
assertEquals(INTEGER, expression.type());
1088+
assertEquals("week_of_year(TIMESTAMP '2019-01-05 01:02:03')", expression.toString());
1089+
assertEquals(integerValue(0), eval(expression));
1090+
1091+
expression = DSL.week_of_year(DSL.literal("2019-01-05"));
1092+
assertEquals(INTEGER, expression.type());
1093+
assertEquals("week_of_year(\"2019-01-05\")", expression.toString());
1094+
assertEquals(integerValue(0), eval(expression));
1095+
1096+
expression = DSL.week_of_year(DSL.literal("2019-01-05 00:01:00"));
1097+
assertEquals(INTEGER, expression.type());
1098+
assertEquals("week_of_year(\"2019-01-05 00:01:00\")", expression.toString());
1099+
assertEquals(integerValue(0), eval(expression));
1100+
}
1101+
1102+
@Test
1103+
public void testWeekOfYearDifferentModes() {
1104+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
1105+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
1106+
1107+
//Test the behavior of different modes passed into the 'week_of_year' function
1108+
testWeekOfYear("2019-01-05", 0, 0);
1109+
testWeekOfYear("2019-01-05", 1, 1);
1110+
testWeekOfYear("2019-01-05", 2, 52);
1111+
testWeekOfYear("2019-01-05", 3, 1);
1112+
testWeekOfYear("2019-01-05", 4, 1);
1113+
testWeekOfYear("2019-01-05", 5, 0);
1114+
testWeekOfYear("2019-01-05", 6, 1);
1115+
testWeekOfYear("2019-01-05", 7, 53);
1116+
1117+
testWeekOfYear("2019-01-06", 0, 1);
1118+
testWeekOfYear("2019-01-06", 1, 1);
1119+
testWeekOfYear("2019-01-06", 2, 1);
1120+
testWeekOfYear("2019-01-06", 3, 1);
1121+
testWeekOfYear("2019-01-06", 4, 2);
1122+
testWeekOfYear("2019-01-06", 5, 0);
1123+
testWeekOfYear("2019-01-06", 6, 2);
1124+
testWeekOfYear("2019-01-06", 7, 53);
1125+
1126+
testWeekOfYear("2019-01-07", 0, 1);
1127+
testWeekOfYear("2019-01-07", 1, 2);
1128+
testWeekOfYear("2019-01-07", 2, 1);
1129+
testWeekOfYear("2019-01-07", 3, 2);
1130+
testWeekOfYear("2019-01-07", 4, 2);
1131+
testWeekOfYear("2019-01-07", 5, 1);
1132+
testWeekOfYear("2019-01-07", 6, 2);
1133+
testWeekOfYear("2019-01-07", 7, 1);
1134+
1135+
testWeekOfYear("2000-01-01", 0, 0);
1136+
testWeekOfYear("2000-01-01", 2, 52);
1137+
testWeekOfYear("1999-12-31", 0, 52);
1138+
1139+
}
1140+
1141+
@Test
1142+
public void weekOfYearModeInUnsupportedFormat() {
1143+
testNullMissingWeekOfYear(DATE);
1144+
1145+
FunctionExpression expression1 = DSL
1146+
.week_of_year(DSL.literal(new ExprDateValue("2019-01-05")), DSL.literal(8));
1147+
SemanticCheckException exception =
1148+
assertThrows(SemanticCheckException.class, () -> eval(expression1));
1149+
assertEquals("mode:8 is invalid, please use mode value between 0-7",
1150+
exception.getMessage());
1151+
1152+
FunctionExpression expression2 = DSL
1153+
.week_of_year(DSL.literal(new ExprDateValue("2019-01-05")), DSL.literal(-1));
1154+
exception = assertThrows(SemanticCheckException.class, () -> eval(expression2));
1155+
assertEquals("mode:-1 is invalid, please use mode value between 0-7",
1156+
exception.getMessage());
1157+
}
1158+
10411159
@Test
10421160
public void to_days() {
10431161
when(nullRef.type()).thenReturn(DATE);

docs/user/dql/functions.rst

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2116,6 +2116,7 @@ Description
21162116
>>>>>>>>>>>
21172117

21182118
Usage: week(date[, mode]) returns the week number for date. If the mode argument is omitted, the default mode 0 is used.
2119+
The function `week_of_year` is also provided as an alias.
21192120

21202121
.. list-table:: The following table describes how the mode argument works.
21212122
:widths: 25 50 25 75
@@ -2164,14 +2165,36 @@ Return type: INTEGER
21642165

21652166
Example::
21662167

2167-
>od SELECT WEEK(DATE('2008-02-20')), WEEK(DATE('2008-02-20'), 1)
2168+
os> SELECT WEEK(DATE('2008-02-20')), WEEK(DATE('2008-02-20'), 1)
21682169
fetched rows / total rows = 1/1
21692170
+----------------------------+-------------------------------+
21702171
| WEEK(DATE('2008-02-20')) | WEEK(DATE('2008-02-20'), 1) |
2171-
|----------------------------|-------------------------------|
2172+
|----------------------------+-------------------------------|
21722173
| 7 | 8 |
21732174
+----------------------------+-------------------------------+
21742175

2176+
WEEK_OF_YEAR
2177+
----
2178+
2179+
Description
2180+
>>>>>>>>>>>
2181+
2182+
The week_of_year function is a synonym for the `week`_ function.
2183+
2184+
Argument type: DATE/DATETIME/TIMESTAMP/STRING
2185+
2186+
Return type: INTEGER
2187+
2188+
Example::
2189+
2190+
os> SELECT WEEK_OF_YEAR(DATE('2008-02-20')), WEEK_OF_YEAR(DATE('2008-02-20'), 1)
2191+
fetched rows / total rows = 1/1
2192+
+------------------------------------+---------------------------------------+
2193+
| WEEK_OF_YEAR(DATE('2008-02-20')) | WEEK_OF_YEAR(DATE('2008-02-20'), 1) |
2194+
|------------------------------------+---------------------------------------|
2195+
| 7 | 8 |
2196+
+------------------------------------+---------------------------------------+
2197+
21752198

21762199
YEAR
21772200
----

integ-test/src/test/java/org/opensearch/sql/sql/DateTimeFunctionIT.java

Lines changed: 54 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ public class DateTimeFunctionIT extends SQLIntegTestCase {
4949
public void init() throws Exception {
5050
super.init();
5151
loadIndex(Index.BANK);
52+
loadIndex(Index.CALCS);
5253
loadIndex(Index.PEOPLE2);
5354
loadIndex(Index.CALCS);
5455
}
@@ -475,11 +476,11 @@ public void testYear() throws IOException {
475476
verifyDataRows(result, rows(2020));
476477
}
477478

478-
private void week(String date, int mode, int expectedResult) throws IOException {
479-
JSONObject result = executeQuery(StringUtils.format("select week(date('%s'), %d)", date,
479+
private void week(String date, int mode, int expectedResult, String functionName) throws IOException {
480+
JSONObject result = executeQuery(StringUtils.format("select %s(date('%s'), %d)", functionName, date,
480481
mode));
481482
verifySchema(result,
482-
schema(StringUtils.format("week(date('%s'), %d)", date, mode), null, "integer"));
483+
schema(StringUtils.format("%s(date('%s'), %d)", functionName, date, mode), null, "integer"));
483484
verifyDataRows(result, rows(expectedResult));
484485
}
485486

@@ -489,11 +490,56 @@ public void testWeek() throws IOException {
489490
verifySchema(result, schema("week(date('2008-02-20'))", null, "integer"));
490491
verifyDataRows(result, rows(7));
491492

492-
week("2008-02-20", 0, 7);
493-
week("2008-02-20", 1, 8);
494-
week("2008-12-31", 1, 53);
495-
week("2000-01-01", 0, 0);
496-
week("2000-01-01", 2, 52);
493+
week("2008-02-20", 0, 7, "week");
494+
week("2008-02-20", 1, 8, "week");
495+
week("2008-12-31", 1, 53, "week");
496+
week("2000-01-01", 0, 0, "week");
497+
week("2000-01-01", 2, 52, "week");
498+
}
499+
500+
@Test
501+
public void testWeekOfYear() throws IOException {
502+
JSONObject result = executeQuery("select week_of_year(date('2008-02-20'))");
503+
verifySchema(result, schema("week_of_year(date('2008-02-20'))", null, "integer"));
504+
verifyDataRows(result, rows(7));
505+
506+
week("2008-02-20", 0, 7, "week_of_year");
507+
week("2008-02-20", 1, 8, "week_of_year");
508+
week("2008-12-31", 1, 53, "week_of_year");
509+
week("2000-01-01", 0, 0, "week_of_year");
510+
week("2000-01-01", 2, 52, "week_of_year");
511+
}
512+
513+
@Test
514+
public void testWeekAlternateSyntaxesReturnTheSameResults() throws IOException {
515+
JSONObject result1 = executeQuery("SELECT week(date('2022-11-22'))");
516+
JSONObject result2 = executeQuery("SELECT week_of_year(date('2022-11-22'))");
517+
verifyDataRows(result1, rows(47));
518+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
519+
520+
result1 = executeQuery(String.format(
521+
"SELECT week(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
522+
result2 = executeQuery(String.format(
523+
"SELECT week_of_year(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
524+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
525+
526+
result1 = executeQuery(String.format(
527+
"SELECT week(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
528+
result2 = executeQuery(String.format(
529+
"SELECT week_of_year(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
530+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
531+
532+
result1 = executeQuery(String.format(
533+
"SELECT week(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
534+
result2 = executeQuery(String.format(
535+
"SELECT week_of_year(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
536+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
537+
538+
result1 = executeQuery(String.format(
539+
"SELECT week(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
540+
result2 = executeQuery(String.format(
541+
"SELECT week_of_year(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
542+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
497543
}
498544

499545
void verifyDateFormat(String date, String type, String format, String formatted) throws IOException {

sql/src/main/antlr/OpenSearchSQLParser.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ datetimeConstantLiteral
250250
| UTC_TIMESTAMP
251251
| UTC_DATE
252252
| UTC_TIME
253+
| WEEK_OF_YEAR
253254
;
254255

255256
intervalLiteral

sql/src/test/java/org/opensearch/sql/sql/antlr/SQLSyntaxParserTest.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,13 @@ public void can_parse_now_like_functions(String name, Boolean hasFsp, Boolean ha
191191
assertNotNull(parser.parse("SELECT id FROM test WHERE " + String.join(" AND ", calls)));
192192
}
193193

194+
195+
@Test
196+
public void can_parse_week_of_year_functions() {
197+
assertNotNull(parser.parse("SELECT week('2022-11-18')"));
198+
assertNotNull(parser.parse("SELECT week_of_year('2022-11-18')"));
199+
}
200+
194201
@Test
195202
public void can_parse_dayofyear_functions() {
196203
assertNotNull(parser.parse("SELECT dayofyear('2022-11-18')"));

0 commit comments

Comments
 (0)