جاوا و تکنولوژی های آن

java programming language

در این وبلاگ به بررسی نکات موجود در جاوا و تکنولوژی های آن می پردازیم

طبقه بندی موضوعی


در بخش های قبلی دیدیم که یکسری عملیات پیچیده کوئری های Criteria را توانسته بودیم بصورت AND ترکیب کنیم در این بخش به پیاده سازی عمل OR و ترکیب آن با سایر عملیات میپردازیم 


ابتدا برای مشخص کردن عملیات درخواستی OR از سمت کلاینت نیاز است کلاسی برای آن ایجاد کنیم :

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


کوئری میتواند از سمت کلاینت شکلی مشابه این حالت داشته باشد :

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

در اینجا عمل or با یک ' تک کوتیشن مشخص شده است


حال تغییراتی در کلاس UserSpecificationBuilder میدهیم که بتواند عمل or را تشخیص دهد :

public Specification<User> build() {
    if (params.size() == 0) {
        return null;
    }
    Specification<User> 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;
 }



ساخت کلاس کنترلری که به درخواست های فیلتر/سرچ رسیدگی میکند :

@GetMapping("/users/espec")
@ResponseBody
public List<User> findAllByOrPredicate(@RequestParam String search) {
    Specification<User> spec = resolveSpecification(search);
    return dao.findAll(spec);
}
 
protected Specification<User> 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();
}



تست همراه با عمل 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()));
}



دو عمل or ترکیب شده در یک کوئری :

@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<User> results = repository
      .findAll(builder.with(spec).with(spec1).build());
 
    assertThat(results, hasSize(2));
    assertThat(userJohn, isIn(results));
    assertThat(userTom, isIn(results));
}


فرض کنید شکل عملیاتی که از کلاینت میخواهیم دریافت کنیم شبیه به SQL باشد مانند حالت زیر :

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


برای تجزیه کردن عملیات ارسالی :

public Deque<?> parse(String searchParam) {
 
    Deque<Object> output = new LinkedList<>();
    Deque<String> 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;
}


کلاس Specification Builder ای که قبلا ساخته بودیم هم میتوانیم با ارائه متد زیر بهینه تر کنیم :

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



همینطور برای تست این حالت پیچیده تر نیاز است تغییراتی در کنترلر بدهیم که در زیر کنترلر را در آدرس دیگری تعریف میکنیم :

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








نظرات  (۰)

هیچ نظری هنوز ثبت نشده است

ارسال نظر

ارسال نظر آزاد است، اما اگر قبلا در بیان ثبت نام کرده اید می توانید ابتدا وارد شوید.
شما میتوانید از این تگهای html استفاده کنید:
<b> یا <strong>، <em> یا <i>، <u>، <strike> یا <s>، <sup>، <sub>، <blockquote>، <code>، <pre>، <hr>، <br>، <p>، <a href="" title="">، <span style="">، <div align="">
تجدید کد امنیتی