-
Notifications
You must be signed in to change notification settings - Fork 14
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
Allow or filtering in source #12
Allow or filtering in source #12
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be a great improvement to the library, thank you!
My comments/questions were mainly around testing/docs/readability. The approach seems sound to me, though.
* Similar to {@link #addFilter} but removes all existing filters for {@code fieldName} first. | ||
* | ||
* @param fieldName | ||
* Name as seen in the query; not multi-value proxies ("id", not "ids") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see that our other addFilter
(and similar) methods' docstrings don't include @param
docs for the other params besides fieldName
, but feels like it would be useful to briefly describe what filterEntryConditionCreator
represents and does here, since it's a bit more complex.
* @param fieldName | ||
* Name as seen in the query; not multi-value proxies ("id", not "ids") | ||
*/ | ||
public void addFilterEntryConditionCreatorExclusively(String fieldName, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like there should be test coverage for this in ParsedQueryTest
.
} | ||
} | ||
return removed; | ||
return combinedFilterEntry.removeAllFiltersFor(fieldName); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks like this cleans up a lot. Nice.
@Target({ | ||
ElementType.FIELD, ElementType.METHOD | ||
}) | ||
public @interface Aggregated { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be helpful to use this in one of the test model/entity classes to demonstrate how it's intended to be used.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I actually ended up removing this in favor of updating FilterBy
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool, ok, so no need for this file anymore.
@@ -277,7 +277,7 @@ public String getFieldName(String name) { | |||
select = selectFrom.from(table); | |||
for (JoinCondition joinCondition : joinConditions) { | |||
if (joinCondition.isLeftJoin()) { | |||
((SelectJoinStep<?>) select).leftOuterJoin(joinCondition.getTable()).on(joinCondition.getCondition()); | |||
((SelectJoinStep<?>) select).leftJoin(joinCondition.getTable()).on(joinCondition.getCondition()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just to verify my understanding.. leftJoin
is equivalent to leftOuterJoin
right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
leftOuterJoin is a convenience method for leftJoin. Seemed odd that we were inconsistent in some places so I cleaned this up.
.getQueryName() | ||
.equals(fieldName)); | ||
boolean removedFromChildren = conditionCreators.stream() | ||
.filter(cc -> cc instanceof CombinedFilterEntry) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It looks like you're doing this instanceof
logic a lot to identify leaf nodes vs. combinations... would it be cleaner to add an isCombinedCondition
helper (and/or isLeafCondition
, or something like that) to FilterEntryConditionCreator
that does the instanceof
checks?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've removed most of these. I'll think about this some more but my knee jerk is that it seems odd to have helpers defined in the interface / higher level classes for checking if the instances are of a specific subclass type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could be an actual interface method (i.e. not actually implemented in FilterEntryConditionCreator), where the implementation in CombinedFilterEntry
simply returns false
(not a leaf node) and the one in BoundFilterEntry
returns true
(is a leaf node).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ahh sorry, I missed your follow up here. Yea that doesn't seem so bad. I guess I'm not sure what it would buy though in the implementation. Every instanceOf check I do is right before a cast and I wouldn't feel comfortable casting without that anyways.
return removedFromThis || removedFromChildren; | ||
} | ||
|
||
public Optional<BoundFilterEntry<T>> getFirstFilterForFieldName(String fieldName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I totally understand what a "first" filter means in this kind of tree structure, or what it's used for. It looks like it'll first look through all top-level "leaf" filters and then dive into the combined filters... Will that find the "first"?
What about using getFlattenedBoundFilterEntries
for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yea this is a part of the API that was a little odd to maintain in the new structure. I think getFlattenedBoundFilterEntries
should be fine actually.
return maybeBfe; | ||
} | ||
|
||
public List<BoundFilterEntry<T>> getAllFiltersForFieldName(String fieldName) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, what about using getFlattenedBoundFilterEntries()
for this?
.get(filterName))).findFirst(); | ||
Optional<BoundFilterEntry<T>> filterEntryOptional = filterTable.rowKeySet().stream().filter(f -> Objects.equals(f | ||
.getFieldName(), finalFieldName) && Objects.equals(f.getFilter(), UriParamParser.BY_NAME | ||
.get(filterName))).findFirst(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Weird wrapping here again.
|
||
// Use reserved words instead of simple look-up to throw exception on disallowed fields | ||
if (!filterEntryOptional.isPresent() || (!RESERVED_WORDS.contains(filterEntryOptional.get().getQueryName()) && !filterTable.contains(filterEntryOptional.get(), filterName))) { | ||
if (!filterEntryOptional.isPresent() || (!RESERVED_WORDS.contains(filterEntryOptional.get().getQueryName()) && !filterTable | ||
.contains(filterEntryOptional.get(), filterName))) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
And again 😄
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure I grok all the details here as I haven't worked much in this repo, but I left a few comments. Is there an example of what the query string would look like that this would be able to parse?
@@ -45,7 +43,7 @@ | |||
public ParsedQuery( | |||
T boundQuerySpec, | |||
Class<T> queryType, | |||
List<BoundFilterEntry<T>> boundFilterEntries, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this is a breaking change, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice catch, I'll fix
.filter(cc -> cc instanceof CombinedFilterEntry) | ||
.map(cc -> ((CombinedFilterEntry<T>) cc)) | ||
.collect(Collectors.toList()); | ||
for (CombinedFilterEntry<T> combinedFilterEntry : combinedFilterEntries) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why collect to a list just to iterate over that list? You could just use map
and findAny
.
import com.hubspot.httpql.FieldFactory; | ||
import com.hubspot.httpql.QuerySpec; | ||
|
||
public class CombinedFilterEntry<T extends QuerySpec> implements FilterEntryConditionCreator<T> { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe this is just a FilterEntryClause
or BooleanFilterEntryClause
? Combined
doesn't really give any more information about what it combines. Or maybe use "expression" or "term" if we're creating boolean syntax trees?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the symmetry of the combined
term with the CombinedCondition
in jooq, which is really the plumbing of httpQL. Agree that FilterEntry doesn't make much sense so I went with CombinedConditionCreator instead
@boulter this isn't a change to actual parsing yet, there's a few more steps required for that. This is a step in that direction that will allow for modifying or augmenting |
@@ -30,7 +29,8 @@ | |||
|
|||
private final Class<T> queryType; | |||
|
|||
private final List<BoundFilterEntry<T>> boundFilterEntries; | |||
private final CombinedConditionCreator<T> combinedFilterEntry; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should probably rename the variable here to match the new classname
@Target({ | ||
ElementType.FIELD, ElementType.METHOD | ||
}) | ||
public @interface Aggregated { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok cool, ok, so no need for this file anymore.
@@ -25,4 +25,6 @@ | |||
Class<? extends Filter>[] value(); | |||
|
|||
String as() default ""; | |||
|
|||
Class<?> typeOverride() default void.class; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm actually not sure I understand what this annotation param does. Could you add it to the docstring above and to the tests?
@@ -181,18 +191,20 @@ protected void buildOrderableFields(final Map<String, BeanPropertyDefinition> fi | |||
boundFilterEntries.add(boundColumn); | |||
} | |||
|
|||
CombinedConditionCreator<T> combinedFilterEntry = new CombinedConditionCreator<>(Operator.AND, Lists.newArrayList(boundFilterEntries)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again you might want to rename the variable here to match the new class name.
Updated, thanks for the review all! Looking to merge this in the morning. |
Step 1 towards #4
Represents replaces the List on ParsedQuery with a CombinedFilterEntry. Which has an Operator (AND, OR) and itself has a List (which BoundFilterEntries now extend). This allows us to have nested complex where conditions as opposed to the previous simple ANDed list.
Other improvements