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 @@ -56,7 +56,7 @@ public class ExprTimestampValue extends AbstractExprValue {
/**
* todo. only support timestamp in format yyyy-MM-dd HH:mm:ss.
*/
private static final DateTimeFormatter FORMATTER_WITNOUT_NANO = DateTimeFormatter
private static final DateTimeFormatter FORMATTER_WITHOUT_NANO = DateTimeFormatter
.ofPattern("yyyy-MM-dd HH:mm:ss");
private final Instant timestamp;

Expand Down Expand Up @@ -92,7 +92,7 @@ public ExprTimestampValue(String timestamp) {

@Override
public String value() {
return timestamp.getNano() == 0 ? FORMATTER_WITNOUT_NANO.withZone(ZONE)
return timestamp.getNano() == 0 ? FORMATTER_WITHOUT_NANO.withZone(ZONE)
.format(timestamp.truncatedTo(ChronoUnit.SECONDS))
: FORMATTER_VARIABLE_MICROS.withZone(ZONE).format(timestamp);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,9 @@ private static FunctionResolver castToString() {
private static FunctionResolver castToByte() {
return FunctionDSL.define(BuiltinFunctionName.CAST_TO_BYTE.getName(),
impl(nullMissingHandling(
(v) -> new ExprByteValue(Short.valueOf(v.stringValue()))), BYTE, STRING),
(v) -> new ExprByteValue(Byte.valueOf(v.stringValue()))), BYTE, STRING),
impl(nullMissingHandling(
(v) -> new ExprByteValue(v.shortValue())), BYTE, DOUBLE),
(v) -> new ExprByteValue(v.byteValue())), BYTE, DOUBLE),
impl(nullMissingHandling(
(v) -> new ExprByteValue(v.booleanValue() ? 1 : 0)), BYTE, BOOLEAN)
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,32 @@

import static org.opensearch.sql.opensearch.data.type.OpenSearchDataType.OPENSEARCH_TEXT_KEYWORD;

import com.google.common.collect.ImmutableMap;
import java.util.Map;
import java.util.function.Function;
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.sql.data.model.ExprBooleanValue;
import org.opensearch.sql.data.model.ExprByteValue;
import org.opensearch.sql.data.model.ExprDateValue;
import org.opensearch.sql.data.model.ExprDatetimeValue;
import org.opensearch.sql.data.model.ExprDoubleValue;
import org.opensearch.sql.data.model.ExprFloatValue;
import org.opensearch.sql.data.model.ExprIntegerValue;
import org.opensearch.sql.data.model.ExprLongValue;
import org.opensearch.sql.data.model.ExprShortValue;
import org.opensearch.sql.data.model.ExprStringValue;
import org.opensearch.sql.data.model.ExprTimeValue;
import org.opensearch.sql.data.model.ExprTimestampValue;
import org.opensearch.sql.data.model.ExprValue;
import org.opensearch.sql.data.type.ExprCoreType;
import org.opensearch.sql.data.type.ExprType;
import org.opensearch.sql.expression.Expression;
import org.opensearch.sql.expression.FunctionExpression;
import org.opensearch.sql.expression.LiteralExpression;
import org.opensearch.sql.expression.NamedArgumentExpression;
import org.opensearch.sql.expression.ReferenceExpression;
import org.opensearch.sql.expression.function.BuiltinFunctionName;
import org.opensearch.sql.expression.function.FunctionName;

/**
* Lucene query abstraction that builds Lucene query from function expression.
Expand All @@ -55,7 +73,8 @@ public abstract class LuceneQuery {
public boolean canSupport(FunctionExpression func) {
return (func.getArguments().size() == 2)
&& (func.getArguments().get(0) instanceof ReferenceExpression)
&& (func.getArguments().get(1) instanceof LiteralExpression)
&& (func.getArguments().get(1) instanceof LiteralExpression
|| literalExpressionWrappedByCast(func))
|| isMultiParameterQuery(func);
}

Expand All @@ -74,18 +93,144 @@ private boolean isMultiParameterQuery(FunctionExpression func) {
return true;
}

/**
* Check if the second argument of the function is a literal expression wrapped by cast function.
*/
private boolean literalExpressionWrappedByCast(FunctionExpression func) {
if (func.getArguments().get(1) instanceof FunctionExpression) {
FunctionExpression expr = (FunctionExpression) func.getArguments().get(1);
return castMap.containsKey(expr.getFunctionName())
&& expr.getArguments().get(0) instanceof LiteralExpression;
}
return false;
}

/**
* Build Lucene query from function expression.
* The cast function is converted to literal expressions before generating DSL.
*
* @param func function
* @return query
*/
public QueryBuilder build(FunctionExpression func) {
ReferenceExpression ref = (ReferenceExpression) func.getArguments().get(0);
LiteralExpression literal = (LiteralExpression) func.getArguments().get(1);
return doBuild(ref.getAttr(), ref.type(), literal.valueOf(null));
Expression expr = func.getArguments().get(1);
ExprValue literalValue = expr instanceof LiteralExpression ? expr
.valueOf(null) : cast((FunctionExpression) expr);
return doBuild(ref.getAttr(), ref.type(), literalValue);
}

private ExprValue cast(FunctionExpression castFunction) {
return castMap.get(castFunction.getFunctionName()).apply(
(LiteralExpression) castFunction.getArguments().get(0));
}

/**
* Type converting map.
*/
private final Map<FunctionName, Function<LiteralExpression, ExprValue>> castMap = ImmutableMap
.<FunctionName, Function<LiteralExpression, ExprValue>>builder()
.put(BuiltinFunctionName.CAST_TO_STRING.getName(), expr -> {
if (!expr.type().equals(ExprCoreType.STRING)) {
return new ExprStringValue(String.valueOf(expr.valueOf(null).value()));
} else {
return expr.valueOf(null);
}
})
.put(BuiltinFunctionName.CAST_TO_BYTE.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprByteValue(expr.valueOf(null).byteValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprByteValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprByteValue(Byte.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_SHORT.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprShortValue(expr.valueOf(null).shortValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprShortValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprShortValue(Short.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_INT.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprIntegerValue(expr.valueOf(null).integerValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprIntegerValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprIntegerValue(Integer.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_LONG.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprLongValue(expr.valueOf(null).longValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprLongValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprLongValue(Long.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_FLOAT.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprFloatValue(expr.valueOf(null).floatValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprFloatValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprFloatValue(Float.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_DOUBLE.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return new ExprDoubleValue(expr.valueOf(null).doubleValue());
} else if (expr.type().equals(ExprCoreType.BOOLEAN)) {
return new ExprDoubleValue(expr.valueOf(null).booleanValue() ? 1 : 0);
} else {
return new ExprDoubleValue(Double.valueOf(expr.valueOf(null).stringValue()));
}
})
.put(BuiltinFunctionName.CAST_TO_BOOLEAN.getName(), expr -> {
if (ExprCoreType.numberTypes().contains(expr.type())) {
return expr.valueOf(null).doubleValue() == 1
? ExprBooleanValue.of(true) : ExprBooleanValue.of(false);
} else if (expr.type().equals(ExprCoreType.STRING)) {
return ExprBooleanValue.of(Boolean.valueOf(expr.valueOf(null).stringValue()));
} else {
return expr.valueOf(null);
}
})
.put(BuiltinFunctionName.CAST_TO_DATE.getName(), expr -> {
if (expr.type().equals(ExprCoreType.STRING)) {
return new ExprDateValue(expr.valueOf(null).stringValue());
} else {
return new ExprDateValue(expr.valueOf(null).dateValue());
}
})
.put(BuiltinFunctionName.CAST_TO_TIME.getName(), expr -> {
if (expr.type().equals(ExprCoreType.STRING)) {
return new ExprTimeValue(expr.valueOf(null).stringValue());
} else {
return new ExprTimeValue(expr.valueOf(null).timeValue());
}
})
.put(BuiltinFunctionName.CAST_TO_DATETIME.getName(), expr -> {
if (expr.type().equals(ExprCoreType.STRING)) {
return new ExprDatetimeValue(expr.valueOf(null).stringValue());
} else {
return new ExprDatetimeValue(expr.valueOf(null).datetimeValue());
}
})
.put(BuiltinFunctionName.CAST_TO_TIMESTAMP.getName(), expr -> {
if (expr.type().equals(ExprCoreType.STRING)) {
return new ExprTimestampValue(expr.valueOf(null).stringValue());
} else {
return new ExprTimestampValue(expr.valueOf(null).timestampValue());
}
})
.build();

/**
* Build method that subclass implements by default which is to build query
* from reference and literal in function arguments.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.index.query.RangeQueryBuilder;
import org.opensearch.sql.data.model.ExprValue;
import org.opensearch.sql.data.type.ExprCoreType;
import org.opensearch.sql.data.type.ExprType;

/**
Expand All @@ -51,7 +52,7 @@ public enum Comparison {

@Override
protected QueryBuilder doBuild(String fieldName, ExprType fieldType, ExprValue literal) {
Object value = literal.value();
Object value = value(literal);

RangeQueryBuilder query = QueryBuilders.rangeQuery(fieldName);
switch (comparison) {
Expand All @@ -68,4 +69,12 @@ protected QueryBuilder doBuild(String fieldName, ExprType fieldType, ExprValue l
}
}

private Object value(ExprValue literal) {
if (literal.type().equals(ExprCoreType.TIMESTAMP)) {
return literal.timestampValue().toEpochMilli();
} else {
return literal.value();
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.opensearch.index.query.QueryBuilder;
import org.opensearch.index.query.QueryBuilders;
import org.opensearch.sql.data.model.ExprValue;
import org.opensearch.sql.data.type.ExprCoreType;
import org.opensearch.sql.data.type.ExprType;

/**
Expand All @@ -40,7 +41,15 @@ public class TermQuery extends LuceneQuery {
@Override
protected QueryBuilder doBuild(String fieldName, ExprType fieldType, ExprValue literal) {
fieldName = convertTextToKeyword(fieldName, fieldType);
return QueryBuilders.termQuery(fieldName, literal.value());
return QueryBuilders.termQuery(fieldName, value(literal));
}

private Object value(ExprValue literal) {
if (literal.type().equals(ExprCoreType.TIMESTAMP)) {
return literal.timestampValue().toEpochMilli();
} else {
return literal.value();
}
}

}
Loading