-
-
Notifications
You must be signed in to change notification settings - Fork 157
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
Nested jpa object search #29
Comments
Did you solve the issue? |
No. I implemented some similar query language by myself. Sorry |
Ok. Thanks. |
Did this some time ago in a pet project sandbox, might help if you are using spring: |
This might be interesting to some: https://github.com/zifnab87/spring-boot-rest-api-helpers |
This may be late but i hope someone will find this useful later. We need to make parse the compound property (e.g This piece of code bases on the skeleton here https://www.baeldung.com/rest-api-search-language-rsql-fiql with some minor correction from the suggestion of @manosbatsis. @AllArgsConstructor
public class GenericRsqlSpecification<T> implements Specification<T> {
private String property;
private ComparisonOperator operator;
private List<String> arguments;
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder builder) {
Path<String> propertyExpression = parseProperty(root);
List<Object> args = castArguments(propertyExpression);
Object argument = args.get(0);
switch (RsqlSearchOperation.getSimpleOrDefaultEqualOperator(operator)) {
case EQUAL:
if (argument instanceof String)
return builder.like(propertyExpression,
argument.toString().replace('*', '%'));
else if (argument == null)
return builder.isNull(propertyExpression);
else return builder.equal(propertyExpression, argument);
case NOT_EQUAL:
if (argument instanceof String)
return builder.notLike(propertyExpression,
argument.toString().replace('*', '%'));
else if (argument == null)
return builder.isNotNull(propertyExpression);
else return builder.notEqual(propertyExpression, argument);
case GREATER_THAN:
return builder.greaterThan(propertyExpression,
argument.toString());
case GREATER_THAN_OR_EQUAL:
return builder.greaterThanOrEqualTo(propertyExpression,
argument.toString());
case LESS_THAN:
return builder.lessThan(propertyExpression,
argument.toString());
case LESS_THAN_OR_EQUAL:
return builder.lessThanOrEqualTo(propertyExpression,
argument.toString());
case IN:
return propertyExpression.in(args);
case NOT_IN:
return builder.not(propertyExpression.in(args));
}
return null;
}
// This method will help us diving deep into nested property using the dot convention
// The originial tutorial did not have this, so it can only parse the shallow properties.
private Path<String> parseProperty(Root<T> root) {
Path<String> path;
if (property.contains(".")) {
// Nested properties
String[] pathSteps = property.split("\\.");
String step = pathSteps[0];
path = root.get(step);
for (int i = 1; i <= pathSteps.length - 1; i++) {
path = path.get(pathSteps[i]);
}
} else {
path = root.get(property);
}
return path;
}
private List<Object> castArguments(Path<?> propertyExpression) {
Class<?> type = propertyExpression.getJavaType();
return arguments.stream().map(arg -> {
if (type.equals(Integer.class)) return Integer.parseInt(arg);
else if (type.equals(Long.class)) return Long.parseLong(arg);
else if (type.equals(Byte.class)) return Byte.parseByte(arg);
else return arg;
}).collect(Collectors.toList());
}
} |
Thanks @perfectstrong, I made a small addition to make it the private Path<String> parseProperty(Root<T> root) {
Path<String> path;
if (property.contains(".")) {
// Nested properties
String[] pathSteps = property.split("\\.");
String step = pathSteps[0];
path = root.get(step);
From lastFrom = root;
for (int i = 1; i <= pathSteps.length - 1; i++) {
if(path instanceof PluralAttributePath) {
PluralAttribute attr = ((PluralAttributePath) path).getAttribute();
Join join = getJoin(attr, lastFrom);
path = join.get(pathSteps[i]);
lastFrom = join;
} else if(path instanceof SingularAttributePath) {
SingularAttribute attr = ((SingularAttributePath) path).getAttribute();
if(attr.getPersistentAttributeType() != Attribute.PersistentAttributeType.BASIC) {
Join join = lastFrom.join(attr, JoinType.LEFT);
path = join.get(pathSteps[i]);
lastFrom = join;
} else {
path = path.get(pathSteps[i]);
}
} else {
path = path.get(pathSteps[i]);
}
}
} else {
path = root.get(property);
}
return path;
}
private Join getJoin(PluralAttribute attr, From from) {
switch (attr.getCollectionType()){
case COLLECTION:
return from.join((CollectionAttribute) attr);
case SET:
return from.join((SetAttribute) attr);
case LIST:
return from.join((ListAttribute) attr);
case MAP:
return from.join((MapAttribute) attr);
default:
return null;
}
} Might be useful to add |
@perfectstrong @austalakov Thanks. Its working |
@austalakov class Department{ Predicate: ename=="test_name";dept.id=="10";dept.name="dname"
Please help me regarding this |
@austalakov
|
Many thanks @perfectstrong !!! |
I came up with a much simpler solution which also prevents joining the same table multiple times in my alternative library. public static Path<?> getDatabasePath(
Root<?> table,
Map<String, Join<?, ?>> joins,
String fieldPath) {
if (!fieldPath.contains(".")) {
return table.get(fieldPath);
}
Path<?> path = table;
From<?, ?> from = table;
String[] fields = fieldPath.split("\\.");
String chain = null;
for (int i = 0; i < fields.length; i++) {
String field = fields[i];
path = from.get(field);
if (chain == null) {
chain = field;
} else {
chain += "." + field;
}
JoinType join = path instanceof PluralAttributePath ? JoinType.INNER
: (path instanceof SingularAttributePath && ((SingularAttributePath<?>) path)
.getAttribute().getPersistentAttributeType() != PersistentAttributeType.BASIC
? JoinType.LEFT
: null);
if (join != null && i < fields.length - 1) {
if (!joins.containsKey(chain)) {
joins.put(chain, from.join(field, join));
}
from = joins.get(chain);
}
}
return path;
} The library supports advanced search features, such as comparisons between fields (numbers, bools, enums, dates, ...), logical operations (including priority), functions, and more. You may check it here for those who lost hope in rsql. |
Hi, great work!
I have only a problem with nested object.
I mix your rsql-parser with specification like explaned by Eugen Paraschiv.
I have 2 entity PERSON and ADDRESS (1,n) .
I want to filter the person who have the address id = 5
When I perform the search action dont work!
http://localhost:9090/api/persons/search?q=addresses.id==5
Error:
Unable to locate Attribute with the the given name [addresses.id]
In my jpa entity Person.addresses exist!
This type of search is supported?
What I wrong?
The text was updated successfully, but these errors were encountered: