Език на заявката REST - Прилагане ИЛИ Операция

ПОЧИВКА Най-горе

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА Тази статия е част от поредица: • Език за заявки REST с критерии Spring и JPA

• REST език за заявки със спецификации Spring Data JPA

• REST език за заявки с Spring Data JPA и Querydsl

• REST Query Language - Операции за разширено търсене

• REST Query Language - Внедряване ИЛИ Операция (текуща статия) • REST Query Language с RSQL

• REST език за заявки с поддръжка на Querydsl Web

1. Общ преглед

В тази бърза статия ще разширим разширените операции за търсене, които сме внедрили в предишната статия, и ще включим базирани на ИЛИ критерии за търсене в нашия REST API Query Language .

2. Подход за прилагане

Преди, по всички критерии, в търсене на параметъра на заявката формира предикати групирани само от и оператора. Нека променим това.

Би трябвало да можем да приложим тази функция или като проста, бърза промяна на съществуващия подход или нов от нулата.

С простия подход ще маркираме критериите, за да покажем, че той трябва да се комбинира с помощта на оператора OR.

Например, тук е URL адресът за тестване на API за „ firstName OR lastName“:

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

Имайте предвид, че ние маркирахме критериите lastName с един кавичка, за да го разграничим. Ще уловим този предикат за оператор OR в обекта ни за критерии - SpecSearchCriteria:

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

Сега, нека модифицираме нашия конструктор на спецификации, UserSpecificationBuilder, за да разгледаме ИЛИ квалифицираните критерии при конструирането на Спецификация :

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. Подобряване на UserController

И накрая, нека настроим нова REST крайна точка в нашия контролер, за да използваме тази функционалност за търсене с оператор OR. Подобрената логика за разбор извлича специалния флаг, който помага при идентифицирането на критериите с оператор OR:

@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. Тест на живо с ИЛИ състояние

В този пример за тестване на живо, с новата крайна точка на API, ще търсим потребители по собственото име „john“ ИЛИ фамилното име „doe“. Обърнете внимание, че параметър lastName има единична кавичка, която го квалифицира като „ИЛИ предикат“:

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. Тест за устойчивост с ИЛИ състояние

Сега, нека извършим същия тест, който направихме по-горе, на нивото на постоянство за потребители с собствено име „john“ ИЛИ фамилно име „doe“ :

@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. Алтернативен подход

При алтернативния подход бихме могли да предоставим заявката за търсене по-скоро като пълна клауза WHERE на SQL заявката.

Например, тук е URL за по-сложно търсене по firstName и възраст:

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

Имайте предвид, че отделихме отделни критерии, оператори и групиращи скоби с интервал, за да образуваме валиден инфиксен израз.

Нека анализираме израза на инфикса с CriteriaParser . Нашият CriteriaParser разделя дадения израз на инфикс в маркери (критерии, скоби, И & ИЛИ оператори) и създава постфикс израз за същия:

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; }

Нека добавим нов метод в нашия конструктор на спецификации, GenericSpecificationBuilder, за да изградим спецификацията за търсене от израза postfix:

 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();
     
    

И накрая, нека добавим друга REST крайна точка в нашия UserController, за да анализираме сложния израз с новия CriteriaParser :

@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. Заключение

В този урок подобрихме нашия език за заявки REST с възможност за търсене с оператор OR.

Пълното изпълнение на тази статия може да бъде намерено в проекта GitHub. Това е проект, базиран на Maven, така че трябва да е лесно да се импортира и да се изпълнява както е.

Напред » REST език на заявката с RSQL « Предишен език на REST заявки - разширени операции за търсене REST отдолу

Току що обявих новия курс Learn Spring , фокусиран върху основите на Spring 5 и Spring Boot 2:

>> ПРЕГЛЕД НА КУРСА