Merge pull request #192 from Doha2012/master

add rsql parser test
This commit is contained in:
Eugen 2015-04-09 21:59:12 +03:00
commit d0dcf49b73
7 changed files with 340 additions and 1 deletions

View File

@ -107,7 +107,15 @@
<artifactId>querydsl-jpa</artifactId>
<version>3.6.2</version>
</dependency>
<!-- Rsql -->
<dependency>
<groupId>cz.jirutka.rsql</groupId>
<artifactId>rsql-parser</artifactId>
<version>2.0.0</version>
</dependency>
<!-- web -->
<dependency>

View File

@ -0,0 +1,34 @@
package org.baeldung.persistence.dao.rsql;
import org.baeldung.persistence.model.User;
import org.springframework.data.jpa.domain.Specification;
import cz.jirutka.rsql.parser.ast.AndNode;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.OrNode;
import cz.jirutka.rsql.parser.ast.RSQLVisitor;
public class CustomRsqlVisitor<T> implements RSQLVisitor<Specification<User>, Void> {
private UserRsqlSpecBuilder builder;
public CustomRsqlVisitor() {
builder = new UserRsqlSpecBuilder();
}
@Override
public Specification<User> visit(final AndNode node, final Void param) {
return builder.createSpecification(node);
}
@Override
public Specification<User> visit(final OrNode node, final Void param) {
return builder.createSpecification(node);
}
@Override
public Specification<User> visit(final ComparisonNode node, final Void params) {
return builder.createSpecification(node);
}
}

View File

@ -0,0 +1,28 @@
package org.baeldung.persistence.dao.rsql;
import cz.jirutka.rsql.parser.ast.ComparisonOperator;
import cz.jirutka.rsql.parser.ast.RSQLOperators;
public enum RsqlSearchOperation {
EQUAL(RSQLOperators.EQUAL), NOT_EQUAL(RSQLOperators.NOT_EQUAL), GREATER_THAN(RSQLOperators.GREATER_THAN), GREATER_THAN_OR_EQUAL(RSQLOperators.GREATER_THAN_OR_EQUAL), LESS_THAN(RSQLOperators.LESS_THAN), LESS_THAN_OR_EQUAL(RSQLOperators.LESS_THAN_OR_EQUAL), IN(
RSQLOperators.IN), NOT_IN(RSQLOperators.NOT_IN);
private ComparisonOperator operator;
private RsqlSearchOperation(final ComparisonOperator operator) {
this.operator = operator;
}
public static RsqlSearchOperation getSimpleOperator(final ComparisonOperator operator) {
for (final RsqlSearchOperation operation : values()) {
if (operation.getOperator() == operator) {
return operation;
}
}
return null;
}
public ComparisonOperator getOperator() {
return operator;
}
}

View File

@ -0,0 +1,58 @@
package org.baeldung.persistence.dao.rsql;
import java.util.ArrayList;
import java.util.List;
import org.baeldung.persistence.model.User;
import org.springframework.data.jpa.domain.Specifications;
import cz.jirutka.rsql.parser.ast.ComparisonNode;
import cz.jirutka.rsql.parser.ast.LogicalNode;
import cz.jirutka.rsql.parser.ast.LogicalOperator;
import cz.jirutka.rsql.parser.ast.Node;
public class UserRsqlSpecBuilder {
public Specifications<User> createSpecification(final Node node) {
if (node instanceof LogicalNode) {
return createSpecification((LogicalNode) node);
}
if (node instanceof ComparisonNode) {
return createSpecification((ComparisonNode) node);
}
return null;
}
public Specifications<User> createSpecification(final LogicalNode logicalNode) {
final List<Specifications<User>> specs = new ArrayList<Specifications<User>>();
Specifications<User> temp;
for (final Node node : logicalNode.getChildren()) {
temp = createSpecification(node);
if (temp != null) {
specs.add(temp);
}
}
Specifications<User> result = specs.get(0);
if (logicalNode.getOperator() == LogicalOperator.AND) {
for (int i = 1; i < specs.size(); i++) {
result = Specifications.where(result).and(specs.get(i));
}
}
else if (logicalNode.getOperator() == LogicalOperator.OR) {
for (int i = 1; i < specs.size(); i++) {
result = Specifications.where(result).or(specs.get(i));
}
}
return result;
}
public Specifications<User> createSpecification(final ComparisonNode comparisonNode) {
final Specifications<User> result = Specifications.where(new UserRsqlSpecification(comparisonNode.getSelector(), comparisonNode.getOperator(), comparisonNode.getArguments()));
return result;
}
}

View File

