1. Обзор
В этой быстрой статье мы расширим расширенные поисковые операции, которые мы реализовали в предыдущая статья и включают Or-основанные критерии поиска в нашем языке запроса REST API .
2. Подход к осуществлению
Раньше все критерии в поиск параметр запроса формируется предикатами, сгруппированными только оператором AND. Давайте изменим это.
Мы должны быть в состоянии реализовать эту функцию либо как простое, быстрое изменение существующего подхода, либо как новое с нуля.
При простом подходе мы помеем критерии, указывающие на то, что он должен быть объединен с помощью оператора OR.
Например, вот URL для проверки API на ” первоеимя ИЛИ последнееимя”:
http://localhost:8080/users?search=firstName:john,'lastName:doe
Обратите внимание, что мы отметили критерии последнееимя с одной цитатой, чтобы дифференцировать его. Мы будем захватывать этот предикат для оператора OR в нашем объекте значения критериев – SpecSearchКритерия:
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, учитывать критерии OR при строительстве Спецификация
public Specificationbuild() { 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. Улучшение управления пользователями
Наконец, давайте навеем новую конечную точку REST в нашем контроллере, чтобы использовать эту функциональность поиска с оператором OR. Улучшенная логика разбора извлекает специальный флаг, который помогает в определении критериев с оператором OR:
@GetMapping("/users/espec") @ResponseBody public ListfindAllByOrPredicate(@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. Тест в реальном 2000 году с условием OR
В этом примере живого теста, с новой конечной точкой API, мы будем искать пользователей по имени “Джон” ИЛИ фамилия “doe”. Обратите внимание, что последнееимя имеет одну цитату, которая квалифицирует его как “OR предикат”:
private String EURL_PREFIX = "http://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. Испытание настойчивости с условием ИЛИ
Теперь давайте выполнить тот же тест, который мы сделали выше, на уровне настойчивости для пользователей с именем “Джон” ИЛИ фамилия “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"); Listresults = repository .findAll(builder.with(spec).with(spec1).build()); assertThat(results, hasSize(2)); assertThat(userJohn, isIn(results)); assertThat(userTom, isIn(results)); }
7. Альтернативный подход
В альтернативном подходе мы могли бы предоставить поисковый запрос, больше похожий на полную ГДЕ оговорка запроса S’L.
Например, вот URL для более сложного поиска по первоеимя и возраст:
http://localhost:8080/users?search=( firstName:john OR firstName:tom ) AND age>22
Обратите внимание, что мы разделили отдельные критерии, операторы и группировки скобки с пространством для формирования действительного выражения infix.
Давайте разобрать выражение infix с помощью КритерииПарсер . Наша КритерииПарсер делит данное выражение infix на токены (критерии, скобки, и операторы OR) и создает выражение postfix для того же:
public Deque> parse(String searchParam) { Deque
Давайте добавим новый метод в нашу спецификацию строитель, 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 в нашем ПользовательКонтроллер разобрать сложное выражение с помощью нового КритерииПарсер :
@GetMapping("/users/spec/adv") @ResponseBody public ListfindAllByAdvPredicate(@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 основе проекта, поэтому она должна быть легко импортировать и работать, как она есть.