Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we split these out into null and missing tests.

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