Skip to content

Commit 1e61486

Browse files
committed
Add position() to V2 engine (#177)
Signed-off-by: Margarit Hakobyan <margarith@bitquilltech.com>
1 parent e2bf254 commit 1e61486

File tree

15 files changed

+319
-23
lines changed

15 files changed

+319
-23
lines changed

core/src/main/java/org/opensearch/sql/analysis/ExpressionAnalyzer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.opensearch.sql.ast.expression.Literal;
3636
import org.opensearch.sql.ast.expression.Not;
3737
import org.opensearch.sql.ast.expression.Or;
38+
import org.opensearch.sql.ast.expression.PositionFunction;
3839
import org.opensearch.sql.ast.expression.QualifiedName;
3940
import org.opensearch.sql.ast.expression.RelevanceFieldList;
4041
import org.opensearch.sql.ast.expression.Span;
@@ -213,6 +214,13 @@ public Expression visitHighlightFunction(HighlightFunction node, AnalysisContext
213214
return new HighlightExpression(expr);
214215
}
215216

217+
@Override
218+
public Expression visitPositionFunction(PositionFunction node, AnalysisContext context) {
219+
Expression stringPatternExpr = node.getStringPatternExpr().accept(this, context);
220+
Expression searchStringExpr = node.getSearchStringExpr().accept(this, context);
221+
return DSL.position(stringPatternExpr, searchStringExpr);
222+
}
223+
216224
@Override
217225
public Expression visitIn(In node, AnalysisContext context) {
218226
return visitIn(node.getField(), node.getValueList(), context);

core/src/main/java/org/opensearch/sql/ast/AbstractNodeVisitor.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.opensearch.sql.ast.expression.Map;
2828
import org.opensearch.sql.ast.expression.Not;
2929
import org.opensearch.sql.ast.expression.Or;
30+
import org.opensearch.sql.ast.expression.PositionFunction;
3031
import org.opensearch.sql.ast.expression.QualifiedName;
3132
import org.opensearch.sql.ast.expression.RelevanceFieldList;
3233
import org.opensearch.sql.ast.expression.Span;
@@ -278,6 +279,10 @@ public T visitHighlightFunction(HighlightFunction node, C context) {
278279
return visitChildren(node, context);
279280
}
280281

282+
public T visitPositionFunction(PositionFunction node, C context) {
283+
return visitChildren(node, context);
284+
}
285+
281286
public T visitStatement(Statement node, C context) {
282287
return visit(node, context);
283288
}

core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.opensearch.sql.ast.expression.Not;
3434
import org.opensearch.sql.ast.expression.Or;
3535
import org.opensearch.sql.ast.expression.ParseMethod;
36+
import org.opensearch.sql.ast.expression.PositionFunction;
3637
import org.opensearch.sql.ast.expression.QualifiedName;
3738
import org.opensearch.sql.ast.expression.Span;
3839
import org.opensearch.sql.ast.expression.SpanUnit;
@@ -288,6 +289,11 @@ public UnresolvedExpression highlight(UnresolvedExpression fieldName,
288289
return new HighlightFunction(fieldName, arguments);
289290
}
290291

292+
public UnresolvedExpression position(UnresolvedExpression stringPatternExpr,
293+
UnresolvedExpression searchStringExpr) {
294+
return new PositionFunction(stringPatternExpr, searchStringExpr);
295+
}
296+
291297
public UnresolvedExpression window(UnresolvedExpression function,
292298
List<UnresolvedExpression> partitionByList,
293299
List<Pair<SortOption, UnresolvedExpression>> sortList) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright OpenSearch Contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package org.opensearch.sql.ast.expression;
7+
8+
import java.util.Arrays;
9+
import java.util.List;
10+
import lombok.AllArgsConstructor;
11+
import lombok.EqualsAndHashCode;
12+
import lombok.Getter;
13+
import lombok.ToString;
14+
import org.opensearch.sql.ast.AbstractNodeVisitor;
15+
16+
17+
/**
18+
* Expression node of Position function.
19+
*/
20+
@AllArgsConstructor
21+
@EqualsAndHashCode(callSuper = false)
22+
@Getter
23+
@ToString
24+
public class PositionFunction extends UnresolvedExpression {
25+
@Getter
26+
private UnresolvedExpression stringPatternExpr;
27+
@Getter
28+
private UnresolvedExpression searchStringExpr;
29+
30+
@Override
31+
public List<UnresolvedExpression> getChild() {
32+
return Arrays.asList(stringPatternExpr, searchStringExpr);
33+
}
34+
35+
@Override
36+
public <R, C> R accept(AbstractNodeVisitor<R, C> nodeVisitor, C context) {
37+
return nodeVisitor.visitPositionFunction(this, context);
38+
}
39+
}

core/src/main/java/org/opensearch/sql/expression/DSL.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,10 @@ public static FunctionExpression cbrt(Expression... expressions) {
229229
return compile(BuiltinFunctionName.CBRT, expressions);
230230
}
231231

232+
public static FunctionExpression position(Expression... expressions) {
233+
return compile(BuiltinFunctionName.POSITION, expressions);
234+
}
235+
232236
public static FunctionExpression truncate(Expression... expressions) {
233237
return compile(BuiltinFunctionName.TRUNCATE, expressions);
234238
}

core/src/main/java/org/opensearch/sql/expression/function/BuiltinFunctionName.java

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -153,23 +153,24 @@ public enum BuiltinFunctionName {
153153
/**
154154
* Text Functions.
155155
*/
156-
SUBSTR(FunctionName.of("substr")),
157-
SUBSTRING(FunctionName.of("substring")),
158-
RTRIM(FunctionName.of("rtrim")),
159-
LTRIM(FunctionName.of("ltrim")),
160-
TRIM(FunctionName.of("trim")),
161-
UPPER(FunctionName.of("upper")),
162-
LOWER(FunctionName.of("lower")),
163-
REGEXP(FunctionName.of("regexp")),
156+
ASCII(FunctionName.of("ascii")),
164157
CONCAT(FunctionName.of("concat")),
165158
CONCAT_WS(FunctionName.of("concat_ws")),
166-
LENGTH(FunctionName.of("length")),
167-
STRCMP(FunctionName.of("strcmp")),
168-
RIGHT(FunctionName.of("right")),
169159
LEFT(FunctionName.of("left")),
170-
ASCII(FunctionName.of("ascii")),
160+
LENGTH(FunctionName.of("length")),
171161
LOCATE(FunctionName.of("locate")),
162+
LOWER(FunctionName.of("lower")),
163+
LTRIM(FunctionName.of("ltrim")),
164+
POSITION(FunctionName.of("position")),
165+
REGEXP(FunctionName.of("regexp")),
172166
REPLACE(FunctionName.of("replace")),
167+
RIGHT(FunctionName.of("right")),
168+
RTRIM(FunctionName.of("rtrim")),
169+
STRCMP(FunctionName.of("strcmp")),
170+
SUBSTR(FunctionName.of("substr")),
171+
SUBSTRING(FunctionName.of("substring")),
172+
TRIM(FunctionName.of("trim")),
173+
UPPER(FunctionName.of("upper")),
173174

174175
/**
175176
* NULL Test.

core/src/main/java/org/opensearch/sql/expression/text/TextFunction.java

Lines changed: 28 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -39,22 +39,23 @@ public class TextFunction {
3939
* @param repository {@link BuiltinFunctionRepository}.
4040
*/
4141
public void register(BuiltinFunctionRepository repository) {
42-
repository.register(substr());
43-
repository.register(substring());
44-
repository.register(ltrim());
45-
repository.register(rtrim());
46-
repository.register(trim());
47-
repository.register(lower());
48-
repository.register(upper());
42+
repository.register(ascii());
4943
repository.register(concat());
5044
repository.register(concat_ws());
51-
repository.register(length());
52-
repository.register(strcmp());
53-
repository.register(right());
5445
repository.register(left());
55-
repository.register(ascii());
46+
repository.register(length());
5647
repository.register(locate());
48+
repository.register(lower());
49+
repository.register(ltrim());
50+
repository.register(position());
5751
repository.register(replace());
52+
repository.register(right());
53+
repository.register(rtrim());
54+
repository.register(strcmp());
55+
repository.register(substr());
56+
repository.register(substring());
57+
repository.register(trim());
58+
repository.register(upper());
5859
}
5960

6061
/**
@@ -241,6 +242,18 @@ private DefaultFunctionResolver locate() {
241242
TextFunction::exprLocate), INTEGER, STRING, STRING, INTEGER));
242243
}
243244

245+
/**
246+
* Returns the position of the first occurrence of a substring in a string starting from 1.
247+
* Returns 0 if substring is not in string.
248+
* Returns NULL if any argument is NULL.
249+
* Supports following signature:
250+
* (STRING IN STRING) -> INTEGER
251+
*/
252+
private DefaultFunctionResolver position() {
253+
return define(BuiltinFunctionName.POSITION.getName(),
254+
impl(nullMissingHandling(TextFunction::exprPosition), INTEGER, STRING, STRING));
255+
}
256+
244257
/**
245258
* REPLACE(str, from_str, to_str) returns the string str with all occurrences of
246259
* the string from_str replaced by the string to_str.
@@ -313,6 +326,10 @@ private static ExprValue exprLocate(ExprValue subStr, ExprValue str, ExprValue p
313326
str.stringValue().indexOf(subStr.stringValue(), pos.integerValue() - 1) + 1);
314327
}
315328

329+
private static ExprValue exprPosition(ExprValue subStr, ExprValue str) {
330+
return exprLocate(subStr, str);
331+
}
332+
316333
private static ExprValue exprReplace(ExprValue str, ExprValue from, ExprValue to) {
317334
return new ExprStringValue(str.stringValue().replaceAll(from.stringValue(), to.stringValue()));
318335
}

core/src/test/java/org/opensearch/sql/analysis/NamedExpressionAnalyzerTest.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.opensearch.sql.ast.expression.Alias;
1717
import org.opensearch.sql.ast.expression.HighlightFunction;
1818
import org.opensearch.sql.ast.expression.Literal;
19+
import org.opensearch.sql.ast.expression.PositionFunction;
1920
import org.opensearch.sql.expression.NamedExpression;
2021
import org.springframework.context.annotation.Configuration;
2122
import org.springframework.test.context.ContextConfiguration;
@@ -48,4 +49,14 @@ void visit_highlight() {
4849
NamedExpression analyze = analyzer.analyze(alias, analysisContext);
4950
assertEquals("highlight(fieldA)", analyze.getNameOrAlias());
5051
}
52+
53+
@Test
54+
void visit_position() {
55+
Alias alias = AstDSL.alias("position(fieldA IN fieldB)",
56+
new PositionFunction(AstDSL.stringLiteral("fieldA"), AstDSL.stringLiteral("fieldB")));
57+
NamedExpressionAnalyzer analyzer = new NamedExpressionAnalyzer(expressionAnalyzer);
58+
59+
NamedExpression analyze = analyzer.analyze(alias, analysisContext);
60+
assertEquals("position(fieldA IN fieldB)", analyze.getNameOrAlias());
61+
}
5162
}

core/src/test/java/org/opensearch/sql/expression/text/TextFunctionTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,26 @@ void locate() {
377377
DSL.locate(missingRef, DSL.literal("hello"), DSL.literal(1))));
378378
}
379379

380+
@Test
381+
void position() {
382+
FunctionExpression expression = DSL.position(
383+
DSL.literal("world"),
384+
DSL.literal("helloworldworld"));
385+
assertEquals(INTEGER, expression.type());
386+
assertEquals(6, eval(expression).integerValue());
387+
388+
expression = DSL.position(
389+
DSL.literal("abc"),
390+
DSL.literal("hello world"));
391+
assertEquals(INTEGER, expression.type());
392+
assertEquals(0, eval(expression).integerValue());
393+
394+
when(nullRef.type()).thenReturn(STRING);
395+
assertEquals(nullValue(), eval(DSL.position(nullRef, DSL.literal("hello"))));
396+
when(missingRef.type()).thenReturn(STRING);
397+
assertEquals(missingValue(), eval(DSL.position(missingRef, DSL.literal("hello"))));
398+
}
399+
380400
@Test
381401
void replace() {
382402
FunctionExpression expression = DSL.replace(

docs/user/dql/functions.rst

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2330,6 +2330,31 @@ Example::
23302330
+---------------------+---------------------+
23312331

23322332

2333+
POSITION
2334+
------
2335+
2336+
Description
2337+
>>>>>>>>>>>
2338+
2339+
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.
2340+
2341+
Argument type: STRING, STRING, INTEGER
2342+
2343+
Return type integer:
2344+
2345+
(STRING IN STRING) -> INTEGER
2346+
2347+
Example::
2348+
2349+
os> SELECT POSITION('world' IN 'helloworld')
2350+
fetched rows / total rows = 1/1
2351+
+-------------------------------------+---------------------------------------+
2352+
| POSITION('world' IN 'helloworld') | POSITION('invalid' IN 'helloworld') |
2353+
|-------------------------------------+---------------------------------------|
2354+
| 6 | 0 |
2355+
+-------------------------------------+---------------------------------------+
2356+
2357+
23332358
REPLACE
23342359
-------
23352360

0 commit comments

Comments
 (0)