Skip to content

Commit

Permalink
Add reverse() string function to V2 SQL Engine(#1154)
Browse files Browse the repository at this point in the history
* Add reverse() string function to V2 SQL engine

Signed-off-by: Margarit Hakobyan <margarith@bitquilltech.com>
  • Loading branch information
margarit-h committed Dec 9, 2022
1 parent 2af7321 commit c923e80
Show file tree
Hide file tree
Showing 12 changed files with 102 additions and 2 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 @@ -486,6 +486,10 @@ public static FunctionExpression replace(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.REPLACE, expressions);
}

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

public static FunctionExpression and(Expression... expressions) {
return compile(FunctionProperties.None, BuiltinFunctionName.AND, expressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,7 @@ public enum BuiltinFunctionName {
POSITION(FunctionName.of("position")),
REGEXP(FunctionName.of("regexp")),
REPLACE(FunctionName.of("replace")),
REVERSE(FunctionName.of("reverse")),
RIGHT(FunctionName.of("right")),
RTRIM(FunctionName.of("rtrim")),
STRCMP(FunctionName.of("strcmp")),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ public void register(BuiltinFunctionRepository repository) {
repository.register(ltrim());
repository.register(position());
repository.register(replace());
repository.register(reverse());
repository.register(right());
repository.register(rtrim());
repository.register(strcmp());
Expand Down Expand Up @@ -268,6 +269,17 @@ private DefaultFunctionResolver replace() {
impl(nullMissingHandling(TextFunction::exprReplace), STRING, STRING, STRING, STRING));
}

/**
* REVERSE(str) returns reversed string of the string supplied as an argument
* Returns NULL if the argument is NULL.
* Supports the following signature:
* (STRING) -> STRING
*/
private DefaultFunctionResolver reverse() {
return define(BuiltinFunctionName.REVERSE.getName(),
impl(nullMissingHandling(TextFunction::exprReverse), STRING, STRING));
}

private static ExprValue exprSubstrStart(ExprValue exprValue, ExprValue start) {
int startIdx = start.integerValue();
if (startIdx == 0) {
Expand Down Expand Up @@ -331,5 +343,9 @@ private static ExprValue exprLocate(ExprValue subStr, ExprValue str, ExprValue p
private static ExprValue exprReplace(ExprValue str, ExprValue from, ExprValue to) {
return new ExprStringValue(str.stringValue().replaceAll(from.stringValue(), to.stringValue()));
}

private static ExprValue exprReverse(ExprValue str) {
return new ExprStringValue(new StringBuilder(str.stringValue()).reverse().toString());
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,18 @@ void replace() {
assertEquals(missingValue(), eval(DSL.replace(missingRef, DSL.literal("a"), DSL.literal("b"))));
}

@Test
void reverse() {
FunctionExpression expression = DSL.reverse(DSL.literal("abcde"));
assertEquals(STRING, expression.type());
assertEquals("edcba", eval(expression).stringValue());

when(nullRef.type()).thenReturn(STRING);
assertEquals(nullValue(), eval(DSL.reverse(nullRef)));
when(missingRef.type()).thenReturn(STRING);
assertEquals(missingValue(), eval(DSL.reverse(missingRef)));
}

void testConcatString(List<String> strings) {
String expected = null;
if (strings.stream().noneMatch(Objects::isNull)) {
Expand Down
23 changes: 23 additions & 0 deletions docs/user/dql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2468,6 +2468,29 @@ Example::
+--------------------------------------------------+


REVERSE
-------

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

Usage: REVERSE(str) returns reversed string of the string supplied as an argument. Returns NULL if the argument is NULL.

Argument type: STRING

Return type: STRING

Example::

os> SELECT REVERSE('abcde'), REVERSE(null)
fetched rows / total rows = 1/1
+--------------------+-----------------+
| REVERSE('abcde') | REVERSE(null) |
|--------------------+-----------------|
| edcba | null |
+--------------------+-----------------+


RIGHT
-----

Expand Down
23 changes: 23 additions & 0 deletions docs/user/ppl/functions/string.rst
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,29 @@ Example::
+-------------------------------------+---------------------------------------+


REVERSE
-----

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

Usage: REVERSE(str) returns reversed string of the string supplied as an argument.

Argument type: STRING

Return type: STRING

Example::

os> source=people | eval `REVERSE('abcde')` = REVERSE('abcde') | fields `REVERSE('abcde')`
fetched rows / total rows = 1/1
+--------------------+
| REVERSE('abcde') |
|--------------------|
| edcba |
+--------------------+


RIGHT
-----

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,4 +139,9 @@ public void testLocate() throws IOException {
public void testReplace() throws IOException {
verifyQuery("replace", "", ", 'world', ' opensearch'", "hello", " opensearch", "hello opensearch");
}

@Test
public void testReverse() throws IOException {
verifyQuery("reverse", "", "", "olleh", "dlrow", "dlrowolleh");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,26 @@ void verifyQuery(String query, String type, Integer output) throws IOException {
verifyDataRows(result, rows(output));
}

void verifyQueryWithNullOutput(String query, String type) throws IOException {
JSONObject result = executeQuery("select 'test null'," + query);
verifySchema(result, schema(query, null, type),
schema("'test null'", null, type));
verifyDataRows(result, rows("test null", null));
}

@Test
public void testRegexp() throws IOException {
verifyQuery("'a' regexp 'b'", "integer", 0);
verifyQuery("'a' regexp '.*'", "integer", 1);
}

@Test
public void testReverse() throws IOException {
verifyQuery("reverse('hello')", "keyword", "olleh");
verifyQuery("reverse('')", "keyword", "");
verifyQueryWithNullOutput("reverse(null)", "keyword");
}

@Test
public void testSubstr() throws IOException {
verifyQuery("substr('hello', 2)", "keyword", "ello");
Expand Down
1 change: 1 addition & 0 deletions ppl/src/main/antlr/OpenSearchPPLLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,7 @@ LEFT: 'LEFT';
ASCII: 'ASCII';
LOCATE: 'LOCATE';
REPLACE: 'REPLACE';
REVERSE: 'REVERSE';
CAST: 'CAST';

// BOOL FUNCTIONS
Expand Down
2 changes: 1 addition & 1 deletion ppl/src/main/antlr/OpenSearchPPLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ systemFunctionBase

textFunctionBase
: SUBSTR | SUBSTRING | TRIM | LTRIM | RTRIM | LOWER | UPPER | CONCAT | CONCAT_WS | LENGTH | STRCMP
| RIGHT | LEFT | ASCII | LOCATE | REPLACE
| RIGHT | LEFT | ASCII | LOCATE | REPLACE | REVERSE
;

positionFunctionName
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 @@ -243,6 +243,7 @@ REPLACE: 'REPLACE';
RINT: 'RINT';
ROUND: 'ROUND';
RTRIM: 'RTRIM';
REVERSE: 'REVERSE';
SIGN: 'SIGN';
SIGNUM: 'SIGNUM';
SIN: 'SIN';
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 @@ -454,7 +454,7 @@ dateTimeFunctionName
textFunctionName
: SUBSTR | SUBSTRING | TRIM | LTRIM | RTRIM | LOWER | UPPER
| CONCAT | CONCAT_WS | SUBSTR | LENGTH | STRCMP | RIGHT | LEFT
| ASCII | LOCATE | REPLACE
| ASCII | LOCATE | REPLACE | REVERSE
;

flowControlFunctionName
Expand Down

0 comments on commit c923e80

Please sign in to comment.