REST-Abfragesprache - Implementierung einer ODER-Operation

REST Top

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> PRÜFEN SIE DEN KURS Dieser Artikel ist Teil einer Reihe: • REST-Abfragesprache mit Spring- und JPA-Kriterien

• REST-Abfragesprache mit Spring Data JPA-Spezifikationen

• REST-Abfragesprache mit Spring Data JPA und Querydsl

• REST-Abfragesprache - Erweiterte Suchvorgänge

• REST-Abfragesprache - Implementierung der ODER-Operation (aktueller Artikel) • REST-Abfragesprache mit RSQL

• REST-Abfragesprache mit Querydsl-Webunterstützung

1. Übersicht

In diesem kurzen Artikel werden wir die erweiterten Suchvorgänge, die wir im vorherigen Artikel implementiert haben, erweitern und OR-basierte Suchkriterien in unsere REST-API-Abfragesprache aufnehmen .

2. Implementierungsansatz

Bevor alle Kriterien in den Suchabfrageparameter gebildet Prädikate gruppiert nur durch AND - Operator. Lassen Sie uns das ändern.

Wir sollten in der Lage sein, diese Funktion entweder als einfache, schnelle Änderung des bestehenden Ansatzes oder als neuen von Grund auf neu zu implementieren.

Mit dem einfachen Ansatz kennzeichnen wir die Kriterien, um anzuzeigen, dass sie mit dem Operator OR kombiniert werden müssen.

Hier ist beispielsweise die URL zum Testen der API auf " Vorname ODER Nachname":

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

Beachten Sie, dass wir die Kriterien lastName mit einem einfachen Anführungszeichen gekennzeichnet haben, um sie zu unterscheiden. Wir werden dieses Prädikat für den OR-Operator in unserem Kriterienwertobjekt - SpecSearchCriteria - erfassen:

public SpecSearchCriteria( String orPredicate, String key, SearchOperation operation, Object value) { super(); this.orPredicate = orPredicate != null && orPredicate.equals(SearchOperation.OR_PREDICATE_FLAG); this.key = key; this.operation = operation; this.value = value; }

3. UserSpecificationBuilder- Verbesserung

Ändern Sie nun unseren Spezifikations-Builder UserSpecificationBuilder, um die ODER-qualifizierten Kriterien bei der Erstellung der Spezifikation zu berücksichtigen :

public Specification build() { if (params.size() == 0) { return null; } Specification result = new UserSpecification(params.get(0)); for (int i = 1; i < params.size(); i++) { result = params.get(i).isOrPredicate() ? Specification.where(result).or(new UserSpecification(params.get(i))) : Specification.where(result).and(new UserSpecification(params.get(i))); } return result; }

4. Verbesserung des UserControllers

Lassen Sie uns abschließend einen neuen REST-Endpunkt in unserem Controller einrichten, um diese Suchfunktion mit dem Operator OR zu verwenden. Die verbesserte Parsing-Logik extrahiert das spezielle Flag, das bei der Identifizierung der Kriterien mit dem OR-Operator hilft:

@GetMapping("/users/espec") @ResponseBody public List findAllByOrPredicate(@RequestParam String search) { Specification spec = resolveSpecification(search); return dao.findAll(spec); } protected Specification resolveSpecification(String searchParameters) { UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); String operationSetExper = Joiner.on("|") .join(SearchOperation.SIMPLE_OPERATION_SET); Pattern pattern = 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.group(6)); } return builder.build(); }

5. Live-Test mit ODER- Bedingung

In diesem Live-Testbeispiel suchen wir mit dem neuen API-Endpunkt nach Benutzern mit dem Vornamen "john" ODER dem Nachnamen "doe". Beachten Sie, dass der Parameter lastName ein einfaches Anführungszeichen enthält, das ihn als "ODER-Prädikat" qualifiziert:

private String EURL_PREFIX = "//localhost:8082/spring-rest-full/auth/users/espec?search="; @Test public void givenFirstOrLastName_whenGettingListOfUsers_thenCorrect() { Response response = givenAuth().get(EURL_PREFIX + "firstName:john,'lastName:doe"); String result = response.body().asString(); assertTrue(result.contains(userJohn.getEmail())); assertTrue(result.contains(userTom.getEmail())); }

