I'm trying to develop a search API with Specification and RSQL. Followed this tutorial - https://www.baeldung.com/rest-api-search-language-rsql-fiql
I have a User entity which have a OneToOne relation with UserProfile.
@Entity
public class User{
@Column(nullable = false)
private String firstName;
@OneToOne(targetEntity = UserProfile.class, fetch = FetchType.EAGER)
@JoinColumn(nullable = false, name = "user_profile_id")
private UserProfile userProfile;
...
@Entity
public class UserProfile{
@Column(nullable = false)
private String education;
...
And Predicate function,
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
List<Object> args = castArguments(root);
Object argument = args.get(0);
switch (RsqlSearchOperation.getSimpleOperator(operator)) {
case EQUAL: {
if (argument instanceof String) {
return builder.like(root.get(property), argument.toString().replace('*', '%'));
} else if (argument == null) {
return builder.isNull(root.get(property));
} else {
return builder.equal(root.get(property), argument);
}
}
case NOT_EQUAL: {
....
When i call the API with parameter ?search=firstName==John , it's returning results as expected. What i need is search by education and return Users having that education. I tried with Join as follows, but it's not working.
if (argument instanceof String) {
Join<User, UserProfile> profileJoin = root.join("user_profile_id");
return builder.like(root.get(property), profileJoin.get(property));
} else if (argument == null) {
Any solution with generic will be really helpful.
Looks like there is no concrete solution with generic. So i did it which is close to being generic.
switch (RsqlSearchOperation.getSimpleOperator(operator)) {
case EQUAL: {
if (doesClassContainProperty(UserProfile.class, property)) {
Join<User, UserProfile> profileJoin = root.join("user_profile_id");
return builder.equal(profileJoin.get(property), argument);
} else {
return builder.equal(root.get(property), argument);
}
}
And here is the method to check whether passed param is in root class or join class
public boolean doesClassContainProperty(Class<?> genericClass, String fieldName) {
return Arrays.stream(genericClass.getDeclaredFields()).anyMatch(f -> f.getName().equals(fieldName));
}
I would like to recommend you another library, which lets you build dynamic filters without having to mess with coding, it supports logical operators, comparators, enums, dates, booleans, searching over relations/joins (no n+1 query problem), functions, and much more: https://github.com/turkraft/spring-filter
Example query:
/search?filter= average (ratings) > 4.5 and brand.name in ('audi', 'land rover') and (year > 2018 or km < 50000) and color : 'white' and accidents is empty
Usage:
@GetMapping(value = "/search")
public List<Entity> search(@EntityFilter Specification<Entity> spec, Pageable page) {
return repo.findAll(spec, page);
}
Don't forget the dependency:
<dependency>
<groupId>com.turkraft</groupId>
<artifactId>spring-filter</artifactId>
<version>0.9.5</version>
</dependency>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.