REST Query Language med RSQL
Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:
>> KONTROLLER KURSEN UdholdenhedstopJeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:
>> KONTROLLER KURSEN Denne artikel er en del af en serie: • REST Query Language with Spring and JPA Criteria• REST-forespørgselssprog med forårsdata JPA-specifikationer
• REST forespørgselssprog med Spring Data JPA og Querydsl
• REST-forespørgselssprog - Avanceret søgning
• REST Query Language - Implementering ELLER drift
• REST Query Language med RSQL (nuværende artikel) • REST Query Language med Querydsl Web Support
1. Oversigt
I denne femte artikel i serien illustrerer vi opbygning af REST API Query-sproget ved hjælp af et sejt bibliotek - rsql-parser.
RSQL er et supersæt af Feed Item Query Language (FIQL) - en ren og enkel filtersyntaks til feeds; så det passer helt naturligt ind i en REST API. Lad os først tilføje en maven-afhængighed til biblioteket: Og også definere hovedenheden vi skal arbejde med igennem eksemplerne - Bruger: Den måde, hvorpå RSQL-udtryk er repræsenteret internt, er i form af noder, og besøgsmønsteret bruges til at analysere input. Med det i tankerne vil vi implementere RSQLVisitor interface og oprette vores egen besøgendeimplementering - CustomRsqlVisitor: Nu skal vi håndtere vedholdenhed og konstruere vores forespørgsel ud af hver af disse noder. Vi bruger de forårsdata JPA-specifikationer, vi brugte før - og vi implementerer en Specifikation bygherre til konstruer specifikationer ud af hver af disse noder, vi besøger: Bemærk hvordan: For eksempel til en forespørgsel “navn == john" - vi har: Ved konstruktionen af forespørgslen brugte vi en Specifikation: Læg mærke til, hvordan specifikationen bruger generiske produkter og ikke er bundet til nogen bestemt enhed (såsom brugeren). Næste - her er vores enum “RsqlSearchOperation“ som indeholder standard rsql-parser-operatører: Lad os nu teste vores nye og fleksible operationer gennem nogle virkelige scenarier: Først - lad os initialisere dataene: Lad os nu teste de forskellige operationer: I det følgende eksempel - vi søger efter brugere efter deres først og efternavn: Lad os derefter søge efter brugere efter deres fornavn ikke "john": Dernæst - vi vil søge efter brugere med alder bedre end "25”: Dernæst - vi vil søge efter brugere med deres fornavn startende med “jo”: Dernæst - vi vil søge efter brugere deres fornavn er “John”Eller“jack“: Endelig - lad os binde det hele sammen med controlleren: Her er en eksempel-URL: Og svaret: Denne vejledning illustrerede, hvordan man bygger et forespørgsel / søgesprog til en REST API uden at skulle genopfinde syntaksen og i stedet bruge FIQL / RSQL. Det fuld implementering af denne artikel kan findes i GitHub-projektet - dette er et Maven-baseret projekt, så det skal være let at importere og køre som det er.2. Forberedelser
cz.jirutka.rsql rsql-parser 2.1.0
@Entity offentlig klasse bruger {@Id @GeneratedValue (strategi = GenerationType.AUTO) privat Lang id; privat streng fornavn; privat streng efternavn; privat streng e-mail; privat int alder }
3. Parse anmodningen
offentlig klasse CustomRsqlVisitor implementerer RSQLVisitor
offentlig klasse GenericRsqlSpecBuilder {public Specification createSpecification (Node node) {if (node instanceof LogicalNode) {return createSpecification ((LogicalNode) node); } hvis (node-forekomst af ComparisonNode) {return createSpecification ((ComparisonNode) node); } returnere null; } offentlig specifikation createSpecification (LogicalNode logicalNode) {List specs = logicalNode.getChildren () .stream () .map (node -> createSpecification (node)) .filter (Objects :: nonNull) .collect (Collectors.toList ()); Specifikationsresultat = specs.get (0); hvis (logicalNode.getOperator () == LogicalOperator.AND) {for (int i = 1; i <specs.størrelse (); i ++) {resultat = Specification.where (resultat) .og (specs.get (i)) ; }} ellers hvis (logicalNode.getOperator () == LogicalOperator.OR) {for (int i = 1; i <specs.size (); i ++) {result = Specification.where (result) .or (specs.get ( jeg)); }} returner resultat; } offentlig Specifikation createSpecification (ComparisonNode comparisonNode) {Specification result = Specification.where (new GenericRsqlSpecification (comparisonNode.getSelector (), comparisonNode.getOperator (), comparisonNode.getArguments ())); returresultat }}
4. Opret brugerdefineret Specifikation
offentlig klasse GenericRsqlSpecification implementerer specifikation {private strengegenskaber; privat ComparisonOperator-operatør; private liste argumenter; @Override public Predicate toPredicate (Root root, CriteriaQuery query, CriteriaBuilder builder) {List args = castArguments (root); Objektargument = args.get (0); switch (RsqlSearchOperation.getSimpleOperator (operator)) {case EQUAL: {if (argument instanceof String) {return builder.like (root.get (property), argument.toString (). erstatte ('*', '%')) ; } ellers hvis (argument == null) {return builder.isNull (root.get (ejendom)); } andet {return builder.equal (root.get (ejendom), argument); }} sag NOT_EQUAL: {if (argument instanceof String) {return builder.notLike (root. get (property), argument.toString (). erstatte ('*', '%')); } ellers hvis (argument == null) {return builder.isNotNull (root.get (ejendom)); } ellers {return builder.notEqual (root.get (ejendom), argument); }} sag GREATER_THAN: {return builder.greaterThan (root. get (property), argument.toString ()); } sag GREATER_THAN_OR_EQUAL: {return builder.greaterThanOrEqualTo (root. get (property), argument.toString ()); } sag LESS_THAN: {return builder.lessThan (root. get (ejendom), argument.toString ()); } sag LESS_THAN_OR_EQUAL: {return builder.lessThanOrEqualTo (root. get (property), argument.toString ()); } sag IN: returner root.get (ejendom) .in (args); sag NOT_IN: returner builder.not (root.get (ejendom) .in (args)); } returnere null; } privat liste castArguments (endelig rodrod) {Klassetype = root.get (egenskab) .getJavaType (); Liste args = argumenter.stream (). Map (arg -> {if (type.equals (Integer.class)) {return Integer.parseInt (arg);} ellers if (type.equals (Long.class)) {return Long.parseLong (arg);} ellers {return arg;}}). Indsamle (Collectors.toList ()); tilbage args; } // standardkonstruktør, getter, setter}
offentlig 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); privat ComparisonOperator-operatør; private RsqlSearchOperation (ComparisonOperator operator) {this.operator = operator; } offentlig statisk RsqlSearchOperation getSimpleOperator (ComparisonOperator operator) {for (RsqlSearchOperation operation: values ()) {if (operation.getOperator () == operator) {return operation; }} returner null; }}
5. Test søgeforespørgsler
@RunWith (SpringJUnit4ClassRunner.class) @ContextConfiguration (classes = {PersistenceConfig.class}) @ Transactional @ TransactionConfiguration public class RsqlTest {@Autowired private UserRepository repository; privat bruger brugerJohn; privat bruger userTom; @Før offentlig ugyldig init () {userJohn = ny bruger (); userJohn.setFirstName ("john"); userJohn.setLastName ("doe"); userJohn.setEmail ("[email protected]"); brugerJohn.setAge (22); repository.save (userJohn); userTom = ny bruger (); userTom.setFirstName ("tom"); userTom.setLastName ("doe"); userTom.setEmail ("[email protected]"); userTom.setAge (26); repository.save (userTom); }}
5.1. Test lighed
@ Test offentlig ugyldighed givenFirstAndLastName_whenGettingListOfUsers_thenCorrect () {Node rootNode = ny RSQLParser (). Parse ("firstName == john; lastName == doe"); Specifikationsspecifikation = rootNode.accept (ny CustomRsqlVisitor ()); Listeresultater = repository.findAll (spec); assertThat (userJohn, isIn (resultater)); assertThat (userTom, not (isIn (results))); }
5.2. Test Negation
@Test offentlig ugyldighed givenFirstNameInverse_whenGettingListOfUsers_thenCorrect () {Node rootNode = ny RSQLParser (). Parse ("firstName! = John"); Specifikationsspecifikation = rootNode.accept (ny CustomRsqlVisitor ()); Listeresultater = repository.findAll (spec); assertThat (userTom, isIn (resultater)); assertThat (brugerJohn, ikke (isIn (resultater))); }
5.3. Test større end
@ Test offentlig ugyldighed givenMinAge_whenGettingListOfUsers_thenCorrect () {Node rootNode = ny RSQLParser (). Parse ("alder> 25"); Specifikationsspecifikation = rootNode.accept (ny CustomRsqlVisitor ()); Listeresultater = repository.findAll (spec); assertThat (userTom, isIn (resultater)); assertThat (brugerJohn, ikke (isIn (resultater))); }
5.4. Test som
@ Test offentlig ugyldighed givenFirstNamePrefix_whenGettingListOfUsers_thenCorrect () {Node rootNode = ny RSQLParser (). Parse ("firstName == jo *"); Specifikation spec = rootNode.accept (ny CustomRsqlVisitor ()); Listeresultater = repository.findAll (spec); assertThat (userJohn, isIn (resultater)); assertThat (userTom, not (isIn (results))); }
5.5. Prøve I
@Test offentlig ugyldighed givenListOfFirstName_whenGettingListOfUsers_thenCorrect () {Node rootNode = ny RSQLParser (). Parse ("firstName = in = (john, jack)"); Specifikationsspecifikation = rootNode.accept (ny CustomRsqlVisitor ()); Listeresultater = repository.findAll (spec); assertThat (userJohn, isIn (resultater)); assertThat (userTom, not (isIn (results))); }
6. UserController
@RequestMapping (metode = RequestMethod.GET, værdi = "/ brugere") @ResponseBody offentlig Liste findAllByRsql (@RequestParam (værdi = "søg") String-søgning) {Node rootNode = ny RSQLParser (). Parse (søg); Specifikationsspecifikation = rootNode.accept (ny CustomRsqlVisitor ()); returner dao.findAll (spec); }
// localhost: 8080 / brugere? search = firstName == jo *; alder <25
[{"id": 1, "firstName": "john", "lastName": "doe", "email": "[email protected]", "age": 24}]
7. Konklusion
Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:
>> KONTROLLER KURSEN Persistens bund Jeg har lige annonceret det nye Lær foråret kursus med fokus på det grundlæggende i Spring 5 og Spring Boot 2:
>> KONTROLLER KURSEN