REST Query Language - OR-bewerking implementeren

REST Top

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS Dit artikel maakt deel uit van een reeks: • REST Query Language met Spring- en JPA-criteria

• REST Query Language met Spring Data JPA-specificaties

• REST Query Language met Spring Data JPA en Querydsl

• REST Query Language - Geavanceerde zoekbewerkingen

• REST Query Language - OR-bewerking implementeren (huidig ​​artikel) • REST Query Language met RSQL

• REST Query-taal met Querydsl-webondersteuning

1. Overzicht

In dit korte artikel zullen we de geavanceerde zoekbewerkingen die we in het vorige artikel hebben geïmplementeerd, uitbreiden en opnemen OF-gebaseerde zoekcriteria in onze REST API Query Language.

2. Implementatiebenadering

Voorheen waren alle criteria in het zoeken queryparameter gevormde predikaten alleen gegroepeerd op EN-operator. Laten we dat veranderen.

We zouden deze functie moeten kunnen implementeren als een eenvoudige, snelle wijziging van de bestaande benadering of als een nieuwe vanaf nul.

Met de eenvoudige benadering markeren we de criteria om aan te geven dat ze moeten worden gecombineerd met de OR-operator.

Hier is bijvoorbeeld de URL om de API te testen op 'voornaam OF achternaam ”:

// localhost: 8080 / users? search = firstName: john, 'lastName: doe

Merk op dat we de criteria hebben gemarkeerd achternaam met een enkel citaat om het te onderscheiden. We zullen dit predikaat voor OR-operator vastleggen in ons criteriumwaardeobject - SpecSearchCriteria:

openbare SpecSearchCriteria (String orPredicate, String-sleutel, SearchOperation-bewerking, Objectwaarde) {super (); this.orPredicate = orPredicate! = null && orPredicate.equals (SearchOperation.OR_PREDICATE_FLAG); this.key = sleutel; this.operation = operatie; this.value = waarde; }

3. UserSpecificationBuilder Verbetering

Laten we nu onze specificatiebouwer aanpassen, UserSpecificationBuilder, om bij het construeren rekening te houden met de OR-gekwalificeerde criteria Specificatie:

openbare specificatie build () {if (params.size () == 0) {return null; } Specificatie resultaat = nieuwe gebruikersspecificatie (params.get (0)); for (int i = 1; i <params.size (); i ++) {result = params.get (i) .isOrPredicate ()? Specification.where (resultaat) .or (nieuwe gebruikersspecificatie (params.get (i))): Specification.where (resultaat). En (nieuwe gebruikersspecificatie (params.get (i))); } resultaat teruggeven; }

4. UserController Verbetering

Laten we tot slot een nieuw REST-eindpunt in onze controller instellen om deze zoekfunctionaliteit met de OR-operator te gebruiken. De verbeterde parseerlogica extraheert de speciale vlag die helpt bij het identificeren van de criteria met de OR-operator:

@GetMapping ("/ users / espec") @ResponseBody openbare lijst findAllByOrPredicate (@RequestParam String zoeken) {Specificatie spec = resolvSpecification (zoeken); retourneer dao.findAll (spec); } beschermde Specificatie resolSpecification (String searchParameters) {UserSpecificationsBuilder builder = nieuwe UserSpecificationsBuilder (); String operationSetExper = Joiner.on ("|") .join (SearchOperation.SIMPLE_OPERATION_SET); Patroonpatroon = Pattern.compile ("(\ p {Punct}?) (\ w +?) (" + OperationSetExper + ") (\ p {Punct}?) (\ w +?) (\ p { Punct}?), "); Matcher matcher = pattern.matcher (searchParameters + ","); while (matcher.find ()) {builder.with (matcher.group (1), matcher.group (2), matcher.group (3), matcher.group (5), matcher.group (4), matcher. groep (6)); } return builder.build (); }

5. Live-test met OF Staat

In dit live testvoorbeeld, met het nieuwe API-eindpunt, zoeken we naar gebruikers op voornaam "john" OF achternaam "doe". Merk op dat parameter achternaam heeft één aanhalingsteken, dat het kwalificeert als een "OF-predikaat":

private String EURL_PREFIX = "// localhost: 8082 / spring-rest-full / auth / users / espec? search ="; @Test openbare ongeldig gegeven gegevenFirstOrLastnaam_whenGettingListOfUsers_thenCorrect () {Antwoordantwoord = gegevenAuth (). Get (EURL_PREFIX + "voornaam: john, 'achternaam: doe"); String resultaat = response.body (). AsString (); assertTrue (result.contains (userJohn.getEmail ())); assertTrue (result.contains (userTom.getEmail ())); }

6. Persistentietest met OF Staat

Laten we nu dezelfde test uitvoeren als hierboven, op persistentieniveau voor gebruikers met voornaam "john" OF achternaam "doe":

