Skip to content

Commit

Permalink
Add WEEKDAY Function to SQL Plugin (#1418)
Browse files Browse the repository at this point in the history
* Added Tests



* Added Implementation



* Added Documentation



* Fixed IT Test



* Adjusted IT test



* Fixed Implementation for Time And Unit Tests



* Addressed PR Comments



* Cleaned WeekdayTest File



* Fixed Docs



* Fixed Doctest



* Removed Unneeded Function



---------

Signed-off-by: GabeFernandez310 <[email protected]>
  • Loading branch information
GabeFernandez310 authored Mar 15, 2023
1 parent 7220cfe commit 4f9f5ca
Show file tree
Hide file tree
Showing 9 changed files with 207 additions and 0 deletions.
5 changes: 5 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 @@ -451,6 +451,11 @@ public static FunctionExpression week(
return compile(functionProperties, BuiltinFunctionName.WEEK, expressions);
}

public static FunctionExpression weekday(FunctionProperties functionProperties,
Expression... expressions) {
return compile(functionProperties, BuiltinFunctionName.WEEKDAY, expressions);
}

public static FunctionExpression weekofyear(
FunctionProperties functionProperties, Expression... expressions) {
return compile(functionProperties, BuiltinFunctionName.WEEKOFYEAR, expressions);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(week(BuiltinFunctionName.WEEK));
repository.register(week(BuiltinFunctionName.WEEKOFYEAR));
repository.register(week(BuiltinFunctionName.WEEK_OF_YEAR));
repository.register(weekday());
repository.register(year());
}

Expand Down Expand Up @@ -900,6 +901,19 @@ private DefaultFunctionResolver week(BuiltinFunctionName week) {
);
}

private DefaultFunctionResolver weekday() {
return define(BuiltinFunctionName.WEEKDAY.getName(),
implWithProperties(nullMissingHandlingWithProperties(
(functionProperties, arg) -> new ExprIntegerValue(
formatNow(functionProperties.getQueryStartClock()).getDayOfWeek().getValue() - 1)),
INTEGER, TIME),
impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, DATE),
impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, DATETIME),
impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, TIMESTAMP),
impl(nullMissingHandling(DateTimeFunction::exprWeekday), INTEGER, STRING)
);
}

/**
* YEAR(STRING/DATE/DATETIME/TIMESTAMP). return the year for date (1000-9999).
*/
Expand Down Expand Up @@ -1687,6 +1701,16 @@ private ExprValue exprWeek(ExprValue date, ExprValue mode) {
CalendarLookup.getWeekNumber(mode.integerValue(), date.dateValue()));
}

/**
* Weekday implementation for ExprValue.
*
* @param date ExprValue of Date/Datetime/String/Timstamp type.
* @return ExprValue.
*/
private ExprValue exprWeekday(ExprValue date) {
return new ExprIntegerValue(date.dateValue().getDayOfWeek().getValue() - 1);
}

private ExprValue unixTimeStamp(Clock clock) {
return new ExprLongValue(Instant.now(clock).getEpochSecond());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ public enum BuiltinFunctionName {
UTC_TIMESTAMP(FunctionName.of("utc_timestamp")),
UNIX_TIMESTAMP(FunctionName.of("unix_timestamp")),
WEEK(FunctionName.of("week")),
WEEKDAY(FunctionName.of("weekday")),
WEEKOFYEAR(FunctionName.of("weekofyear")),
WEEK_OF_YEAR(FunctionName.of("week_of_year")),
YEAR(FunctionName.of("year")),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/


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.opensearch.sql.data.model.ExprValueUtils.integerValue;
import static org.opensearch.sql.data.type.ExprCoreType.INTEGER;

import java.time.LocalDate;
import java.util.stream.Stream;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.opensearch.sql.data.model.ExprDateValue;
import org.opensearch.sql.data.model.ExprTimeValue;
import org.opensearch.sql.data.model.ExprValue;
import org.opensearch.sql.exception.SemanticCheckException;
import org.opensearch.sql.expression.DSL;
import org.opensearch.sql.expression.Expression;
import org.opensearch.sql.expression.ExpressionTestBase;
import org.opensearch.sql.expression.FunctionExpression;
import org.opensearch.sql.expression.LiteralExpression;


class WeekdayTest extends ExpressionTestBase {

private void weekdayQuery(
FunctionExpression dateExpression,
int dayOfWeek,
String testExpr) {

assertAll(
() -> assertEquals(INTEGER, dateExpression.type()),
() -> assertEquals(integerValue(dayOfWeek), eval(dateExpression)),
() -> assertEquals(testExpr, dateExpression.toString())
);
}

private static Stream<Arguments> getTestDataForWeekday() {
return Stream.of(
Arguments.of(
DSL.literal(new ExprDateValue("2020-08-07")),
4,
"weekday(DATE '2020-08-07')"),
Arguments.of(
DSL.literal(new ExprDateValue("2020-08-09")),
6,
"weekday(DATE '2020-08-09')"),
Arguments.of(
DSL.literal("2020-08-09"),
6,
"weekday(\"2020-08-09\")"),
Arguments.of(
DSL.literal("2020-08-09 01:02:03"),
6,
"weekday(\"2020-08-09 01:02:03\")")
);
}

@MethodSource("getTestDataForWeekday")
@ParameterizedTest
public void weekday(LiteralExpression arg, int expectedInt, String expectedString) {
FunctionExpression expression = DSL.weekday(
functionProperties,
arg);

weekdayQuery(expression, expectedInt, expectedString);
}

@Test
public void testWeekdayWithTimeType() {
FunctionExpression expression = DSL.weekday(
functionProperties, DSL.literal(new ExprTimeValue("12:23:34")));

assertAll(
() -> assertEquals(INTEGER, eval(expression).type()),
() -> assertEquals((
LocalDate.now(
functionProperties.getQueryStartClock()).getDayOfWeek().getValue() - 1),
eval(expression).integerValue()),
() -> assertEquals("weekday(TIME '12:23:34')", expression.toString())
);
}

private void testInvalidWeekday(String date) {
FunctionExpression expression = DSL.weekday(
functionProperties, DSL.literal(new ExprDateValue(date)));
eval(expression);
}

@Test
public void weekdayLeapYear() {
assertAll(
//Feb. 29 of a leap year
() -> weekdayQuery(DSL.weekday(
functionProperties,
DSL.literal("2020-02-29")), 5, "weekday(\"2020-02-29\")"),
//day after Feb. 29 of a leap year
() -> weekdayQuery(DSL.weekday(
functionProperties,
DSL.literal("2020-03-01")), 6, "weekday(\"2020-03-01\")"),
//Feb. 28 of a non-leap year
() -> weekdayQuery(DSL.weekday(
functionProperties,
DSL.literal("2021-02-28")), 6, "weekday(\"2021-02-28\")"),
//Feb. 29 of a non-leap year
() -> assertThrows(
SemanticCheckException.class, () -> testInvalidWeekday("2021-02-29"))
);
}

@Test
public void weekdayInvalidArgument() {
assertAll(
//40th day of the month
() -> assertThrows(SemanticCheckException.class,
() -> testInvalidWeekday("2021-02-40")),

//13th month of the year
() -> assertThrows(SemanticCheckException.class,
() -> testInvalidWeekday("2021-13-29")),

//incorrect format
() -> assertThrows(SemanticCheckException.class,
() -> testInvalidWeekday("asdfasdf"))
);
}

private ExprValue eval(Expression expression) {
return expression.valueOf();
}
}
24 changes: 24 additions & 0 deletions docs/user/dql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2704,6 +2704,30 @@ Example::
| 7 | 8 |
+----------------------------+-------------------------------+

WEEKDAY
_______

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

Usage: weekday(date) returns the weekday index for date (0 = Monday, 1 = Tuesday, ..., 6 = Sunday).

It is similar to the `dayofweek`_ function, but returns different indexes for each day.

Argument type: STRING/DATE/DATETIME/TIME/TIMESTAMP

Return type: INTEGER

Example::

os> SELECT weekday('2020-08-26'), weekday('2020-08-27')
fetched rows / total rows = 1/1
+-------------------------+-------------------------+
| weekday('2020-08-26') | weekday('2020-08-27') |
|-------------------------+-------------------------|
| 2 | 3 |
+-------------------------+-------------------------+

WEEK_OF_YEAR
------------

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -942,6 +942,12 @@ public void testWeek() throws IOException {
week("2000-01-01", 2, 52, "week");
}

@Test
public void testWeekday() throws IOException {
JSONObject result = executeQuery(String.format("SELECT weekday(date0) FROM %s LIMIT 3", TEST_INDEX_CALCS));
verifyDataRows(result, rows(3), rows(1), rows(2));
}

@Test
public void testWeekOfYearUnderscores() throws IOException {
JSONObject result = executeQuery("select week_of_year(date('2008-02-20'))");
Expand Down
1 change: 1 addition & 0 deletions sql/src/main/antlr/OpenSearchSQLLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,7 @@ TOPHITS: 'TOPHITS';
TYPEOF: 'TYPEOF';
WEEK_OF_YEAR: 'WEEK_OF_YEAR';
WEEKOFYEAR: 'WEEKOFYEAR';
WEEKDAY: 'WEEKDAY';
WILDCARDQUERY: 'WILDCARDQUERY';
WILDCARD_QUERY: 'WILDCARD_QUERY';

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 @@ -480,6 +480,7 @@ dateTimeFunctionName
| TO_DAYS
| UNIX_TIMESTAMP
| WEEK
| WEEKDAY
| WEEK_OF_YEAR
| WEEKOFYEAR
| YEAR
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ private static Stream<Arguments> nowLikeFunctionsData() {
);
}

@Test
public void can_parse_weekday_function() {
assertNotNull(parser.parse("SELECT weekday('2022-11-18')"));
assertNotNull(parser.parse("SELECT day_of_week('2022-11-18')"));
}

@ParameterizedTest(name = "{0}")
@MethodSource("nowLikeFunctionsData")
public void can_parse_now_like_functions(String name, Boolean hasFsp, Boolean hasShortcut) {
Expand Down

0 comments on commit 4f9f5ca

Please sign in to comment.