Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Day_Of_Year Function To OpenSearch #1128

Merged
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
4 changes: 4 additions & 0 deletions core/src/main/java/org/opensearch/sql/expression/DSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -330,6 +330,10 @@ public static FunctionExpression dayofyear(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DAYOFYEAR, expressions);
}

public static FunctionExpression day_of_year(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.DAY_OF_YEAR, expressions);
}

public static FunctionExpression from_days(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.FROM_DAYS, expressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(dayName());
repository.register(dayOfMonth());
repository.register(dayOfWeek());
repository.register(dayOfYear());
repository.register(dayOfYear(BuiltinFunctionName.DAYOFYEAR));
repository.register(dayOfYear(BuiltinFunctionName.DAY_OF_YEAR));
repository.register(from_days());
repository.register(from_unixtime());
repository.register(hour());
Expand Down Expand Up @@ -359,8 +360,8 @@ private DefaultFunctionResolver dayOfWeek() {
* DAYOFYEAR(STRING/DATE/DATETIME/TIMESTAMP).
* return the day of the year for date (1-366).
*/
private DefaultFunctionResolver dayOfYear() {
return define(BuiltinFunctionName.DAYOFYEAR.getName(),
private DefaultFunctionResolver dayOfYear(BuiltinFunctionName dayOfYear) {
return define(dayOfYear.getName(),
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, DATE),
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprDayOfYear), INTEGER, TIMESTAMP),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ public enum BuiltinFunctionName {
DAYOFMONTH(FunctionName.of("dayofmonth")),
DAYOFWEEK(FunctionName.of("dayofweek")),
DAYOFYEAR(FunctionName.of("dayofyear")),
DAY_OF_YEAR(FunctionName.of("day_of_year")),
FROM_DAYS(FunctionName.of("from_days")),
FROM_UNIXTIME(FunctionName.of("from_unixtime")),
HOUR(FunctionName.of("hour")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,10 @@

package org.opensearch.sql.expression.datetime;

import static org.junit.jupiter.api.Assertions.assertAll;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.Mockito.lenient;
import static org.mockito.Mockito.when;
import static org.opensearch.sql.data.model.ExprValueUtils.integerValue;
import static org.opensearch.sql.data.model.ExprValueUtils.longValue;
Expand Down Expand Up @@ -479,6 +481,93 @@ public void dayOfYear() {
assertEquals(integerValue(220), eval(expression));
}

public void testDayOfYearWithUnderscores(String date, int dayOfYear) {
FunctionExpression expression = DSL.day_of_year(DSL.literal(new ExprDateValue(date)));
assertEquals(INTEGER, expression.type());
assertEquals(integerValue(dayOfYear), eval(expression));
}

@Test
public void dayOfYearWithUnderscoresDifferentArgumentFormats() {
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());

FunctionExpression expression1 = DSL.day_of_year(DSL.literal(new ExprDateValue("2020-08-07")));
FunctionExpression expression2 = DSL.day_of_year(DSL.literal("2020-08-07"));
FunctionExpression expression3 = DSL.day_of_year(DSL.literal("2020-08-07 01:02:03"));

assertAll(
() -> testDayOfYearWithUnderscores("2020-08-07", 220),
() -> assertEquals("day_of_year(DATE '2020-08-07')", expression1.toString()),

() -> testDayOfYearWithUnderscores("2020-08-07", 220),
() -> assertEquals("day_of_year(\"2020-08-07\")", expression2.toString()),

() -> testDayOfYearWithUnderscores("2020-08-07 01:02:03", 220),
() -> assertEquals("day_of_year(\"2020-08-07 01:02:03\")", expression3.toString())
);
}

@Test
public void dayOfYearWithUnderscoresCornerCaseDates() {
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());

assertAll(
//31st of December during non leap year (should be 365)
() -> testDayOfYearWithUnderscores("2019-12-31", 365),
//Year 1200
() -> testDayOfYearWithUnderscores("1200-02-28", 59),
//Year 4000
() -> testDayOfYearWithUnderscores("4000-02-28", 59)
);
}

@Test
public void dayOfYearWithUnderscoresLeapYear() {
lenient().when(nullRef.valueOf(env)).thenReturn(nullValue());
lenient().when(missingRef.valueOf(env)).thenReturn(missingValue());

assertAll(
//28th of Feb
() -> testDayOfYearWithUnderscores("2020-02-28", 59),

//29th of Feb during leap year
() -> testDayOfYearWithUnderscores("2020-02-29 23:59:59", 60),
() -> testDayOfYearWithUnderscores("2020-02-29", 60),

//1st of March during leap year
() -> testDayOfYearWithUnderscores("2020-03-01 00:00:00", 61),
() -> testDayOfYearWithUnderscores("2020-03-01", 61),

//1st of March during non leap year
() -> testDayOfYearWithUnderscores("2019-03-01", 60),

//31st of December during leap year (should be 366)
() -> testDayOfYearWithUnderscores("2020-12-31", 366)
);
}

public void testInvalidDayOfYear(String date) {
FunctionExpression expression = DSL.day_of_year(DSL.literal(new ExprDateValue(date)));
eval(expression);
}

@Test
public void invalidDayOfYearArgument() {
when(nullRef.type()).thenReturn(DATE);
when(missingRef.type()).thenReturn(DATE);
assertEquals(nullValue(), eval(DSL.day_of_year(nullRef)));
assertEquals(missingValue(), eval(DSL.day_of_year(missingRef)));

//29th of Feb non-leapyear
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("2019-02-29"));
//13th month
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("2019-13-15"));
//incorrect format for type
assertThrows(SemanticCheckException.class, () -> testInvalidDayOfYear("asdfasdfasdf"));
}

@Test
public void from_days() {
when(nullRef.type()).thenReturn(LONG);
Expand Down
57 changes: 57 additions & 0 deletions docs/user/dql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1457,6 +1457,7 @@ Description
>>>>>>>>>>>

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

Argument type: STRING/DATE/DATETIME/TIMESTAMP

Expand All @@ -1472,6 +1473,62 @@ Example::
| 239 |
+---------------------------------+

os> SELECT DAYOFYEAR(DATETIME('2020-08-26 00:00:00'))
fetched rows / total rows = 1/1
+----------------------------------------------+
| DAYOFYEAR(DATETIME('2020-08-26 00:00:00')) |
|----------------------------------------------|
| 239 |
+----------------------------------------------+

os> SELECT DAYOFYEAR(TIMESTAMP('2020-08-26 00:00:00'))
fetched rows / total rows = 1/1
+-----------------------------------------------+
| DAYOFYEAR(TIMESTAMP('2020-08-26 00:00:00')) |
|-----------------------------------------------|
| 239 |
+-----------------------------------------------+


DAY_OF_YEAR
---------

Description
>>>>>>>>>>>

This function is an alias to the `dayofyear`_ function

Argument type: STRING/DATE/DATETIME/TIMESTAMP

Return type: INTEGER

Example::

os> SELECT DAY_OF_YEAR(DATE('2020-08-26'))
fetched rows / total rows = 1/1
+-----------------------------------+
| DAY_OF_YEAR(DATE('2020-08-26')) |
|-----------------------------------|
| 239 |
+-----------------------------------+

os> SELECT DAY_OF_YEAR(DATETIME('2020-08-26 00:00:00'))
fetched rows / total rows = 1/1
+------------------------------------------------+
| DAY_OF_YEAR(DATETIME('2020-08-26 00:00:00')) |
|------------------------------------------------|
| 239 |
+------------------------------------------------+

os> SELECT DAY_OF_YEAR(TIMESTAMP('2020-08-26 00:00:00'))
fetched rows / total rows = 1/1
+-------------------------------------------------+
| DAY_OF_YEAR(TIMESTAMP('2020-08-26 00:00:00')) |
|-------------------------------------------------|
| 239 |
+-------------------------------------------------+



FROM_DAYS
---------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
package org.opensearch.sql.sql;

import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BANK;
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_CALCS;
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_PEOPLE2;
import static org.opensearch.sql.legacy.plugin.RestSqlAction.QUERY_API_ENDPOINT;
import static org.opensearch.sql.util.MatcherUtils.rows;
Expand Down Expand Up @@ -49,6 +50,7 @@ public void init() throws Exception {
super.init();
loadIndex(Index.BANK);
loadIndex(Index.PEOPLE2);
loadIndex(Index.CALCS);
}

@Test
Expand Down Expand Up @@ -225,6 +227,56 @@ public void testDayOfYear() throws IOException {
verifyDataRows(result, rows(260));
}

@Test
public void testDayOfYearWithUnderscores() throws IOException {
JSONObject result = executeQuery("select day_of_year(date('2020-09-16'))");
verifySchema(result, schema("day_of_year(date('2020-09-16'))", null, "integer"));
verifyDataRows(result, rows(260));

result = executeQuery("select day_of_year(datetime('2020-09-16 00:00:00'))");
verifySchema(result, schema("day_of_year(datetime('2020-09-16 00:00:00'))", null, "integer"));
verifyDataRows(result, rows(260));

result = executeQuery("select day_of_year(timestamp('2020-09-16 00:00:00'))");
verifySchema(result, schema("day_of_year(timestamp('2020-09-16 00:00:00'))", null, "integer"));
verifyDataRows(result, rows(260));

result = executeQuery("select day_of_year('2020-09-16')");
verifySchema(result, schema("day_of_year('2020-09-16')", null, "integer"));
verifyDataRows(result, rows(260));
}

@Test
public void testDayOfYearAlternateSyntaxesReturnTheSameResults() throws IOException {
JSONObject result1 = executeQuery("SELECT dayofyear(date('2022-11-22'))");
JSONObject result2 = executeQuery("SELECT day_of_year(date('2022-11-22'))");
verifyDataRows(result1, rows(326));
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));

result1 = executeQuery(String.format(
"SELECT dayofyear(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
result2 = executeQuery(String.format(
"SELECT day_of_year(CAST(date0 AS date)) FROM %s", TEST_INDEX_CALCS));
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));

result1 = executeQuery(String.format(
"SELECT dayofyear(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
result2 = executeQuery(String.format(
"SELECT day_of_year(datetime(CAST(time0 AS STRING))) FROM %s", TEST_INDEX_CALCS));
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));

result1 = executeQuery(String.format(
"SELECT dayofyear(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
result2 = executeQuery(String.format(
"SELECT day_of_year(CAST(time0 AS STRING)) FROM %s", TEST_INDEX_CALCS));
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));

result1 = executeQuery(String.format(
"SELECT dayofyear(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
result2 = executeQuery(String.format(
"SELECT day_of_year(CAST(datetime0 AS timestamp)) FROM %s", TEST_INDEX_CALCS));
result1.getJSONArray("datarows").similar(result2.getJSONArray("datarows"));
}
@Test
public void testFromDays() throws IOException {
JSONObject result = executeQuery("select from_days(738049)");
Expand Down
1 change: 1 addition & 0 deletions sql/src/main/antlr/OpenSearchSQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,7 @@ datetimeConstantLiteral
: CURRENT_DATE
| CURRENT_TIME
| CURRENT_TIMESTAMP
| DAY_OF_YEAR
| LOCALTIME
| LOCALTIMESTAMP
| UTC_TIMESTAMP
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,12 @@ public void can_parse_now_like_functions(String name, Boolean hasFsp, Boolean ha
assertNotNull(parser.parse("SELECT id FROM test WHERE " + String.join(" AND ", calls)));
}

@Test
public void can_parse_dayofyear_functions() {
assertNotNull(parser.parse("SELECT dayofyear('2022-11-18')"));
assertNotNull(parser.parse("SELECT day_of_year('2022-11-18')"));
}

@Test
public void can_parse_multi_match_relevance_function() {
assertNotNull(parser.parse(
Expand Down