@Test openbare ongeldigheid gegevenFirstOrLastName_whenGettingListOfUsers_thenCorrect () {UserSpecificationsBuilder builder = nieuwe UserSpecificationsBuilder (); SpecSearchCriteria spec = nieuwe SpecSearchCriteria ("firstName", SearchOperation.EQUALITY, "john"); SpecSearchCriteria spec1 = nieuwe SpecSearchCriteria ("'", "lastName", SearchOperation.EQUALITY, "doe"); Lijstresultaten = repository .findAll (builder.with (spec) .with (spec1) .build ()); assertThat (results, hasSize (2)); assertThat (userJohn, isIn (resultaten)); assertThat (userTom, isIn (resultaten)); }

7. Alternatieve aanpak

In de alternatieve benadering kunnen we de zoekopdracht meer als een complete zoekopdracht geven WAAR clausule van SQL-query.

Hier is bijvoorbeeld de URL voor een complexere zoekopdracht op Voornaam en leeftijd:

// localhost: 8080 / users? search = (firstName: john OR firstName: tom) EN leeftijd> 22

Merk op dat we afzonderlijke criteria, operatoren en haakjes groeperen met een spatie hebben gescheiden om een ​​geldige tussenvoegseluitdrukking te vormen.

Laten we de tussenvoegseluitdrukking ontleden met een CriteriaParser. Onze CriteriaParser splitst de gegeven tussenvoegseluitdrukking in tokens (criteria, haakjes, EN & OF-operatoren) en maakt een postfix-uitdrukking voor hetzelfde:

openbare Deque-parse (String searchParam) {Deque-uitvoer = nieuwe LinkedList (); Deque stack = nieuwe LinkedList (); Arrays.stream (searchParam.split ("\ s +")). ForEach (token -> {if (ops.containsKey (token)) {while (! Stack.isEmpty () && isHigerPrecedenceOperator (token, stack.peek () )) {output.push (stack.pop (). equalsIgnoreCase (SearchOperation.OR_OPERATOR)? SearchOperation.OR_OPERATOR: SearchOperation.AND_OPERATOR);} stack.push (token.equalsIgnoreCase (SearchOperation.OR_OPERATOR)? );} else if (token.equals (SearchOperation.LEFT_PARANTHESIS)) {stack.push (SearchOperation.LEFT_PARANTHESIS);} else if (token.equals (SearchOperation.RIGHT_PARANTHESIS)) {while (! stack.peek (). equals ( SearchOperation.LEFT_PARANTHESIS)) {output.push (stack.pop ());} stack.pop ();} anders {Matcher matcher = SpecCriteraRegex.matcher (token); while (matcher.find ()) {output.push ( nieuwe SpecSearchCriteria (matcher.group (1), matcher.group (2), matcher.group (3), matcher.group (4), matcher.group (5)));}}}); while (! stack.isEmpty ()) {output.push (stack.pop ()); } terugkeer output; }

Laten we een nieuwe methode toevoegen in onze specificatiebouwer, GenericSpecificationBuilder, om de zoekopdracht te construeren Specificatie van de postfix-uitdrukking:

 openbare specificatie build (Deque postFixedExprStack, Function converter) {Deque specStack = nieuwe LinkedList (); while (! postFixedExprStack.isEmpty ()) {Object mayBeOperand = postFixedExprStack.pollLast (); if (! (mayBeOperand instanceof String)) {specStack.push (converter.apply ((SpecSearchCriteria) mayBeOperand)); } else {Specificatie operand1 = specStack.pop (); Specificatie operand2 = specStack.pop (); if (mayBeOperand.equals (SearchOperation.AND_OPERATOR)) {specStack.push (Specification.where (operand1) .and (operand2)); } else if (mayBeOperand.equals (SearchOperation.OR_OPERATOR)) {specStack.push (Specification.where (operand1) .or (operand2)); }}} retourneer specStack.pop ();

Laten we tot slot nog een REST-eindpunt toevoegen in onze UserController om de complexe uitdrukking te ontleden met de nieuwe CriteriaParser:

@GetMapping ("/ users / spec / adv") @ResponseBody openbare lijst findAllByAdvPredicate (@RequestParam String zoeken) {Specificatie spec = resolSpecificationFromInfixExpr (zoeken); retourneer dao.findAll (spec); } beschermde specificatie resolSpecificationFromInfixExpr (String searchParameters) {CriteriaParser parser = nieuwe CriteriaParser (); GenericSpecificationsBuilder specBuilder = nieuwe GenericSpecificationsBuilder (); retourneer specBuilder.build (parser.parse (searchParameters), UserSpecification :: new); }

8. Conclusie

In deze zelfstudie hebben we onze REST-querytaal verbeterd met de mogelijkheid om te zoeken met een OR-operator.

De volledige implementatie van dit artikel is te vinden in het GitHub-project. Dit is een op Maven gebaseerd project, dus het zou gemakkelijk moeten kunnen worden geïmporteerd en uitgevoerd zoals het is.

De volgende » REST Query Language met RSQL « Vorige REST Query Language - Geavanceerde zoekbewerkingen REST onderaan

Ik heb zojuist het nieuwe aangekondigd Leer de lente natuurlijk, gericht op de basisprincipes van Spring 5 en Spring Boot 2:

>> BEKIJK DE CURSUS