Skip to content

Commit b304ddc

Browse files
Add Day_Of_Year Function To OpenSearch (#1128)
Signed-off-by: GabeFernandez310 <[email protected]>
1 parent 43ceda1 commit b304ddc

File tree

8 files changed

+214
-3
lines changed

8 files changed

+214
-3
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
@@ -330,6 +330,10 @@ public static FunctionExpression dayofyear(Expression... expressions) {
330330
return compile(FunctionProperties.None, BuiltinFunctionName.DAYOFYEAR, expressions);
331331
}
332332

333+
public static FunctionExpression day_of_year(Expression... expressions) {
334+
return compile(FunctionProperties.None, BuiltinFunctionName.DAY_OF_YEAR, expressions);
335+
}
336+
333337
public static FunctionExpression from_days(Expression... expressions) {
334338
return compile(FunctionProperties.None, BuiltinFunctionName.FROM_DAYS, expressions);
335339
}

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
@@ -102,7 +102,8 @@ public void register(BuiltinFunctionRepository repository) {
102102
repository.register(dayName());
103103
repository.register(dayOfMonth());
104104
repository.register(dayOfWeek());
105-
repository.register(dayOfYear());
105+
repository.register(dayOfYear(BuiltinFunctionName.DAYOFYEAR));
106+
repository.register(dayOfYear(BuiltinFunctionName.DAY_OF_YEAR));
106107
repository.register(from_days());
107108
repository.register(from_unixtime());
108109
repository.register(hour());
@@ -359,8 +360,8 @@ private DefaultFunctionResolver dayOfWeek() {
359360
* DAYOFYEAR(STRING/DATE/DATETIME/TIMESTAMP).
360361
* return the day of the year for date (1-366).
361362
*/
362-
private DefaultFunctionResolver dayOfYear() {
363-
return define(BuiltinFunctionName.DAYOFYEAR.getName(),
363+
private DefaultFunctionResolver dayOfYear(BuiltinFunctionName dayOfYear) {
364+
return define(dayOfYear.getName(),
364365
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, DATE),
365366
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, DATETIME),
366367
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), 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
@@ -69,6 +69,7 @@ public enum BuiltinFunctionName {
6969
DAYOFMONTH(FunctionName.of("dayofmonth")),
7070
DAYOFWEEK(FunctionName.of("dayofweek")),
7171
DAYOFYEAR(FunctionName.of("dayofyear")),
72+
DAY_OF_YEAR(FunctionName.of("day_of_year")),
7273
FROM_DAYS(FunctionName.of("from_days")),
7374
FROM_UNIXTIME(FunctionName.of("from_unixtime")),
7475
HOUR(FunctionName.of("hour")),

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

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66

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

9+
import static org.junit.jupiter.api.Assertions.assertAll;
910
import static org.junit.jupiter.api.Assertions.assertEquals;
1011
import static org.junit.jupiter.api.Assertions.assertThrows;
12+
import static org.mockito.Mockito.lenient;
1113
import static org.mockito.Mockito.when;
1214
import static org.opensearch.sql.data.model.ExprValueUtils.integerValue;
1315
import static org.opensearch.sql.data.model.ExprValueUtils.longValue;
@@ -479,6 +481,93 @@ public void dayOfYear() {
479481
assertEquals(integerValue(220), eval(expression));
480482
}
481483

484+
public void testDayOfYearWithUnderscores(String date, int dayOfYear) {
485+
FunctionExpression expression = DSL.day_of_year(DSL.literal(new ExprDateValue(date)));
486+
assertEquals(INTEGER, expression.type());
487+
assertEquals(integerValue(dayOfYear), eval(expression));
488+
}
489+
490+
@Test
491+
public void dayOfYearWithUnderscoresDifferentArgumentFormats() {
492+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
493+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
494+
495+
FunctionExpression expression1 = DSL.day_of_year(DSL.literal(new ExprDateValue("2020-08-07")));
496+
FunctionExpression expression2 = DSL.day_of_year(DSL.literal("2020-08-07"));
497+
FunctionExpression expression3 = DSL.day_of_year(DSL.literal("2020-08-07 01:02:03"));
498+
499+
assertAll(
500+
() -> testDayOfYearWithUnderscores("2020-08-07", 220),
501+
() -> assertEquals("day_of_year(DATE '2020-08-07')", expression1.toString()),
502+
503+
() -> testDayOfYearWithUnderscores("2020-08-07", 220),
504+
() -> assertEquals("day_of_year(\"2020-08-07\")", expression2.toString()),
505+
506+
() -> testDayOfYearWithUnderscores("2020-08-07 01:02:03", 220),
507+
() -> assertEquals("day_of_year(\"2020-08-07 01:02:03\")", expression3.toString())
508+
);
509+
}
510+
511+
@Test
512+
public void dayOfYearWithUnderscoresCornerCaseDates() {
513+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
514+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
515+
516+
assertAll(
517+
//31st of December during non leap year (should be 365)
518+
() -> testDayOfYearWithUnderscores("2019-12-31", 365),
519+
//Year 1200
520+
() -> testDayOfYearWithUnderscores("1200-02-28", 59),
521+
//Year 4000
522+
() -> testDayOfYearWithUnderscores("4000-02-28", 59)
523+
);
524+
}
525+
526+
@Test
527+
public void dayOfYearWithUnderscoresLeapYear() {
528+
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
529+
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());
530+
531+
assertAll(
532+
//28th of Feb
533+
() -> testDayOfYearWithUnderscores("2020-02-28", 59),
534+
535+
//29th of Feb during leap year
536+
() -> testDayOfYearWithUnderscores("2020-02-29 23:59:59", 60),
537+
() -> testDayOfYearWithUnderscores("2020-02-29", 60),
538+
539+
//1st of March during leap year
540+
() -> testDayOfYearWithUnderscores("2020-03-01 00:00:00", 61),
541+
() -> testDayOfYearWithUnderscores("2020-03-01", 61),
542+
543+
//1st of March during non leap year
544+
() -> testDayOfYearWithUnderscores("2019-03-01", 60),
545+
546+
//31st of December during leap year (should be 366)
547+
() -> testDayOfYearWithUnderscores("2020-12-31", 366)
548+
);
549+
}
550+
551+
public void testInvalidDayOfYear(String date) {
552+
FunctionExpression expression = DSL.day_of_year(DSL.literal(new ExprDateValue(date)));
553+
eval(expression);
554+
}
555+
556+
@Test
557+
public void invalidDayOfYearArgument() {
558+
when(nullRef.type()).thenReturn(DATE);
559+
when(missingRef.type()).thenReturn(DATE);
560+
assertEquals(nullValue(), eval(DSL.day_of_year(nullRef)));
561+
assertEquals(missingValue(), eval(DSL.day_of_year(missingRef)));
562+
563+
//29th of Feb non-leapyear
564+
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("2019-02-29"));
565+
//13th month
566+
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("2019-13-15"));
567+
//incorrect format for type
568+
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("asdfasdfasdf"));
569+
}
570+
482571
@Test
483572
public void from_days() {
484573
when(nullRef.type()).thenReturn(LONG);

docs/user/dql/functions.rst

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1457,6 +1457,7 @@ Description
14571457
>>>>>>>>>>>
14581458

14591459
Usage: dayofyear(date) returns the day of the year for date, in the range 1 to 366.
1460+
The function `day_of_year`_ is also provided as an alias.
14601461

14611462
Argument type: STRING/DATE/DATETIME/TIMESTAMP
14621463

@@ -1472,6 +1473,62 @@ Example::
14721473
| 239 |
14731474
+---------------------------------+
14741475

1476+
os> SELECT DAYOFYEAR(DATETIME('2020-08-26 00:00:00'))
1477+
fetched rows / total rows = 1/1
1478+
+----------------------------------------------+
1479+
| DAYOFYEAR(DATETIME('2020-08-26 00:00:00')) |
1480+
|----------------------------------------------|
1481+
| 239 |
1482+
+----------------------------------------------+
1483+
1484+
os> SELECT DAYOFYEAR(TIMESTAMP('2020-08-26 00:00:00'))
1485+
fetched rows / total rows = 1/1
1486+
+-----------------------------------------------+
1487+
| DAYOFYEAR(TIMESTAMP('2020-08-26 00:00:00')) |
1488+
|-----------------------------------------------|
1489+
| 239 |
1490+
+-----------------------------------------------+
1491+
1492+
1493+
DAY_OF_YEAR
1494+
---------
1495+
1496+
Description
1497+
>>>>>>>>>>>
1498+
1499+
This function is an alias to the `dayofyear`_ function
1500+
1501+
Argument type: STRING/DATE/DATETIME/TIMESTAMP
1502+
1503+
Return type: INTEGER
1504+
1505+
Example::
1506+
1507+
os> SELECT DAY_OF_YEAR(DATE('2020-08-26'))
1508+
fetched rows / total rows = 1/1
1509+
+-----------------------------------+
1510+
| DAY_OF_YEAR(DATE('2020-08-26')) |
1511+
|-----------------------------------|
1512+
| 239 |
1513+
+-----------------------------------+
1514+
1515+
os> SELECT DAY_OF_YEAR(DATETIME('2020-08-26 00:00:00'))
1516+
fetched rows / total rows = 1/1
1517+
+------------------------------------------------+
1518+
| DAY_OF_YEAR(DATETIME('2020-08-26 00:00:00')) |
1519+
|------------------------------------------------|
1520+
| 239 |
1521+
+------------------------------------------------+
1522+
1523+
os> SELECT DAY_OF_YEAR(TIMESTAMP('2020-08-26 00:00:00'))
1524+
fetched rows / total rows = 1/1
1525+
+-------------------------------------------------+
1526+
| DAY_OF_YEAR(TIMESTAMP('2020-08-26 00:00:00')) |
1527+
|-------------------------------------------------|
1528+
| 239 |
1529+
+-------------------------------------------------+
1530+
1531+
14751532

14761533
FROM_DAYS
14771534
---------

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

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
package org.opensearch.sql.sql;
88

99
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK;
10+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_CALCS;
1011
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_PEOPLE2;
1112
import static org.opensearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT;
1213
import static org.opensearch.sql.util.MatcherUtils.rows;
@@ -49,6 +50,7 @@ public void init() throws Exception {
4950
super.init();
5051
loadIndex(Index.BANK);
5152
loadIndex(Index.PEOPLE2);
53+
loadIndex(Index.CALCS);
5254
}
5355

5456
@Test
@@ -225,6 +227,56 @@ public void testDayOfYear() throws IOException {
225227
verifyDataRows(result, rows(260));
226228
}
227229

230+
@Test
231+
public void testDayOfYearWithUnderscores() throws IOException {
232+
JSONObject result = executeQuery("select day_of_year(date('2020-09-16'))");
233+
verifySchema(result, schema("day_of_year(date('2020-09-16'))", null, "integer"));
234+
verifyDataRows(result, rows(260));
235+
236+
result = executeQuery("select day_of_year(datetime('2020-09-16 00:00:00'))");
237+
verifySchema(result, schema("day_of_year(datetime('2020-09-16 00:00:00'))", null, "integer"));
238+
verifyDataRows(result, rows(260));
239+
240+
result = executeQuery("select day_of_year(timestamp('2020-09-16 00:00:00'))");
241+
verifySchema(result, schema("day_of_year(timestamp('2020-09-16 00:00:00'))", null, "integer"));
242+
verifyDataRows(result, rows(260));
243+
244+
result = executeQuery("select day_of_year('2020-09-16')");
245+
verifySchema(result, schema("day_of_year('2020-09-16')", null, "integer"));
246+
verifyDataRows(result, rows(260));
247+
}
248+
249+
@Test
250+
public void testDayOfYearAlternateSyntaxesReturnTheSameResults() throws IOException {
251+
JSONObject result1 = executeQuery("SELECT dayofyear(date('2022-11-22'))");
252+
JSONObject result2 = executeQuery("SELECT day_of_year(date('2022-11-22'))");
253+
verifyDataRows(result1, rows(326));
254+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
255+
256+
result1 = executeQuery(String.format(
257+
"SELECT dayofyear(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
258+
result2 = executeQuery(String.format(
259+
"SELECT day_of_year(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
260+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
261+
262+
result1 = executeQuery(String.format(
263+
"SELECT dayofyear(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
264+
result2 = executeQuery(String.format(
265+
"SELECT day_of_year(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
266+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
267+
268+
result1 = executeQuery(String.format(
269+
"SELECT dayofyear(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
270+
result2 = executeQuery(String.format(
271+
"SELECT day_of_year(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
272+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
273+
274+
result1 = executeQuery(String.format(
275+
"SELECT dayofyear(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
276+
result2 = executeQuery(String.format(
277+
"SELECT day_of_year(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
278+
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
279+
}
228280
@Test
229281
public void testFromDays() throws IOException {
230282
JSONObject result = executeQuery("select from_days(738049)");

sql/src/main/antlr/OpenSearchSQLParser.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,7 @@ datetimeConstantLiteral
244244
: CURRENT_DATE
245245
| CURRENT_TIME
246246
| CURRENT_TIMESTAMP
247+
| DAY_OF_YEAR
247248
| LOCALTIME
248249
| LOCALTIMESTAMP
249250
| UTC_TIMESTAMP

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -191,6 +191,12 @@ 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+
@Test
195+
public void can_parse_dayofyear_functions() {
196+
assertNotNull(parser.parse("SELECT dayofyear('2022-11-18')"));
197+
assertNotNull(parser.parse("SELECT day_of_year('2022-11-18')"));
198+
}
199+
194200
@Test
195201
public void can_parse_multi_match_relevance_function() {
196202
assertNotNull(parser.parse(

0 commit comments

Comments
 (0)