6. Persistenztest mit ODER- Bedingung

Lassen Sie uns nun denselben Test wie oben auf der Persistenzstufe für Benutzer mit dem Vornamen "John" oder dem Nachnamen "Doe" durchführen :

@Test public void givenFirstOrLastName_whenGettingListOfUsers_thenCorrect() { UserSpecificationsBuilder builder = new UserSpecificationsBuilder(); SpecSearchCriteria spec = new SpecSearchCriteria("firstName", SearchOperation.EQUALITY, "john"); SpecSearchCriteria spec1 = new SpecSearchCriteria("'","lastName", SearchOperation.EQUALITY, "doe"); List results = repository .findAll(builder.with(spec).with(spec1).build()); assertThat(results, hasSize(2)); assertThat(userJohn, isIn(results)); assertThat(userTom, isIn(results)); }

7. Alternativer Ansatz

Im alternativen Ansatz könnten wir die Suchabfrage eher wie eine vollständige WHERE- Klausel der SQL-Abfrage bereitstellen .

Hier ist beispielsweise die URL für eine komplexere Suche nach Vorname und Alter:

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

Beachten Sie, dass wir einzelne Kriterien, Operatoren und Gruppierungsklammern durch ein Leerzeichen getrennt haben, um einen gültigen Infix-Ausdruck zu bilden.

Analysieren wir den Infix-Ausdruck mit einem CriteriaParser . Unser CriteriaParser teilt den angegebenen Infix-Ausdruck in Token (Kriterien, Klammern, AND & OR-Operatoren) auf und erstellt einen Postfix-Ausdruck für denselben:

public Deque parse(String searchParam) { Deque output = new LinkedList(); Deque stack = new 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) ? SearchOperation.OR_OPERATOR : SearchOperation.AND_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(); } else { Matcher matcher = SpecCriteraRegex.matcher(token); while (matcher.find()) { output.push(new SpecSearchCriteria( matcher.group(1), matcher.group(2), matcher.group(3), matcher.group(4), matcher.group(5))); } } }); while (!stack.isEmpty()) { output.push(stack.pop()); } return output; }

Lassen Sie uns eine neue Methode in unserer Spezifikation Builder hinzufügen, GenericSpecificationBuilder, um die Suche zu konstruieren Spezifikation aus dem Postfix Ausdruck:

 public Specification build(Deque postFixedExprStack, Function
    
      converter) { Deque
     
       specStack = new LinkedList(); while (!postFixedExprStack.isEmpty()) { Object mayBeOperand = postFixedExprStack.pollLast(); if (!(mayBeOperand instanceof String)) { specStack.push(converter.apply((SpecSearchCriteria) mayBeOperand)); } else { Specification operand1 = specStack.pop(); Specification 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)); } } } return specStack.pop();
     
    

Fügen Sie abschließend einen weiteren REST-Endpunkt in unseren UserController ein , um den komplexen Ausdruck mit dem neuen CriteriaParser zu analysieren :

@GetMapping("/users/spec/adv") @ResponseBody public List findAllByAdvPredicate(@RequestParam String search) { Specification spec = resolveSpecificationFromInfixExpr(search); return dao.findAll(spec); } protected Specification resolveSpecificationFromInfixExpr(String searchParameters) { CriteriaParser parser = new CriteriaParser(); GenericSpecificationsBuilder specBuilder = new GenericSpecificationsBuilder(); return specBuilder.build(parser.parse(searchParameters), UserSpecification::new); }

8. Fazit

In diesem Tutorial haben wir unsere REST-Abfragesprache um die Möglichkeit erweitert, mit einem ODER-Operator zu suchen.

Die vollständige Implementierung dieses Artikels finden Sie im GitHub-Projekt. Dies ist ein Maven-basiertes Projekt, daher sollte es einfach zu importieren und auszuführen sein.

Weiter » REST-Abfragesprache mit RSQL « Vorherige REST-Abfragesprache - Erweiterte Suchvorgänge REST unten

Ich habe gerade den neuen Learn Spring- Kurs angekündigt , der sich auf die Grundlagen von Spring 5 und Spring Boot 2 konzentriert:

>> Überprüfen Sie den Kurs