Skip to content
Closed
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
6 changes: 4 additions & 2 deletions docs/content/querying/filters.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@ The simplest filter is a selector filter. The selector filter will match a speci
The grammar for a SELECTOR filter is as follows:

``` json
"filter": { "type": "selector", "dimension": <dimension_string>, "value": <dimension_value_string> }
"filter": { "type": "selector", "dimension": <dimension_string>, "value": <dimension_value_string>, "operator": <operator> }
```

This is the equivalent of `WHERE <dimension_string> = '<dimension_value_string>'`.
When `operator` is not specified it will be translated to `EQUAL` operator.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

how this is different from what we have like bound, in and not

`operator` can be one of `GT`, `GreaterThan`, `>=`, `GTE`, `GreaterThanOrEqualTo`, `>`, `LT`, `LessThan`, `<`, `LTE`, `LessThanOrEqualTo`, `<=`, `EQ`, `Equals`, `==`, `NE`, `NotEquals`, `!=`, `<>`. Except `EQUAL` and `NOT_EQUAL` operator, null or empty value cannot be used.
This is the equivalent of `WHERE <dimension_string> <operator> '<dimension_value_string>'`.

### Regular expression filter

Expand Down
11 changes: 10 additions & 1 deletion processing/src/main/java/io/druid/query/Druids.java
Original file line number Diff line number Diff line change
Expand Up @@ -244,23 +244,26 @@ public static NotDimFilterBuilder newNotDimFilterBuilder()
public static class SelectorDimFilterBuilder
{
private String dimension;
private String operation;
private String value;

public SelectorDimFilterBuilder()
{
dimension = "";
operation = "";
value = "";
}

public SelectorDimFilter build()
{
return new SelectorDimFilter(dimension, value);
return new SelectorDimFilter(dimension, value, operation);
}

public SelectorDimFilterBuilder copy(SelectorDimFilterBuilder builder)
{
return new SelectorDimFilterBuilder()
.dimension(builder.dimension)
.operation(builder.operation)
.value(builder.value);
}

Expand All @@ -270,6 +273,12 @@ public SelectorDimFilterBuilder dimension(String d)
return this;
}

public SelectorDimFilterBuilder operation(String op)
{
operation = op;
return this;
}

public SelectorDimFilterBuilder value(String v)
{
value = v;
Expand Down
133 changes: 133 additions & 0 deletions processing/src/main/java/io/druid/query/filter/BinaryOperator.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
package io.druid.query.filter;

import com.google.common.base.Predicate;
import com.google.common.base.Strings;

/**
*/
public enum BinaryOperator
{
GT {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.compareTo(value) > 0;
}
};
}
},
GTE {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.compareTo(value) >= 0;
}
};
}
},
LT {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.compareTo(value) < 0;
}
};
}
},
LTE {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.compareTo(value) <= 0;
}
};
}
},
EQ {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return input.equals(value);
}
};
}
},
NE {
@Override
public Predicate<String> toPredicate(final String value)
{
return new Predicate<String>()
{
@Override
public boolean apply(String input)
{
return !input.equals(value);
}
};
}
};

public abstract Predicate<String> toPredicate(final String value);

public static BinaryOperator get(String value)
{
if (Strings.isNullOrEmpty(value)) {
return EQ;
}
value = value.toLowerCase();
switch (value) {
case "gt":
case "greaterthan":
case ">":
return GT;
case "gte":
case "greaterthanorequalto":
case ">=":
return GTE;
case "lt":
case "lessthan":
case "<":
return LT;
case "lte":
case "lessthanorequalto":
case "<=":
return LTE;
case "eq":
case "equals":
case "=":
case "==":
return EQ;
case "ne":
case "notequals":
case "!=":
case "<>":
return NE;
}
throw new IllegalArgumentException("Invalid operator " + value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import com.metamx.common.StringUtils;

import java.nio.ByteBuffer;
Expand All @@ -32,17 +33,31 @@ public class SelectorDimFilter implements DimFilter
{
private final String dimension;
private final String value;
private final BinaryOperator operator;

@JsonCreator
public SelectorDimFilter(
@JsonProperty("dimension") String dimension,
@JsonProperty("value") String value
@JsonProperty("value") String value,
@JsonProperty("operator") String operator
)
{
Preconditions.checkArgument(dimension != null, "dimension must not be null");

this.dimension = dimension;
this.value = value;
this.operator = BinaryOperator.get(operator);

// don't allow null comparison, for now
Preconditions.checkArgument(
!(this.operator != BinaryOperator.EQ && this.operator != BinaryOperator.NE && Strings.isNullOrEmpty(value)),
"null comparison is not allowed, except equals/not-equals"
);
}

public SelectorDimFilter(String dimension, String value)
{
this(dimension, value, null);
}

@Override
Expand All @@ -51,8 +66,9 @@ public byte[] getCacheKey()
byte[] dimensionBytes = StringUtils.toUtf8(dimension);
byte[] valueBytes = (value == null) ? new byte[]{} : StringUtils.toUtf8(value);

return ByteBuffer.allocate(2 + dimensionBytes.length + valueBytes.length)
return ByteBuffer.allocate(3 + dimensionBytes.length + valueBytes.length)
.put(DimFilterCacheHelper.SELECTOR_CACHE_ID)
.put((byte) operator.ordinal())
.put(dimensionBytes)
.put(DimFilterCacheHelper.STRING_SEPARATOR)
.put(valueBytes)
Expand All @@ -71,6 +87,12 @@ public String getDimension()
return dimension;
}

@JsonProperty
public String getOperator()
{
return operator.name();
}

@JsonProperty
public String getValue()
{
Expand All @@ -95,6 +117,9 @@ public boolean equals(Object o)
if (value != null ? !value.equals(that.value) : that.value != null) {
return false;
}
if (!operator.equals(that.operator)) {
return false;
}

return true;
}
Expand All @@ -104,12 +129,13 @@ public int hashCode()
{
int result = dimension != null ? dimension.hashCode() : 0;
result = 31 * result + (value != null ? value.hashCode() : 0);
result = 31 * result + operator.ordinal();
return result;
}

@Override
public String toString()
{
return String.format("%s = %s", dimension, value);
return String.format("%s %s %s", dimension, operator.name(), value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ public T get(int index)
@Override
public int indexOf(T value)
{
return Arrays.asList(baseArray).indexOf(value);
return Arrays.binarySearch(baseArray, value);
}

@Override
Expand Down
7 changes: 5 additions & 2 deletions processing/src/main/java/io/druid/segment/filter/Filters.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,11 @@ public static Filter convertDimensionFilters(DimFilter dimFilter)
filter = new NotFilter(convertDimensionFilters(((NotDimFilter) dimFilter).getField()));
} else if (dimFilter instanceof SelectorDimFilter) {
final SelectorDimFilter selectorDimFilter = (SelectorDimFilter) dimFilter;

filter = new SelectorFilter(selectorDimFilter.getDimension(), selectorDimFilter.getValue());
filter = new SelectorFilter(
selectorDimFilter.getDimension(),
selectorDimFilter.getValue(),
selectorDimFilter.getOperator()
);
} else if (dimFilter instanceof ExtractionDimFilter) {
final ExtractionDimFilter extractionDimFilter = (ExtractionDimFilter) dimFilter;

Expand Down
Loading