Skip to content

Commit

Permalink
Add Day_Of_Year Function To OpenSearch
Browse files Browse the repository at this point in the history
Signed-off-by: GabeFernandez310 <gabrielf@bitquilltech.com>
  • Loading branch information
GabeFernandez310 committed Dec 1, 2022
1 parent fef20f8 commit a76edcf
Show file tree
Hide file tree
Showing 8 changed files with 214 additions and 4 deletions.
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 @@ -325,6 +325,10 @@ public static FunctionExpression dayofyear(Expression... expressions) {
return compile(BuiltinFunctionName.DAYOFYEAR, expressions);
}

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

public static FunctionExpression from_days(Expression... expressions) {
return compile(BuiltinFunctionName.FROM_DAYS, expressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,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 @@ -319,8 +320,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 @@ -68,6 +68,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 @@ -1443,6 +1443,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 @@ -1458,6 +1459,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
2 changes: 1 addition & 1 deletion sql/src/main/antlr/OpenSearchSQLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,7 @@ trigonometricFunctionName

dateTimeFunctionName
: ADDDATE | CONVERT_TZ | DATE | DATE_ADD | DATE_FORMAT | DATE_SUB
| DATETIME | DAY | DAYNAME | DAYOFMONTH | DAYOFWEEK | DAYOFYEAR | FROM_DAYS | FROM_UNIXTIME
| DATETIME | DAY | DAYNAME | DAYOFMONTH | DAYOFWEEK | DAYOFYEAR | DAY_OF_YEAR | FROM_DAYS | FROM_UNIXTIME
| HOUR | MAKEDATE | MAKETIME | MICROSECOND | MINUTE | MONTH | MONTHNAME | PERIOD_ADD
| PERIOD_DIFF | QUARTER | SECOND | SUBDATE | SYSDATE | TIME | TIME_TO_SEC
| TIMESTAMP | TO_DAYS | UNIX_TIMESTAMP | WEEK | YEAR
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

0 comments on commit a76edcf

Please sign in to comment.