add rsql parser test
This commit is contained in:
		
							parent
							
								
									7a61d8e790
								
							
						
					
					
						commit
						6e34100ae6
					
				| @ -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> | ||||
|  | ||||
| @ -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); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -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") | ||||
|  | ||||
| @ -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))); | ||||
|     } | ||||
| } | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user