Skip to content

Commit

Permalink
Add position() to V2 engine (#177)
Browse files Browse the repository at this point in the history
Signed-off-by: Margarit Hakobyan <[email protected]>
  • Loading branch information
margarit-h authored Nov 30, 2022
1 parent cfb2650 commit 2964853
Show file tree
Hide file tree
Showing 15 changed files with 319 additions and 23 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.opensearch.sql.ast.expression.Literal;
import org.opensearch.sql.ast.expression.Not;
import org.opensearch.sql.ast.expression.Or;
import org.opensearch.sql.ast.expression.PositionFunction;
import org.opensearch.sql.ast.expression.QualifiedName;
import org.opensearch.sql.ast.expression.RelevanceFieldList;
import org.opensearch.sql.ast.expression.Span;
Expand Down Expand Up @@ -213,6 +214,13 @@ public Expression visitHighlightFunction(HighlightFunction node, AnalysisContext
return new HighlightExpression(expr);
}

@Override
public Expression visitPositionFunction(PositionFunction node, AnalysisContext context) {
Expression stringPatternExpr = node.getStringPatternExpr().accept(this, context);
Expression searchStringExpr = node.getSearchStringExpr().accept(this, context);
return DSL.position(stringPatternExpr, searchStringExpr);
}

@Override
public Expression visitIn(In node, AnalysisContext context) {
return visitIn(node.getField(), node.getValueList(), context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.opensearch.sql.ast.expression.Map;
import org.opensearch.sql.ast.expression.Not;
import org.opensearch.sql.ast.expression.Or;
import org.opensearch.sql.ast.expression.PositionFunction;
import org.opensearch.sql.ast.expression.QualifiedName;
import org.opensearch.sql.ast.expression.RelevanceFieldList;
import org.opensearch.sql.ast.expression.Span;
Expand Down Expand Up @@ -278,6 +279,10 @@ public T visitHighlightFunction(HighlightFunction node, C context) {
return visitChildren(node, context);
}

public T visitPositionFunction(PositionFunction node, C context) {
return visitChildren(node, context);
}

public T visitStatement(Statement node, C context) {
return visit(node, context);
}
Expand Down
6 changes: 6 additions & 0 deletions core/src/main/java/org/opensearch/sql/ast/dsl/AstDSL.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import org.opensearch.sql.ast.expression.Not;
import org.opensearch.sql.ast.expression.Or;
import org.opensearch.sql.ast.expression.ParseMethod;
import org.opensearch.sql.ast.expression.PositionFunction;
import org.opensearch.sql.ast.expression.QualifiedName;
import org.opensearch.sql.ast.expression.Span;
import org.opensearch.sql.ast.expression.SpanUnit;
Expand Down Expand Up @@ -288,6 +289,11 @@ public UnresolvedExpression highlight(UnresolvedExpression fieldName,
return new HighlightFunction(fieldName, arguments);
}

public UnresolvedExpression position(UnresolvedExpression stringPatternExpr,
UnresolvedExpression searchStringExpr) {
return new PositionFunction(stringPatternExpr, searchStringExpr);
}

public UnresolvedExpression window(UnresolvedExpression function,
List<UnresolvedExpression> partitionByList,
List<Pair<SortOption, UnresolvedExpression>> sortList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

package org.opensearch.sql.ast.expression;

import java.util.Arrays;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.ToString;
import org.opensearch.sql.ast.AbstractNodeVisitor;


/**
* Expression node of Position function.
*/
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
@Getter
@ToString
public class PositionFunction extends UnresolvedExpression {
@Getter
private UnresolvedExpression stringPatternExpr;
@Getter
private UnresolvedExpression searchStringExpr;

@Override
public List<UnresolvedExpression> getChild() {
return Arrays.asList(stringPatternExpr, searchStringExpr);
}

@Override
public <R, C> R accept(AbstractNodeVisitor<R, C> nodeVisitor, C context) {
return nodeVisitor.visitPositionFunction(this, context);
}
}
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 @@ -229,6 +229,10 @@ public static FunctionExpression cbrt(Expression... expressions) {
return compile(BuiltinFunctionName.CBRT, expressions);
}

public static FunctionExpression position(Expression... expressions) {
return compile(BuiltinFunctionName.POSITION, expressions);
}

public static FunctionExpression truncate(Expression... expressions) {
return compile(BuiltinFunctionName.TRUNCATE, expressions);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,24 @@ public enum BuiltinFunctionName {
/**
* Text Functions.
*/
SUBSTR(FunctionName.of("substr")),
SUBSTRING(FunctionName.of("substring")),
RTRIM(FunctionName.of("rtrim")),
LTRIM(FunctionName.of("ltrim")),
TRIM(FunctionName.of("trim")),
UPPER(FunctionName.of("upper")),
LOWER(FunctionName.of("lower")),
REGEXP(FunctionName.of("regexp")),
ASCII(FunctionName.of("ascii")),
CONCAT(FunctionName.of("concat")),
CONCAT_WS(FunctionName.of("concat_ws")),
LENGTH(FunctionName.of("length")),
STRCMP(FunctionName.of("strcmp")),
RIGHT(FunctionName.of("right")),
LEFT(FunctionName.of("left")),
ASCII(FunctionName.of("ascii")),
LENGTH(FunctionName.of("length")),
LOCATE(FunctionName.of("locate")),
LOWER(FunctionName.of("lower")),
LTRIM(FunctionName.of("ltrim")),
POSITION(FunctionName.of("position")),
REGEXP(FunctionName.of("regexp")),
REPLACE(FunctionName.of("replace")),
RIGHT(FunctionName.of("right")),
RTRIM(FunctionName.of("rtrim")),
STRCMP(FunctionName.of("strcmp")),
SUBSTR(FunctionName.of("substr")),
SUBSTRING(FunctionName.of("substring")),
TRIM(FunctionName.of("trim")),
UPPER(FunctionName.of("upper")),

/**
* NULL Test.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,23 @@ public class TextFunction {
* @param repository {@link BuiltinFunctionRepository}.
*/
public void register(BuiltinFunctionRepository repository) {
repository.register(substr());
repository.register(substring());
repository.register(ltrim());
repository.register(rtrim());
repository.register(trim());
repository.register(lower());
repository.register(upper());
repository.register(ascii());
repository.register(concat());
repository.register(concat_ws());
repository.register(length());
repository.register(strcmp());
repository.register(right());
repository.register(left());
repository.register(ascii());
repository.register(length());
repository.register(locate());
repository.register(lower());
repository.register(ltrim());
repository.register(position());
repository.register(replace());
repository.register(right());
repository.register(rtrim());
repository.register(strcmp());
repository.register(substr());
repository.register(substring());
repository.register(trim());
repository.register(upper());
}

/**
Expand Down Expand Up @@ -241,6 +242,18 @@ private DefaultFunctionResolver locate() {
TextFunction::exprLocate), INTEGER, STRING, STRING, INTEGER));
}

/**
* Returns the position of the first occurrence of a substring in a string starting from 1.
* Returns 0 if substring is not in string.
* Returns NULL if any argument is NULL.
* Supports following signature:
* (STRING IN STRING) -> INTEGER
*/
private DefaultFunctionResolver position() {
return define(BuiltinFunctionName.POSITION.getName(),
impl(nullMissingHandling(TextFunction::exprPosition), INTEGER, STRING, STRING));
}

/**
* REPLACE(str, from_str, to_str) returns the string str with all occurrences of
* the string from_str replaced by the string to_str.
Expand Down Expand Up @@ -313,6 +326,10 @@ private static ExprValue exprLocate(ExprValue subStr, ExprValue str, ExprValue p
str.stringValue().indexOf(subStr.stringValue(), pos.integerValue() - 1) + 1);
}

private static ExprValue exprPosition(ExprValue subStr, ExprValue str) {
return exprLocate(subStr, str);
}

private static ExprValue exprReplace(ExprValue str, ExprValue from, ExprValue to) {
return new ExprStringValue(str.stringValue().replaceAll(from.stringValue(), to.stringValue()));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import org.opensearch.sql.ast.expression.Alias;
import org.opensearch.sql.ast.expression.HighlightFunction;
import org.opensearch.sql.ast.expression.Literal;
import org.opensearch.sql.ast.expression.PositionFunction;
import org.opensearch.sql.expression.NamedExpression;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
Expand Down Expand Up @@ -48,4 +49,14 @@ void visit_highlight() {
NamedExpression analyze = analyzer.analyze(alias, analysisContext);
assertEquals("highlight(fieldA)", analyze.getNameOrAlias());
}

@Test
void visit_position() {
Alias alias = AstDSL.alias("position(fieldA IN fieldB)",
new PositionFunction(AstDSL.stringLiteral("fieldA"), AstDSL.stringLiteral("fieldB")));
NamedExpressionAnalyzer analyzer = new NamedExpressionAnalyzer(expressionAnalyzer);

NamedExpression analyze = analyzer.analyze(alias, analysisContext);
assertEquals("position(fieldA IN fieldB)", analyze.getNameOrAlias());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,26 @@ void locate() {
DSL.locate(missingRef, DSL.literal("hello"), DSL.literal(1))));
}

@Test
void position() {
FunctionExpression expression = DSL.position(
DSL.literal("world"),
DSL.literal("helloworldworld"));
assertEquals(INTEGER, expression.type());
assertEquals(6, eval(expression).integerValue());

expression = DSL.position(
DSL.literal("abc"),
DSL.literal("hello world"));
assertEquals(INTEGER, expression.type());
assertEquals(0, eval(expression).integerValue());

when(nullRef.type()).thenReturn(STRING);
assertEquals(nullValue(), eval(DSL.position(nullRef, DSL.literal("hello"))));
when(missingRef.type()).thenReturn(STRING);
assertEquals(missingValue(), eval(DSL.position(missingRef, DSL.literal("hello"))));
}

@Test
void replace() {
FunctionExpression expression = DSL.replace(
Expand Down
25 changes: 25 additions & 0 deletions docs/user/dql/functions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2331,6 +2331,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, INTEGER

Return type integer:

(STRING IN STRING) -> INTEGER

Example::

os> SELECT POSITION('world' IN 'helloworld')
fetched rows / total rows = 1/1
+-------------------------------------+---------------------------------------+
| POSITION('world' IN 'helloworld') | POSITION('invalid' IN 'helloworld') |
|-------------------------------------+---------------------------------------|
| 6 | 0 |
+-------------------------------------+---------------------------------------+


REPLACE
-------

Expand Down
Loading

0 comments on commit 2964853

Please sign in to comment.