@ -0,0 +1,94 @@
package org.baeldung.persistence.dao.rsql;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import org.baeldung.persistence.model.User;
import org.springframework.data.jpa.domain.Specification;
import cz.jirutka.rsql.parser.ast.ComparisonOperator;
public class UserRsqlSpecification implements Specification<User> {
private String property;
private ComparisonOperator operator;
private List<String> arguments;
public UserRsqlSpecification(final String property, final ComparisonOperator operator, final List<String> arguments) {
super();
this.property = property;
this.operator = operator;
this.arguments = arguments;
}
@Override
public Predicate toPredicate(final Root<User> root, final CriteriaQuery<?> query, final CriteriaBuilder builder) {
final List<Object> args = castArguments(root);
final Object argument = args.get(0);
switch (RsqlSearchOperation.getSimpleOperator(operator)) {
case EQUAL: {
if (argument instanceof String) {
return builder.like(root.<String> 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: {
if (argument instanceof String) {
return builder.notLike(root.<String> get(property), argument.toString().replace('*', '%'));
} else if (argument == null) {
return builder.isNotNull(root.get(property));
} else {
return builder.notEqual(root.get(property), argument);
}
}
case GREATER_THAN: {
return builder.greaterThan(root.<String> get(property), argument.toString());
}
case GREATER_THAN_OR_EQUAL: {
return builder.greaterThanOrEqualTo(root.<String> get(property), argument.toString());
}
case LESS_THAN: {
return builder.lessThan(root.<String> get(property), argument.toString());
}
case LESS_THAN_OR_EQUAL: {
return builder.lessThanOrEqualTo(root.<String> get(property), argument.toString());
}
case IN:
return root.get(property).in(args);
case NOT_IN:
return builder.not(root.get(property).in(args));
}
return null;
}
// === private
private List<Object> castArguments(final Root<User> root) {
final List<Object> args = new ArrayList<Object>();
final Class<? extends Object> type = root.get(property).getJavaType();
for (final String argument : arguments) {
if (type.equals(Integer.class)) {
args.add(Integer.parseInt(argument));
} else if (type.equals(Long.class)) {
args.add(Long.parseLong(argument));
} else {
args.add(argument);
}
}
return args;
}
}

View File

@ -10,6 +10,7 @@ import org.baeldung.persistence.dao.MyUserPredicatesBuilder;
import org.baeldung.persistence.dao.MyUserRepository;
import org.baeldung.persistence.dao.UserRepository;
import org.baeldung.persistence.dao.UserSpecificationsBuilder;
import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor;
import org.baeldung.persistence.model.MyUser;
import org.baeldung.persistence.model.User;
import org.baeldung.web.util.SearchCriteria;
@ -29,6 +30,9 @@ import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.mysema.query.types.expr.BooleanExpression;
import cz.jirutka.rsql.parser.RSQLParser;
import cz.jirutka.rsql.parser.ast.Node;
@Controller
public class UserController {
@ -91,6 +95,14 @@ public class UserController {
return mydao.findAll(exp);
}
@RequestMapping(method = RequestMethod.GET, value = "/users/rsql")
@ResponseBody
public List<User> findAllByRsql(@RequestParam(value = "search") final String search) {
final Node rootNode = new RSQLParser().parse(search);
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
return dao.findAll(spec);
}
// API - WRITE
@RequestMapping(method = RequestMethod.POST, value = "/users")

View File

@ -0,0 +1,105 @@
package org.baeldung.persistence.query;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.collection.IsIn.isIn;
import static org.hamcrest.core.IsNot.not;
import java.util.List;
import org.baeldung.persistence.dao.UserRepository;
import org.baeldung.persistence.dao.rsql.CustomRsqlVisitor;
import org.baeldung.persistence.model.User;
import org.baeldung.spring.PersistenceConfig;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.annotation.Transactional;
import cz.jirutka.rsql.parser.RSQLParser;
import cz.jirutka.rsql.parser.ast.Node;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { PersistenceConfig.class })
@Transactional
@TransactionConfiguration
public class RsqlTest {
@Autowired
private UserRepository repository;
private User userJohn;
private User userTom;
@Before
public void init() {
userJohn = new User();
userJohn.setFirstName("john");
userJohn.setLastName("doe");
userJohn.setEmail("john@doe.com");
userJohn.setAge(22);
repository.save(userJohn);
userTom = new User();
userTom.setFirstName("tom");
userTom.setLastName("doe");
userTom.setEmail("tom@doe.com");
userTom.setAge(26);
repository.save(userTom);
}
@Test
public void givenFirstAndLastName_whenGettingListOfUsers_thenCorrect() {
final Node rootNode = new RSQLParser().parse("firstName==john;lastName==doe");
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
final List<User> results = repository.findAll(spec);
assertThat(userJohn, isIn(results));
assertThat(userTom, not(isIn(results)));
}
@Test
public void givenFirstNameInverse_whenGettingListOfUsers_thenCorrect() {
final Node rootNode = new RSQLParser().parse("firstName!=john");
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
final List<User> results = repository.findAll(spec);
assertThat(userTom, isIn(results));
assertThat(userJohn, not(isIn(results)));
}
@Test
public void givenMinAge_whenGettingListOfUsers_thenCorrect() {
final Node rootNode = new RSQLParser().parse("age>25");
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
final List<User> results = repository.findAll(spec);
assertThat(userTom, isIn(results));
assertThat(userJohn, not(isIn(results)));
}
@Test
public void givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect() {
final Node rootNode = new RSQLParser().parse("firstName==jo*");
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
final List<User> results = repository.findAll(spec);
assertThat(userJohn, isIn(results));
assertThat(userTom, not(isIn(results)));
}
@Test
public void givenListOfFirstName_whenGettingListOfUsers_thenCorrect() {
final Node rootNode = new RSQLParser().parse("firstName=in=(john,jack)");
final Specification<User> spec = rootNode.accept(new CustomRsqlVisitor<User>());
final List<User> results = repository.findAll(spec);
assertThat(userJohn, isIn(results));
assertThat(userTom, not(isIn(results)));
}
}