Skip to content

Commit

Permalink
Allow Inverted Versions of Filter Regex
Browse files Browse the repository at this point in the history
  • Loading branch information
nbauernfeind committed Jun 7, 2024
1 parent bd53581 commit cfa2c05
Show file tree
Hide file tree
Showing 3 changed files with 125 additions and 96 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ public void init(
} else if (colClass == LocalDateTime.class) {
filter = makeComparableRangeFilter(columnName, condition, (LocalDateTime) realValue.getValue());
} else if (colClass == ZonedDateTime.class) {
filter = makeInstantRangeFilter(columnName, condition,
filter = makeZonedDateTimeRangeFilter(columnName, condition,
DateTimeUtils.epochNanos((ZonedDateTime) realValue.getValue()));
} else if (BigDecimal.class.isAssignableFrom(colClass)) {
filter = makeComparableRangeFilter(columnName, condition, (BigDecimal) realValue.getValue());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,112 +49,76 @@ public class WhereFilterFactory {
private static final ExpressionParser<WhereFilter> parser = new ExpressionParser<>();

static {
// <ColumnName>==<Number|Boolean|"String">
// <ColumnName>=<Number|Boolean|"String">
// <ColumnName>!=<Number|Boolean|"String">
// <ColumnName> == <Number|Boolean|"String">
// <ColumnName> = <Number|Boolean|"String">
// <ColumnName> != <Number|Boolean|"String">
// <ColumnName> < <Number|Boolean|"String">
// <ColumnName> <= <Number|Boolean|"String">
// <ColumnName> > <Number|Boolean|"String">
// <ColumnName> >= <Number|Boolean|"String">
parser.registerFactory(new AbstractExpressionFactory<>(
START_PTRN + "(" + ID_PTRN + ")\\s*(?:(?:={1,2})|(!=))\\s*(" + LITERAL_PTRN + ")" + END_PTRN) {
START_PTRN + "(" + ID_PTRN + ")\\s*((?:=|!|<|>)=?)\\s*(" + LITERAL_PTRN + ")" + END_PTRN) {
@Override
public WhereFilter getExpression(String expression, Matcher matcher, Object... args) {
final String columnName = matcher.group(1);
final boolean inverted = matcher.group(2) != null;
final String op = matcher.group(2);
final String value = matcher.group(3);
final boolean mirrored = false;

final FormulaParserConfiguration parserConfiguration = (FormulaParserConfiguration) args[0];
if (isRowVariable(columnName)) {
log.debug().append("WhereFilterFactory creating ConditionFilter for expression: ")
.append(expression).endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}
log.debug().append("WhereFilterFactory creating MatchFilter for expression: ").append(expression)
.endl();
return new MatchFilter(
MatchFilter.CaseSensitivity.MatchCase,
inverted ? MatchFilter.MatchType.Inverted : MatchFilter.MatchType.Regular,
columnName,
value);
return getWhereFilterOneSideColumn(
expression, (FormulaParserConfiguration) args[0], columnName, op, value, mirrored);
}
});

// <Number|Boolean|"String"> == <ColumnName>
// <Number|Boolean|"String"> = <ColumnName>
// <Number|Boolean|"String"> != <ColumnName>
// <Number|Boolean|"String"> < <ColumnName>
// <Number|Boolean|"String"> <= <ColumnName>
// <Number|Boolean|"String"> > <ColumnName>
// <Number|Boolean|"String"> >= <ColumnName>
parser.registerFactory(new AbstractExpressionFactory<>(
START_PTRN + "(" + LITERAL_PTRN + ")\\s*((?:=|!|<|>)=?)\\s*(" + ID_PTRN + ")" + END_PTRN) {
@Override
public WhereFilter getExpression(String expression, Matcher matcher, Object... args) {
final String value = matcher.group(1);
final String op = matcher.group(6);
final String columnName = matcher.group(7);
final boolean mirrored = true;

return getWhereFilterOneSideColumn(
expression, (FormulaParserConfiguration) args[0], columnName, op, value, mirrored);
}
});

// <ColumnName> == <User QueryScopeParam>
// <ColumnName> = <User QueryScopeParam>
// <ColumnName> != <User QueryScopeParam>
// <ColumnName> < <User QueryScopeParam>
// <ColumnName> <= <User QueryScopeParam>
// <ColumnName> > <User QueryScopeParam>
// <ColumnName> >= <User QueryScopeParam>
parser.registerFactory(new AbstractExpressionFactory<>(
START_PTRN + "(" + ID_PTRN + ")\\s*((?:=|!|<|>)=?)\\s*(" + ID_PTRN + ")" + END_PTRN) {
@Override
public WhereFilter getExpression(String expression, Matcher matcher, Object... args) {
final String columnName = matcher.group(1);
final String op = matcher.group(2);
final String paramName = matcher.group(3);

final FormulaParserConfiguration parserConfiguration = (FormulaParserConfiguration) args[0];

if (isRowVariable(columnName)) {
log.debug().append("WhereFilterFactory creating ConditionFilter for expression: ")
.append(expression).endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}
if (!ExecutionContext.getContext().getQueryScope().hasParamName(paramName)) {
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
boolean mirrored = false;
final QueryScope queryScope = ExecutionContext.getContext().getQueryScope();
if (!queryScope.hasParamName(paramName)) {
if (queryScope.hasParamName(columnName)) {
mirrored = true;
} else {
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}
}

boolean inverted = false;
switch (op) {
case "!=":
inverted = true;
case "=":
case "==":
log.debug().append("WhereFilterFactory creating MatchFilter for expression: ")
.append(expression).endl();
return new MatchFilter(
MatchFilter.CaseSensitivity.MatchCase,
inverted ? MatchFilter.MatchType.Inverted : MatchFilter.MatchType.Regular,
columnName,
paramName);
case "<":
case ">":
case "<=":
case ">=":
log.debug().append("WhereFilterFactory creating RangeConditionFilter for expression: ")
.append(expression).endl();
return new RangeConditionFilter(columnName, op, paramName, expression,
parserConfiguration);
default:
throw new IllegalStateException("Unexpected operator: " + op);
}
}
});

// <ColumnName> < <Number|Boolean|"String">
// <ColumnName> <= <Number|Boolean|"String">
// <ColumnName> > <Number|Boolean|"String">
// <ColumnName> >= <Number|Boolean|"String">
parser.registerFactory(new AbstractExpressionFactory<>(
START_PTRN + "(" + ID_PTRN + ")\\s*([<>]=?)\\s*(" + LITERAL_PTRN + ")" + END_PTRN) {
@Override
public WhereFilter getExpression(String expression, Matcher matcher, Object... args) {
final FormulaParserConfiguration parserConfiguration = (FormulaParserConfiguration) args[0];
final String columnName = matcher.group(1);
final String conditionString = matcher.group(2);
final String value = matcher.group(3);
if (isRowVariable(columnName)) {
log.debug().append("WhereFilterFactory creating ConditionFilter for expression: ")
.append(expression).endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}
try {
log.debug().append("WhereFilterFactory creating RangeConditionFilter for expression: ")
.append(expression).endl();
return new RangeConditionFilter(columnName, conditionString, value, expression,
parserConfiguration);
} catch (Exception e) {
log.warn().append("WhereFilterFactory could not make RangeFilter for expression: ")
.append(expression).append(" due to ").append(e)
.append(" Creating ConditionFilter instead.").endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}
return getWhereFilterOneSideColumn(
expression, parserConfiguration, columnName, op, paramName, mirrored);
}
});

Expand Down Expand Up @@ -222,6 +186,57 @@ public WhereFilter getExpression(String expression, Matcher matcher, Object... a
});
}

private static @NotNull WhereFilter getWhereFilterOneSideColumn(
final String expression,
final FormulaParserConfiguration parserConfiguration,
final String columnName,
String op,
final String value,
boolean mirrored) {

if (isRowVariable(columnName)) {
log.debug().append("WhereFilterFactory creating ConditionFilter for expression: ")
.append(expression).endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}

switch (op) {
case "!=":
mirrored = !mirrored;
case "=":
case "==":
log.debug().append("WhereFilterFactory creating MatchFilter for expression: ").append(expression)
.endl();
return new MatchFilter(
CaseSensitivity.MatchCase,
mirrored ? MatchType.Inverted : MatchType.Regular,
columnName,
value);

case "<":
case ">":
case "<=":
case ">=":
try {
if (mirrored) {
final String dir = op.substring(0, 1);
op = op.replaceFirst(dir, dir.equals("<") ? ">" : "<");
}
log.debug().append("WhereFilterFactory creating RangeConditionFilter for expression: ")
.append(expression).endl();
return new RangeConditionFilter(columnName, op, value, expression, parserConfiguration);
} catch (Exception e) {
log.warn().append("WhereFilterFactory could not make RangeFilter for expression: ")
.append(expression).append(" due to ").append(e)
.append(" Creating ConditionFilter instead.").endl();
return ConditionFilter.createConditionFilter(expression, parserConfiguration);
}

default:
throw new IllegalStateException("Unexpected operator: " + op);
}
}

private static boolean isRowVariable(String columnName) {
return columnName.equals("i") || columnName.equals("ii") || columnName.equals("k");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import java.math.BigDecimal;
import java.math.BigInteger;
import java.time.Instant;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
Expand Down Expand Up @@ -1152,6 +1153,19 @@ public void testComparableBinarySearch() {
QueryScope.addParam("nine", null);
}

@Test
public void testZonedDateRangeFilter() {
final ZonedDateTime startTime = DateTimeUtils.parseZonedDateTime("2021-04-23T09:30 NY");
final ZonedDateTime[] array = new ZonedDateTime[10];
for (int ii = 0; ii < array.length; ++ii) {
array[ii] = DateTimeUtils.plus(startTime, 60_000_000_000L * ii);
}
final Table table = TableTools.newTable(col("ZDT", array));
showWithRowSet(table);

testRangeFilterHelper(table, "ZDT", array[5]);
}

@Test
public void testInstantRangeFilter() {
final Instant startTime = DateTimeUtils.parseInstant("2021-04-23T09:30 NY");
Expand All @@ -1162,11 +1176,7 @@ public void testInstantRangeFilter() {
final Table table = TableTools.newTable(col("DT", array));
showWithRowSet(table);

final Table sorted = table.sort("DT");
final Table backwards = table.sort("DT");

assertTableEquals(sorted.where("DT < '" + array[5] + "'"), sorted.where("ii < 5"));
assertTableEquals(backwards.where("DT < '" + array[5] + "'"), backwards.where("ii < 5"));
testRangeFilterHelper(table, "DT", array[5]);
}

@Test
Expand All @@ -1184,22 +1194,26 @@ public void testCharRangeFilter() {
final Table table = TableTools.newTable(charCol("CH", array));
showWithRowSet(table);

final Table sorted = table.sort("CH");
final Table backwards = table.sort("CH");
testRangeFilterHelper(table, "CH", array[5]);
}

private <T> void testRangeFilterHelper(Table table, String name, T mid) {
final Table sorted = table.sort(name);
final Table backwards = table.sort(name);

showWithRowSet(sorted);
log.debug().append("Pivot: " + array[5]).endl();
log.debug().append("Pivot: " + mid).endl();

final Table rangeFiltered = sorted.where("CH < '" + array[5] + "'");
final Table standardFiltered = sorted.where("'" + array[5] + "' > CH");
final Table rangeFiltered = sorted.where(name + " < '" + mid + "'");
final Table standardFiltered = sorted.where("'" + mid + "' > " + name);

showWithRowSet(rangeFiltered);
showWithRowSet(standardFiltered);
assertTableEquals(rangeFiltered, standardFiltered);
assertTableEquals(backwards.where("CH < '" + array[5] + "'"), backwards.where("'" + array[5] + "' > CH"));
assertTableEquals(backwards.where("CH <= '" + array[5] + "'"), backwards.where("'" + array[5] + "' >= CH"));
assertTableEquals(backwards.where("CH > '" + array[5] + "'"), backwards.where("'" + array[5] + "' < CH"));
assertTableEquals(backwards.where("CH >= '" + array[5] + "'"), backwards.where("'" + array[5] + "' <= CH"));
assertTableEquals(backwards.where(name + " < '" + mid + "'"), backwards.where("'" + mid + "' > " + name));
assertTableEquals(backwards.where(name + " <= '" + mid + "'"), backwards.where("'" + mid + "' >= " + name));
assertTableEquals(backwards.where(name + " > '" + mid + "'"), backwards.where("'" + mid + "' < " + name));
assertTableEquals(backwards.where(name + " >= '" + mid + "'"), backwards.where("'" + mid + "' <= " + name));
}

@Test
Expand Down

0 comments on commit cfa2c05

Please sign in to comment.