-
Notifications
You must be signed in to change notification settings - Fork 32
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
dynamic query restrictions #895
dynamic query restrictions #895
Conversation
da5b807
to
52d42e9
Compare
59e75cc
to
0f9b1d3
Compare
Signed-off-by: Nathan Rauh <[email protected]>
0f9b1d3
to
d41c721
Compare
After a lot of difficulty reviewing another PR that combines several different new parts of Jakarta Data, I decided to just finish off updating this PR with how I expect Restrictions to work. I was originally thinking that Restrictions would only come from the static metamodel, but others have expressed a desire that users be able to create them like they can for Sort. I'm okay with that, but not by giving users access to record/implementation class constructors where they can populate them with any combinations of things they like. With this PR, all implementation classes are made non-public and kept out of the API. There are only 2 places where users obtain restrictions:
This also aims to de-emphasize interfaces that the end user shouldn't care about (and only Data Providers need, such as Operator and the Composite restriction) out of the main view by placing them in inner classes. There is no JavaDoc in this on purpose. The idea is to iterate over the design first, and later spend time on JavaDoc and other things. Other spec function like Pattern, Range, Is, ... are intentionally kept out so we can focus only on getting Restriction correct.
greater/lessThan methods require a Comparable parameter (lots of things can be sortable, but I think they are all Comparable). TextAttribute methods, and their equivalents on Restrict, require a String parameter. I added a commented out method showing what it might be appropriate to do with a Pattern parameter as well if that gets added. But ignore that if you don't want to think about Pattern yet. I would like to see more error checking for values supplied to Restriction factory methods in general, and identify if we can use any more precise types that would help keep invalid objects out, but we can save that for when we are further along. |
This is where I would have put it. |
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.
Added a commit to cover most of Gavin's review comments.
Also, I made an update to ensure ignoreCase is only available to String values, and not to numbers, dates, and so forth.
That's fine. I have moved it to |
Thanks! |
Switched methods such as startsWith(prefixPattern) to startsWith(prefix) and automatically add escaping to spare the user from that level of detail/complexity. |
a910b1f
to
2b8a88c
Compare
return new TextRestrictionRecord<>(field, Operator.LIKE, ESCAPED, p); | ||
} | ||
|
||
public static <T> Restriction<T> not(Object value, String field) { |
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 is not clear that not
means, notEquals´. Please either, use
neor
NotEquals`
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.
Sure, I can update that. Given that we have a equalTo
method, the equivalent here will be notEqualTo
return new TextRestrictionRecord<>(field, NOT, Operator.EQUAL, value); | ||
} | ||
|
||
public static <T> TextRestriction<T> notContains(String substring, String field) { |
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.
Can we remove all of those no
method factory sequences?
It would be great if we have only two options: by the method instead negate
and a single method factory not
.
It is pretty similar to the Java Predicate
: https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/function/Predicate.html
TextRestriction<Book> = TextRestriction.not(TextRestriction.contains(...));
TextRestriction<Book> = TextRestriction.contains(...).negate();
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.
@otaviojava JDQL has operators like not between
, not like
, etc, so it makes sense to me for such things to be reflected here. And it's significantly less verbose.
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 agree with Gavin on this.
Being able to write code such as,
products.search(_Product.description.notContains("refurbished"));
is much more straightforward and readable than
products.search(_Product.description.contains("refurbished").negated());
or
products.search(Restrict.not(_Product.description.contains("refurbished")));
* @throws IllegalArgumentException if the same character is supplied for | ||
* both wildcard types. | ||
*/ | ||
private static String toLikeEscaped(char charWildcard, |
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.
Once everybody agreed with Gavin's Pattern
class, why not centralize this behavior in that 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.
Yes, I think it's much better to have a nice class which models this basic concept. It's a much semantically stronger construct.
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 agree, if Pattern gets added, this utility method can move to there. Also note that the method is private, so the documentation on it is only for our own benefit and will not show in the API, so users won't even notice once it is moved.
public interface Restriction<T> { | ||
boolean isNegated(); | ||
|
||
enum Operator { |
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.
Please remove this one from the inner class; it is good, mainly for the Javadoc.
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 suppose I could move it to a top level class, although note that it currently is not something that applications use. Its current purpose is for Jakarta Data providers to know which type of restriction it is, so I had intentionally made in an inner class to keep it more out of the way of application developers. If you are looking ahead to the possibility of reusing it for an @Is
annotation, then it would become something that is directly used by applications.
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.
@is annotation
That is what I do have on my mind.
api/src/main/java/jakarta/data/metamodel/SortableAttribute.java
Outdated
Show resolved
Hide resolved
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Create test scenarions
Thanks for contributing the unit tests. That helps to confirm the correctness of the changes. |
It does make sense. |
…mposite restriction
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
@njr-11 one more PR against yours. |
@otaviojava I don't see another PR against this anywhere. |
This one here: |
Create more test scenarios to the new interfaces
I see it now and have accepted your PR/commits for more unit tests. Thanks for adding the test coverage. |
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
Signed-off-by: Otavio Santana <[email protected]>
This is the last one: I put the validation on field making the field mandatory |
829 dynamic query restrictions
As a first step, IMHO, it is good to go. @njr-11 @gavinking, what do you think? |
This PR contains some ideas that I was experimenting with locally for dynamic query restrictions #829 and hadn't been planning to propose until after a lot more discussion occurred. However, because code changes are already being proposed and I wanted to illustrate how this should tie into the static metamodel, I decided to share this prematurely so everyone can see what I was originally aiming for with the pattern.
Usage with single restriction,
Usage with multiple restrictions,
This was just the basics, and doesn't yet include not/negation, or Pattern/Range, which might make parts of it unnecessary. It also just occurred to me that if we had a TextRestriction subclass, which I did not include in this, we could omit the
*IgnoreCase
methods and have.ignoreCase()
instead.