Skip to content

Commit

Permalink
Add position() string function to PPL (#1147)
Browse files Browse the repository at this point in the history
* Add position() string function to PPL (#184)

Signed-off-by: Margarit Hakobyan <[email protected]>
  • Loading branch information
margarit-h authored Dec 7, 2022
1 parent 354e843 commit f530770
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 0 deletions.
25 changes: 25 additions & 0 deletions docs/user/ppl/functions/string.rst
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,31 @@ Example::
+---------------------+---------------------+


POSITION
------

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

Usage: The syntax POSITION(substr IN str) returns the position of the first occurrence of substring substr in string str. Returns 0 if substr is not in str. Returns NULL if any argument is NULL.

Argument type: STRING, STRING

Return type INTEGER

(STRING IN STRING) -> INTEGER

Example::

os> source=people | eval `POSITION('world' IN 'helloworld')` = POSITION('world' IN 'helloworld'), `POSITION('invalid' IN 'helloworld')`= POSITION('invalid' IN 'helloworld') | fields `POSITION('world' IN 'helloworld')`, `POSITION('invalid' IN 'helloworld')`
fetched rows / total rows = 1/1
+-------------------------------------+---------------------------------------+
| POSITION('world' IN 'helloworld') | POSITION('invalid' IN 'helloworld') |
|-------------------------------------+---------------------------------------|
| 6 | 0 |
+-------------------------------------+---------------------------------------+


RIGHT
-----

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ppl;

import org.junit.Test;

import java.io.IOException;

import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_CALCS;
import static org.opensearch.sql.util.MatcherUtils.rows;
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;

public class PositionFunctionIT extends PPLIntegTestCase {
@Override
public void init() throws IOException {
loadIndex(Index.CALCS);
}

@Test
public void test_position_function() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | eval f=position('ON', str1) | fields f";

var result = executeQuery(query);

assertEquals(17, result.getInt("total"));
verifyDataRows(result,
rows(7), rows(7),
rows(2), rows(0),
rows(0), rows(0),
rows(0), rows(0),
rows(0), rows(0),
rows(0), rows(0),
rows(0), rows(0),
rows(0), rows(0),
rows(0));
}

@Test
public void test_position_function_with_fields_only() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | eval f=position(str3 IN str2) | where str2 IN ('one', 'two', 'three')| fields f";

var result = executeQuery(query);

assertEquals(3, result.getInt("total"));
verifyDataRows(result, rows(3), rows(0), rows(4));
}

@Test
public void test_position_function_with_string_literals() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | eval f=position('world' IN 'hello world') | where str2='one' | fields f";

var result = executeQuery(query);

assertEquals(1, result.getInt("total"));
verifyDataRows(result, rows(7));
}

@Test
public void test_position_function_with_nulls() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | eval f=position('ee' IN str2) | where isnull(str2) | fields str2,f";

var result = executeQuery(query);

assertEquals(4, result.getInt("total"));
verifyDataRows(result,
rows(null, null),
rows(null, null),
rows(null, null),
rows(null, null));
}

@Test
public void test_position_function_with_function_as_arg() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | eval f=position(upper(str3) IN str1) | where like(str1, 'BINDING SUPPLIES') | fields f";

var result = executeQuery(query);

assertEquals(1, result.getInt("total"));
verifyDataRows(result, rows(15));
}

@Test
public void test_position_function_with_function_in_where_clause() throws IOException {
String query = "source=" + TEST_INDEX_CALCS
+ " | where position(str3 IN str2)=1 | fields str2";

var result = executeQuery(query);

assertEquals(2, result.getInt("total"));
verifyDataRows(result, rows("eight"), rows("eleven"));
}
}
1 change: 1 addition & 0 deletions ppl/src/main/antlr/OpenSearchPPLLexer.g4
Original file line number Diff line number Diff line change
Expand Up @@ -215,6 +215,7 @@ LOG10: 'LOG10';
LOG2: 'LOG2';
MOD: 'MOD';
PI: 'PI';
POSITION: 'POSITION';
POW: 'POW';
POWER: 'POWER';
RAND: 'RAND';
Expand Down
11 changes: 11 additions & 0 deletions ppl/src/main/antlr/OpenSearchPPLParser.g4
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ valueExpression
| LT_PRTHS left=valueExpression binaryOperator
right=valueExpression RT_PRTHS #parentheticBinaryArithmetic
| primaryExpression #valueExpressionDefault
| positionFunction #positionFunctionCall
;

primaryExpression
Expand All @@ -267,6 +268,10 @@ primaryExpression
| literalValue
;

positionFunction
: positionFunctionName LT_PRTHS functionArg IN functionArg RT_PRTHS
;

booleanExpression
: booleanFunctionCall
;
Expand Down Expand Up @@ -362,6 +367,7 @@ evalFunctionName
| textFunctionBase
| conditionFunctionBase
| systemFunctionBase
| positionFunctionName
;

functionArgs
Expand Down Expand Up @@ -484,6 +490,10 @@ textFunctionBase
| RIGHT | LEFT | ASCII | LOCATE | REPLACE
;

positionFunctionName
: POSITION
;

/** operators */
comparisonOperator
: EQUAL | NOT_EQUAL | LESS | NOT_LESS | GREATER | NOT_GREATER | REGEXP
Expand Down Expand Up @@ -603,4 +613,5 @@ keywordsCanBeId
| dateAndTimeFunctionBase
| textFunctionBase
| mathematicalFunctionBase
| positionFunctionName
;
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import static org.opensearch.sql.ast.dsl.AstDSL.qualifiedName;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NULL;
import static org.opensearch.sql.expression.function.BuiltinFunctionName.POSITION;
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BinaryArithmeticContext;
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BooleanFunctionCallContext;
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BooleanLiteralContext;
Expand Down Expand Up @@ -290,6 +291,15 @@ public UnresolvedExpression visitTableSource(TableSourceContext ctx) {
}
}

@Override
public UnresolvedExpression visitPositionFunction(
OpenSearchPPLParser.PositionFunctionContext ctx) {
return new Function(
POSITION.getName().getFunctionName(),
Arrays.asList(visitFunctionArg(ctx.functionArg(0)),
visitFunctionArg(ctx.functionArg(1))));
}

/**
* Literal and value.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,19 @@ public void testEvalFunctionExprNoArgs() {
));
}

@Test
public void testPositionFunctionExpr() {
assertEqual("source=t | eval f=position('substr' IN 'str')",
eval(
relation("t"),
let(
field("f"),
function("position",
stringLiteral("substr"), stringLiteral("str"))
)
));
}

@Test
public void testEvalBinaryOperationExpr() {
assertEqual("source=t | eval f=a+b",
Expand Down

0 comments on commit f530770

Please sign in to comment.