Skip to content

Commit f530770

Browse files
authored
Add position() string function to PPL (#1147)
* Add position() string function to PPL (#184) Signed-off-by: Margarit Hakobyan <[email protected]>
1 parent 354e843 commit f530770

File tree

6 files changed

+160
-0
lines changed

6 files changed

+160
-0
lines changed

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

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)