Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

initial implementation of startsWith #78

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
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 @@ -109,6 +109,11 @@ public <T> Boolean gt(BoundReference<T> ref, Literal<T> lit) {
return cmp.compare(ref.get(struct), lit.value()) > 0;
}

@Override
public Boolean startsWith(BoundReference<String> ref, Literal<String> lit) {
return ref.get(struct).startsWith(lit.value());
}

@Override
public <T> Boolean gtEq(BoundReference<T> ref, Literal<T> lit) {
Comparator<T> cmp = lit.comparator();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ enum Operation {
NOT_IN,
NOT,
AND,
OR;
OR,
STARTS_WITH;

/**
* @return the operation used when this is negated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,10 @@ public <T> R notIn(BoundReference<T> ref, Literal<T> lit) {
return null;
}

public R startsWith(BoundReference<String> ref, Literal<String> lit) {
return null;
}

public <T> R predicate(BoundPredicate<T> pred) {
switch (pred.op()) {
case IS_NULL:
Expand All @@ -113,6 +117,11 @@ public <T> R predicate(BoundPredicate<T> pred) {
return in(pred.ref(), pred.literal());
case NOT_IN:
return notIn(pred.ref(), pred.literal());
case STARTS_WITH:
// due to the fact that startWith function in Expressions accepts only string types casting is a must here.
// in Unbound#bind function we added a check for that

return startsWith((BoundReference<String>) pred.ref(), (Literal<String>) pred.literal());
default:
throw new UnsupportedOperationException(
"Unknown operation for predicate: " + pred.op());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,10 @@ public static <T> UnboundPredicate<T> notEqual(String name, T value) {
return new UnboundPredicate<>(Expression.Operation.NOT_EQ, ref(name), value);
}

public static <T> UnboundPredicate<T> startsWith(String name, T value) {
return new UnboundPredicate<>(Operation.STARTS_WITH, ref(name), value);
}

public static <T> UnboundPredicate<T> predicate(Operation op, String name, T value) {
Preconditions.checkArgument(op != Operation.IS_NULL && op != Operation.NOT_NULL,
"Cannot create %s predicate inclusive a value", op);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ public String toString() {
return String.valueOf(ref()) + " == " + literal();
case NOT_EQ:
return String.valueOf(ref()) + " != " + literal();
case STARTS_WITH:
return "startsWith(" + ref() + "," + literal() + ")";
// case IN:
// break;
// case NOT_IN:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@

import static com.netflix.iceberg.expressions.Expression.Operation.IS_NULL;
import static com.netflix.iceberg.expressions.Expression.Operation.NOT_NULL;
import static com.netflix.iceberg.expressions.Expression.Operation.STARTS_WITH;

public class UnboundPredicate<T> extends Predicate<T, NamedReference> {

Expand Down Expand Up @@ -63,6 +64,10 @@ public Expression bind(Types.StructType struct) {
}
}

if (op() == STARTS_WITH && field.type() != Types.StringType.get()) {
throw new ValidationException("Operation startsWith accepts only strings");
}

Literal<T> lit = literal().to(field.type());
if (lit == null) {
throw new ValidationException(String.format(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
package com.netflix.iceberg.expressions;

import com.netflix.iceberg.TestHelpers;
import com.netflix.iceberg.exceptions.ValidationException;
import com.netflix.iceberg.types.Types;
import com.netflix.iceberg.types.Types.StructType;
import org.apache.avro.util.Utf8;
Expand All @@ -26,6 +27,7 @@
import static com.netflix.iceberg.expressions.Expressions.alwaysFalse;
import static com.netflix.iceberg.expressions.Expressions.alwaysTrue;
import static com.netflix.iceberg.expressions.Expressions.and;
import static com.netflix.iceberg.expressions.Expressions.startsWith;
import static com.netflix.iceberg.expressions.Expressions.equal;
import static com.netflix.iceberg.expressions.Expressions.greaterThan;
import static com.netflix.iceberg.expressions.Expressions.greaterThanOrEqual;
Expand Down Expand Up @@ -151,4 +153,22 @@ public void testCharSeqValue() {
Assert.assertFalse("string(abc) == utf8(abcd) => false",
evaluator.eval(TestHelpers.Row.of(new Utf8("abcd"))));
}

@Test
public void testStartsWith() {
StructType struct = StructType.of(required(3, "s", Types.StringType.get()));
Evaluator evaluator = new Evaluator(struct, startsWith("s", "abc"));
Assert.assertTrue("startsWith(abcdddd, abc) => true",
evaluator.eval(TestHelpers.Row.of(("abcdddd"))));

Assert.assertFalse("startsWith(xyzffff, abc) => false",
evaluator.eval(TestHelpers.Row.of(("xyzffff"))));
}

@Test(expected = ValidationException.class)
Copy link
Contributor

Choose a reason for hiding this comment

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

This is more of a binding test, which should go in TestExpressionBinding.

public void testStartsWithThrowsOnNotString() {
StructType struct = StructType.of(required(3, "s", Types.IntegerType.get()));
Evaluator evaluator = new Evaluator(struct, startsWith("s", 112));
evaluator.eval(TestHelpers.Row.of(("xyzffff")));
}
}