28
28
import static com .yahoo .bullet .common .Utilities .isEmpty ;
29
29
import static com .yahoo .bullet .typesystem .TypedObject .GENERIC_UNKNOWN ;
30
30
import static com .yahoo .bullet .typesystem .TypedObject .IS_NOT_NULL ;
31
- import static com .yahoo .bullet .typesystem .TypedObject .IS_NOT_UNKNOWN ;
31
+ import static com .yahoo .bullet .typesystem .TypedObject .IS_PRIMITIVE_OR_NULL ;
32
32
33
33
@ Slf4j
34
34
public class FilterOperations {
@@ -56,14 +56,16 @@ public interface LogicalOperator extends BiPredicate<BulletRecord, Stream<Boolea
56
56
// SOME_LONG_VALUE EQ [1.23, 35.2] will be false
57
57
// SOME_LONG_VALUE NE [1.23. 425.3] will be false
58
58
// SOME_LONG_VALUE GT/LT/GE/LE [12.4, 253.4] will be false! even if SOME_LONG_VALUE numerically could make it true.
59
- private static final Comparator <TypedObject > EQ = (t , s ) -> s .anyMatch (i -> t . compareTo ( i ) == 0 );
60
- private static final Comparator <TypedObject > NE = (t , s ) -> s .noneMatch (i -> t . compareTo ( i ) == 0 );
59
+ private static final Comparator <TypedObject > EQ = (t , s ) -> s .anyMatch (t :: equalTo );
60
+ private static final Comparator <TypedObject > NE = (t , s ) -> s .noneMatch (t :: equalTo );
61
61
private static final Comparator <TypedObject > GT = (t , s ) -> s .anyMatch (i -> t .compareTo (i ) > 0 );
62
62
private static final Comparator <TypedObject > LT = (t , s ) -> s .anyMatch (i -> t .compareTo (i ) < 0 );
63
63
private static final Comparator <TypedObject > GE = (t , s ) -> s .anyMatch (i -> t .compareTo (i ) >= 0 );
64
64
private static final Comparator <TypedObject > LE = (t , s ) -> s .anyMatch (i -> t .compareTo (i ) <= 0 );
65
65
private static final Comparator <Pattern > RLIKE = (t , s ) -> s .map (p -> p .matcher (t .toString ())).anyMatch (Matcher ::matches );
66
- private static final Comparator <TypedObject > SIZEOF = (t , s ) -> s .anyMatch (i -> sizeOf (t ) == i .getValue ());
66
+ private static final Comparator <TypedObject > SIZEOF = (t , s ) -> s .anyMatch (i -> i .equalTo (t .size ()));
67
+ private static final Comparator <TypedObject > CONTAINSKEY = (t , s ) -> s .anyMatch (i -> t .containsKey ((String ) i .getValue ()));
68
+ private static final Comparator <TypedObject > CONTAINSVALUE = (t , s ) -> s .anyMatch (t ::containsValue );
67
69
private static final LogicalOperator AND = (r , s ) -> s .allMatch (Boolean ::valueOf );
68
70
private static final LogicalOperator OR = (r , s ) -> s .anyMatch (Boolean ::valueOf );
69
71
private static final LogicalOperator NOT = (r , s ) -> !s .findFirst ().get ();
@@ -76,9 +78,11 @@ public interface LogicalOperator extends BiPredicate<BulletRecord, Stream<Boolea
76
78
COMPARATORS .put (Clause .Operation .LESS_THAN , isNotNullAnd (LT ));
77
79
COMPARATORS .put (Clause .Operation .GREATER_EQUALS , isNotNullAnd (GE ));
78
80
COMPARATORS .put (Clause .Operation .LESS_EQUALS , isNotNullAnd (LE ));
81
+ COMPARATORS .put (Clause .Operation .SIZE_OF , isNotNullAnd (SIZEOF ));
82
+ COMPARATORS .put (Clause .Operation .CONTAINS_KEY , isNotNullAnd (CONTAINSKEY ));
83
+ COMPARATORS .put (Clause .Operation .CONTAINS_VALUE , isNotNullAnd (CONTAINSVALUE ));
79
84
}
80
85
static final Comparator <Pattern > REGEX_LIKE = isNotNullAnd (RLIKE );
81
- static final Comparator <TypedObject > SIZE_OF = isNotNullAnd (SIZEOF );
82
86
static final Map <Clause .Operation , LogicalOperator > LOGICAL_OPERATORS = new EnumMap <>(Clause .Operation .class );
83
87
static {
84
88
LOGICAL_OPERATORS .put (Clause .Operation .AND , AND );
@@ -89,40 +93,26 @@ public interface LogicalOperator extends BiPredicate<BulletRecord, Stream<Boolea
89
93
/**
90
94
* Exposed for testing. Cast the values to the type of the object if possible.
91
95
*
92
- * @param object The {@link TypedObject } to cast the values to.
96
+ * @param type The {@link Type } to cast the values to.
93
97
* @param values The {@link List} of values to try and cast to the object.
94
98
* @return A {@link Stream} of casted {@link TypedObject}.
95
99
*/
96
- static Stream <TypedObject > cast (BulletRecord record , TypedObject object , List <ObjectFilterClause .Value > values ) {
97
- return values .stream ().filter (Objects ::nonNull ).map (v -> getValue (record , object , v )).filter (IS_NOT_UNKNOWN );
100
+ static Stream <TypedObject > cast (BulletRecord record , Type type , List <ObjectFilterClause .Value > values ) {
101
+ return values .stream ().filter (Objects ::nonNull ).map (v -> getTypedValue (record , type , v )).filter (IS_PRIMITIVE_OR_NULL );
98
102
}
99
103
100
104
private static <T > Comparator <T > isNotNullAnd (Comparator <T > comparator ) {
101
105
return (t , s ) -> IS_NOT_NULL .test (t ) && comparator .compare (t , s );
102
106
}
103
107
104
- private static Integer sizeOf (TypedObject object ) {
105
- Object o = object .getValue ();
106
- if (o instanceof List ) {
107
- return List .class .cast (o ).size ();
108
- }
109
- if (o instanceof Map ) {
110
- return Map .class .cast (o ).size ();
111
- }
112
- if (o instanceof String ) {
113
- return String .class .cast (o ).length ();
114
- }
115
- return 1 ;
116
- }
117
-
118
- private static TypedObject getValue (BulletRecord record , TypedObject object , ObjectFilterClause .Value value ) {
108
+ private static TypedObject getTypedValue (BulletRecord record , Type type , ObjectFilterClause .Value value ) {
119
109
switch (value .getKind ()) {
120
110
case FIELD :
121
- return object .typeCastFromObject (extractField (value .getValue (), record ));
111
+ return TypedObject .typeCastFromObject (type , extractField (value .getValue (), record ));
122
112
case VALUE :
123
113
// Right now, we cast the filter values which are lists of strings to the value being filtered on's type.
124
114
// In the future, we might want to support providing non-String values.
125
- return object .typeCast (value .getValue ());
115
+ return TypedObject .typeCast (type , value .getValue ());
126
116
default :
127
117
log .error ("Unsupported value kind: " + value .getKind ().name ());
128
118
return GENERIC_UNKNOWN ;
@@ -135,14 +125,21 @@ private static boolean performRelational(BulletRecord record, ObjectFilterClause
135
125
return true ;
136
126
}
137
127
TypedObject object = extractTypedObject (clause .getField (), record );
138
- switch (operator ) {
139
- case REGEX_LIKE :
140
- return REGEX_LIKE .compare (object , clause .getPatterns ().stream ());
141
- case SIZE_OF :
142
- return SIZE_OF .compare (object , cast (record , new TypedObject (Type .INTEGER , 0 ), clause .getValues ()));
143
- default :
144
- return COMPARATORS .get (operator ).compare (object , cast (record , object , clause .getValues ()));
128
+ if (operator == Clause .Operation .REGEX_LIKE ) {
129
+ return REGEX_LIKE .compare (object , clause .getPatterns ().stream ());
145
130
}
131
+
132
+ Type type ;
133
+ if (operator == Clause .Operation .SIZE_OF ) {
134
+ type = Type .INTEGER ;
135
+ } else if (operator == Clause .Operation .CONTAINS_KEY ) {
136
+ type = Type .STRING ;
137
+ } else if (operator == Clause .Operation .CONTAINS_VALUE ) {
138
+ type = object .getPrimitiveType ();
139
+ } else {
140
+ type = object .getType ();
141
+ }
142
+ return COMPARATORS .get (operator ).compare (object , cast (record , type , clause .getValues ()));
146
143
}
147
144
148
145
private static boolean performRelational (BulletRecord record , StringFilterClause clause ) {
@@ -170,12 +167,18 @@ public static boolean perform(BulletRecord record, Clause clause) {
170
167
// cost of violating polymorphism in this one spot.
171
168
// We do not want processing logic in FilterClause or LogicalClause, otherwise we could put the appropriate
172
169
// methods in those classes.
173
- if (clause instanceof ObjectFilterClause ) {
174
- return performRelational (record , (ObjectFilterClause ) clause );
175
- } else if (clause instanceof StringFilterClause ) {
176
- return performRelational (record , (StringFilterClause ) clause );
177
- } else {
178
- return performLogical (record , (LogicalClause ) clause );
170
+ try {
171
+ if (clause instanceof ObjectFilterClause ) {
172
+ return performRelational (record , (ObjectFilterClause ) clause );
173
+ } else if (clause instanceof StringFilterClause ) {
174
+ return performRelational (record , (StringFilterClause ) clause );
175
+ } else {
176
+ return performLogical (record , (LogicalClause ) clause );
177
+ }
178
+ } catch (RuntimeException e ) {
179
+ log .error ("Unable to perform filter {} to record {}" , clause , record );
180
+ log .error ("Skipping due to" , e );
181
+ return false ;
179
182
}
180
183
}
181
184
}
0 commit comments