Overview
Using QueryDSL, its possible to derive queries from the attributes contained in a Request query string. For example:
?firstname=Dave&lastname=Matthews
This query would search for users with firstname equal to Dave and Last name equal to matthews.
Problem
How do you use the LIKE operator? For example, suppose I want to fetch a list of users that have the lastname containing the word "Matt". Ive read solutions that propose to bind the fields and instead of using equals, use contains in all of them. This does solve the problem but creates another as it replaces the EQUAL operator. Other proposed solutions is to customize every repository with every possible field of the class, as in : Querydsl web with contains
Current solution
The current solution I have is to search using equals, it does not solve the problem. It does not work with LIKE, CONTAINS, or anything of the sort. So basically I have the method below:
#RequestMapping(value = "/", method = RequestMethod.GET)
String index(Model model,
#QuerydslPredicate(root = User.class) Predicate predicate,
Pageable pageable) {
model.addAttribute("users", repository.findAll(predicate, pageable));
return "index";
}
Another Topic in Stackoverflow
The following topic
How can you use different operators with QueryDSL Web?
Approach a similar issue however using another operators as example. The proposed solution by the user Geert Graat, is to use the library Spring Data QueryDSL Value Operators, which can be found at : https://bitbucket.org/gt_tech/spring-data-querydsl-value-operators/src/master/
However, some questions remain:
A) Is it still valid to 2022?
B) Is this the only and proper solution?
References
https://www.logicbig.com/tutorials/spring-framework/spring-data/query-dsl-web-support.html
https://www.logicbig.com/tutorials/spring-framework/spring-data/web-query-dsl-collection-properties.html
https://gt-tech.bitbucket.io/spring-data-querydsl-value-operators/README.html
https://bitbucket.org/gt_tech/spring-data-querydsl-value-operators/src/master/
https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web.type-safe
Related
I am not sure if that is possible or not and after a lot of research I ended up here to ask for your help or even guidance.
So, let's say I have a json array that has 10 different types of objects inside the array. This is a json that is being retrieved through an API with sports games.
What I need to do is filtering through these objects in my application. I am using JAVA and so far I have ended up that I will use stream filter and predicates. I am aware that I can create different types of predicates and put them in the stream.filter() function, but is it possible to do it somehow dynamically?
For example, I need to filter this array by time. This predicate will be
return p -> p.getTime() > 1;
And then:
return match.stream().filter( predicate ).collect(Collectors.<Match>toList());
What if another filter has another one condition which is team name. Is it possible to add some how the other predicate and also add the "AND" "OR" condition between those two? I need to do this dynamically using one filter function with different predicates.
Is there a way to make something like a custom query to store it in a database and retrieve it and use it like a predicate? Or the predicate itself is it possible to be stored in a database?
If I am completely wrong on this please guide me to find another way to do this. Otherwise a help would be appreciated. Thank you and happy new year to all. :)
This is an interesting problem. And I think this will not be uncommon face as well considering data lake scenarios.
I think, as suggested in a comment above, the way to apply is to have a Predicate. You may have a predicate that applies the conditions as AND or OR and then supply it to the stream processor. Like this (assuming that you have a base class Data to which you have mapped your API output):
/* Create the predicate with the conditions. Showing 2 here with an "AND" combination. */
Predicate<? extends Data> p = d -> d.getTime() > 1;
p.and( d -> d.getName().equals( "Football" ) ); //Consider ".or()" here, if that is what you need.
/* Supply this predicate to the stream processor. */
match.stream().filter( p ).collect(Collectors.<Match>toList());
Using an and() call is the same as calling .filter() one after the other on the stream processor. Something like this:
stream.filter(...).filter(...)...
So, you will be able to construct such a stream call in a for loop.
Is there a way to make something like a custom query to store it in a database and retrieve it and use it like a predicate? Or the predicate itself is it possible to be stored in a database?
You may do this within your Predicate itself. That is, instead writing the logic as shown above, you may make a database call to fetch you Java code. However, you will have to do dynamic compilation using JavaCompiler. That may be a bit complicated. However, you may consider a JVM-based scripting language like Groovy for such things.
I am currently trying to utilize Querydsl in order to work with:
Multiple Search Parameters
Work with a direct match for a field
Work in a contains fashion
Utilize wildcards in the input string
How do I need to set my bindings to work like this? Is it even possible? Can I only have one or two of the above?
Our current setup utilizes the like binding (see below code). I have come across a lot of different things, some suggesting using multibinding but I don't believe that is the correct implementation of this (although it could be and I just don't understand the multibinding concept).
I am thinking that there could be something to do with the .first method utilized below.
#Override
default void customize(QuerydslBindings bindings, QApp root) {
bindings.bind(root.name).first(StringExpression::like);
bindings.bind(root.number).first(StringExpression::like);
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
The wildcard functionality for a contains purpose is not working correctly. Input values in the url look something like: /user?name=%mora% and work very sporadically with different inputs. For instance something like /user?name=%ing% works and returns the proper result set of 2 records but /user?name=%ca% returns an empty result set, even though there is at least one record that fits that search criteria.
What we would like to have happen is all of these things work:
/user?name=Bob (returns all records that have the name Bob)
/user (returns all user records)
/user?name=%bo% (returns all records that have bo in the name field)
/user?name=Bob&number=53 (returns the record(s) with this name and number)
Try this:
#Override
default void customize(QuerydslBindings bindings, QPerson person) {
bindings.bind(String.class).first((SingleValueBinding<StringPath, String>) StringExpression::containsIgnoreCase);
}
It makes case-insensitive 'like' filter for all string properties.
More info is here
I am able to search for a keyword in a column using
List<Application> findByProposalContainingIgnoreCase(String keyword);
How can I achieve the same using a list of keywords?
For example:
List<Application> findByProposalContainingIgnoreCase(List<String> keywords);
UPDATE:
If it is not possible to do so, is the below way effective:
#Autowired
private ApplicationService applicationService;
List<Application> applications = new ArrayList<>();
for (String keyword : keywords) {
applications.addAll(applicationService.findByProposalContainingIgnoreCase(keyword));
}
It's not possible as normal sql cannot do it either. The use of the word 'like' implies searching for all the possibilities that contain the keyword provided. In your case you'd have to string multiple contains statements together like
List<Application> findByProposalContainingIgnoreCaseOrProposalContainingIgnoreCase(String keyword1, String keyword2);
You might be better of doing the inkeyword for your list
List<Application> findByProposalIn(Set<String> proposals);
The problem arises though that now you would have to add both uppercase and lowercase of the proposal to the set as it might not be case sensitive, and it will look for the exact matches not part of the word. Its not ideal but I think it'll work for what you're trying to do.
UPDATE
You're answer by adding the for loop would suffice for what you need.
I have a PagingAndSortingRepository:
public interface BrowserLinkPagination extends PagingAndSortingRepository<BrowserLink, Long> {
List<BrowserLink> findByUserAndUriLike(User user, String uri, Pageable pageable);
}
Now what I want to do is to search for multiple words in the uri column. Like comes pretty close, but it is order dependent on how the words occur in the string.
EDIT for clarification: Order dependences is exactly what I not want. I want the search strings to be independent of order, so LIKE is not what I am looking for.
I guess this is pretty common to find, having several search terms to look for in a string, I know I could implement it by providing the SQL to execute, but I am curiuous if there is a way to express that in terms of spring data? am
I am using postgresql for production and h2 for development / tests.
After reading more about the topic it is kind of obvious I need some kind of fulltext search.
I will start using the one provided by postgresql. I found a short working example: http://rachbelaid.com/postgres-full-text-search-is-good-enough/
Thanks for the comments.
Use In in method name. In Your case it could look like that:
public interface BrowserLinkPagination extends PagingAndSortingRepository<BrowserLink, Long> {
List<BrowserLink> findByUserAndUriIn(User user, List<String> uri, Pageable pageable);
}
When You are using the standard API created by Spring, the usage from the browser URI is pretty simple - just type in the address:
/your_api/browserlink/search/findByUserAndUriIn?user=xxx&uri=uri1,uri2,uri3
I'd like to use Hibernate's Criteria API for precisely what everybody says is probably its most likely use case, applying complex search criteria. Problem is, the table that I want to query against is not composed entirely of primitive values, but partially from other objects, and I need to query against those object's id's.
I found this article from 2 years ago that suggests it's not possible. Here's how I tried it to no avail, there are other aspect of Hibernate where I know of where this sort of dot notation is supported within string literals to indicate object nesting.
if (!lookupBean.getCompanyInput().equals("")) {
criteria.add(Restrictions.like("company.company", lookupBean.getCompanyInput() + "%"));
}
EDIT:
Here's my correctly factored code for accomplishing what I was trying above, using the suggestion from the first answer below; note that I am even using an additional createCriteria call to order on an attribute in yet another associated object/table:
if (!lookupBean.getCompanyValue().equals("")) {
criteria.createCriteria("company").add(
Restrictions.like("company", lookupBean.getCompanyValue() + "%"));
}
List<TrailerDetail> tdList =
criteria.createCriteria("location").addOrder(Order.asc("location")).list();
Not entirely sure I follow your example, but it's certainly possible to specify filter conditions on an associated entity, simply by nesting Criteria objects to form a tree. For example, if I have an entity called Order with a many-to-one relationship to a User entity, I can find all orders for a user named Fred with a query like this:
List<Order> orders = session.createCriteria(Order.class)
.createCriteria("user")
.add(eq("name", "fred"))
.list();
If you're talking about an entity that has a relationship to itself, that should work as well. You can also replace "name" with "id" if you need to filter on the ID of an associated object.