Skip to content

Commit f1be1c0

Browse files
authored
Add position() function to PPL (#179)
Signed-off-by: Margarit Hakobyan <margarith@bitquilltech.com>
1 parent c276efc commit f1be1c0

File tree

8 files changed

+166
-4
lines changed

8 files changed

+166
-4
lines changed

docs/user/dql/functions.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE
219219

220220
Example::
221221

222-
opensearchsql> SELECT CBRT(8), CBRT(9.261), CBRT(-27);
222+
os> SELECT CBRT(8), CBRT(9.261), CBRT(-27)
223223
fetched rows / total rows = 1/1
224224
+-----------+---------------+-------------+
225225
| CBRT(8) | CBRT(9.261) | CBRT(-27) |

docs/user/ppl/functions/math.rst

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -678,11 +678,10 @@ INTEGER/LONG/FLOAT/DOUBLE -> DOUBLE
678678

679679
Example::
680680

681-
opensearchsql> source=location | eval `CBRT(8)` = CBRT(8), `CBRT(9.261)` = CBRT(9.261), `CBRT(-27)` = CBRT(-27) | fields `CBRT(8)`, `CBRT(9.261)`, `CBRT(-27)`;
682-
fetched rows / total rows = 2/2
681+
os> source=people | eval `CBRT(8)` = CBRT(8), `CBRT(9.261)` = CBRT(9.261), `CBRT(-27)` = CBRT(-27) | fields `CBRT(8)`, `CBRT(9.261)`, `CBRT(-27)`
682+
fetched rows / total rows = 1/1
683683
+-----------+---------------+-------------+
684684
| CBRT(8) | CBRT(9.261) | CBRT(-27) |
685685
|-----------+---------------+-------------|
686686
| 2.0 | 2.1 | -3.0 |
687-
| 2.0 | 2.1 | -3.0 |
688687
+-----------+---------------+-------------+

docs/user/ppl/functions/string.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,6 +150,31 @@ Example::
150150
+---------------------+---------------------+
151151

152152

153+
POSITION
154+
------
155+
156+
Description
157+
>>>>>>>>>>>
158+
159+
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`.
160+
161+
Argument type: STRING, STRING
162+
163+
Return type integer:
164+
165+
(STRING IN STRING) -> INTEGER
166+
167+
Example::
168+
169+
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')`
170+
fetched rows / total rows = 1/1
171+
+-------------------------------------+---------------------------------------+
172+
| POSITION('world' IN 'helloworld') | POSITION('invalid' IN 'helloworld') |
173+
|-------------------------------------+---------------------------------------|
174+
| 6 | 0 |
175+
+-------------------------------------+---------------------------------------+
176+
177+
153178
RIGHT
154179
-----
155180

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ppl;
7+
8+
import org.junit.Test;
9+
10+
import java.io.IOException;
11+
12+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_BEER;
13+
import static org.opensearch.sql.legacy.TestsConstants.TEST_INDEX_CALCS;
14+
import static org.opensearch.sql.util.MatcherUtils.rows;
15+
import static org.opensearch.sql.util.MatcherUtils.schema;
16+
import static org.opensearch.sql.util.MatcherUtils.verifyDataRows;
17+
import static org.opensearch.sql.util.MatcherUtils.verifySchema;
18+
19+
public class PositionFunctionIT extends PPLIntegTestCase {
20+
@Override
21+
public void init() throws IOException {
22+
loadIndex(Index.CALCS);
23+
}
24+
25+
@Test
26+
public void test_position_function() throws IOException {
27+
String query = "source=" + TEST_INDEX_CALCS
28+
+ " | eval f=position('ON', str1) | fields f";
29+
30+
var result = executeQuery(query);
31+
32+
assertEquals(17, result.getInt("total"));
33+
verifyDataRows(result,
34+
rows(7), rows(7),
35+
rows(2), rows(0),
36+
rows(0), rows(0),
37+
rows(0), rows(0),
38+
rows(0), rows(0),
39+
rows(0), rows(0),
40+
rows(0), rows(0),
41+
rows(0), rows(0),
42+
rows(0));
43+
}
44+
45+
@Test
46+
public void test_position_function_with_fields_only() throws IOException {
47+
String query = "source=" + TEST_INDEX_CALCS
48+
+ " | eval f=position(str3 IN str2) | where str2 IN ('one', 'two', 'three')| fields f";
49+
50+
var result = executeQuery(query);
51+
52+
assertEquals(3, result.getInt("total"));
53+
verifyDataRows(result, rows(3), rows(0), rows(4));
54+
}
55+
56+
@Test
57+
public void test_position_function_with_string_literals() throws IOException {
58+
String query = "source=" + TEST_INDEX_CALCS
59+
+ " | eval f=position('world' IN 'hello world') | where str2='one' | fields f";
60+
61+
var result = executeQuery(query);
62+
63+
assertEquals(1, result.getInt("total"));
64+
verifyDataRows(result, rows(7));
65+
}
66+
67+
@Test
68+
public void test_position_function_with_nulls() throws IOException {
69+
String query = "source=" + TEST_INDEX_CALCS
70+
+ " | eval f=position('ee' IN str2) | where isnull(str2) | fields str2,f";
71+
72+
var result = executeQuery(query);
73+
74+
assertEquals(4, result.getInt("total"));
75+
verifyDataRows(result,
76+
rows(null, null),
77+
rows(null, null),
78+
rows(null, null),
79+
rows(null, null));
80+
}
81+
82+
@Test
83+
public void test_position_function_with_function_as_arg() throws IOException {
84+
String query = "source=" + TEST_INDEX_CALCS
85+
+ " | eval f=position(upper(str3) IN str1) | where like(str1, 'BINDING SUPPLIES') | fields f";
86+
87+
var result = executeQuery(query);
88+
89+
assertEquals(1, result.getInt("total"));
90+
verifyDataRows(result, rows(15));
91+
}
92+
93+
@Test
94+
public void test_position_function_with_function_in_where_clause() throws IOException {
95+
String query = "source=" + TEST_INDEX_CALCS
96+
+ " | where position(str3 IN str2)=1 | fields str2";
97+
98+
var result = executeQuery(query);
99+
100+
assertEquals(2, result.getInt("total"));
101+
verifyDataRows(result, rows("eight"), rows("eleven"));
102+
}
103+
}

ppl/src/main/antlr/OpenSearchPPLLexer.g4

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ LOG10: 'LOG10';
215215
LOG2: 'LOG2';
216216
MOD: 'MOD';
217217
PI: 'PI';
218+
POSITION: 'POSITION';
218219
POW: 'POW';
219220
POWER: 'POWER';
220221
RAND: 'RAND';

ppl/src/main/antlr/OpenSearchPPLParser.g4

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,7 @@ valueExpression
258258
| LT_PRTHS left=valueExpression binaryOperator
259259
right=valueExpression RT_PRTHS #parentheticBinaryArithmetic
260260
| primaryExpression #valueExpressionDefault
261+
| positionFunction #positionFunctionCall
261262
;
262263

263264
primaryExpression
@@ -267,6 +268,10 @@ primaryExpression
267268
| literalValue
268269
;
269270

271+
positionFunction
272+
: positionFunctionName LT_PRTHS functionArg IN functionArg RT_PRTHS
273+
;
274+
270275
booleanExpression
271276
: booleanFunctionCall
272277
;
@@ -362,6 +367,7 @@ evalFunctionName
362367
| textFunctionBase
363368
| conditionFunctionBase
364369
| systemFunctionBase
370+
| positionFunctionName
365371
;
366372

367373
functionArgs
@@ -484,6 +490,10 @@ textFunctionBase
484490
| RIGHT | LEFT | ASCII | LOCATE | REPLACE
485491
;
486492

493+
positionFunctionName
494+
: POSITION
495+
;
496+
487497
/** operators */
488498
comparisonOperator
489499
: EQUAL | NOT_EQUAL | LESS | NOT_LESS | GREATER | NOT_GREATER | REGEXP
@@ -603,4 +613,5 @@ keywordsCanBeId
603613
| dateAndTimeFunctionBase
604614
| textFunctionBase
605615
| mathematicalFunctionBase
616+
| positionFunctionName
606617
;

ppl/src/main/java/org/opensearch/sql/ppl/parser/AstExpressionBuilder.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import static org.opensearch.sql.ast.dsl.AstDSL.qualifiedName;
1010
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NOT_NULL;
1111
import static org.opensearch.sql.expression.function.BuiltinFunctionName.IS_NULL;
12+
import static org.opensearch.sql.expression.function.BuiltinFunctionName.POSITION;
1213
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BinaryArithmeticContext;
1314
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BooleanFunctionCallContext;
1415
import static org.opensearch.sql.ppl.antlr.parser.OpenSearchPPLParser.BooleanLiteralContext;
@@ -290,6 +291,15 @@ public UnresolvedExpression visitTableSource(TableSourceContext ctx) {
290291
}
291292
}
292293

294+
@Override
295+
public UnresolvedExpression visitPositionFunction(
296+
OpenSearchPPLParser.PositionFunctionContext ctx) {
297+
return new Function(
298+
POSITION.getName().getFunctionName(),
299+
Arrays.asList(visitFunctionArg(ctx.functionArg(0)),
300+
visitFunctionArg(ctx.functionArg(1))));
301+
}
302+
293303
/**
294304
* Literal and value.
295305
*/

ppl/src/test/java/org/opensearch/sql/ppl/parser/AstExpressionBuilderTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,19 @@ public void testEvalFunctionExprNoArgs() {
181181
));
182182
}
183183

184+
@Test
185+
public void testPositionFunctionExpr() {
186+
assertEqual("source=t | eval f=position('substr' IN 'str')",
187+
eval(
188+
relation("t"),
189+
let(
190+
field("f"),
191+
function("position",
192+
stringLiteral("substr"), stringLiteral("str"))
193+
)
194+
));
195+
}
196+
184197
@Test
185198
public void testEvalBinaryOperationExpr() {
186199
assertEqual("source=t | eval f=a+b",

0 commit comments

Comments
 (0)