반복 ElasticSearch 중첩 조회

9453 단어 ElasticSearch

0 개요


실제 업무에서 우리는 자주 and와 or, 그리고 상관없는 플러그인 조회 방식을 만날 수 있다. 본고는 귀속 방식을 이용하여 이런 상대적으로 복잡한 플러그인 조회를 실현한다.

1 인스턴스 분석


아래와 같이 사용자 테이블에name이 빨간색이고 그의 나이는 10 이상이거나 15 이상이어야 합니다
select * from user where name=' ' and  (age=10 or  age >15)


or 쓰기 인스턴스 조회
select * from user where objectType=13 and  (userId=1234 or  userType =2 )
query  
{
    "query": {
        "bool": {
            "must": [
                {
                    "term": {
                        "objectType": 13
                    }
                },
                {
                    "bool": {
                        "should": [
                            {
                                "bool": {
                                    "must": [
                                        {
                                            "term": {
                                                "userId": 1234
                                            }
                                        }
                                    ]
                                }
                            },
                            {
                                "bool": {
                                    "must": [
                                        {
                                            "term": {
                                                "userType": 2
                                            }
                                        }
                                    ]
                                }
                            }
                        ]
                    }
                }
            ]
        }
    }
}

//JSON 내보내기
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;


public class QueryBuilderJSON {
  

  public static void main(String[] args) {
    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
    queryBuilder.must(QueryBuilders.termQuery("objectType", 13));

    final BoolQueryBuilder and = QueryBuilders.boolQuery();
    queryBuilder.must(and);
    final BoolQueryBuilder or1 = QueryBuilders.boolQuery();
    final BoolQueryBuilder or2 = QueryBuilders.boolQuery();
    and.should(or1);
    and.should(or2);
    RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("created").from(12);

    or1.must(QueryBuilders.termQuery("useType", 1));
    or2.filter(rangeQueryBuilder);

    System.out.print(searchSourceBuilder.query(queryBuilder).toString());
  }
}


2 실현


ElasticSearch의 기본 조회 방식은 제가 이전에 쓴 ElasticSearch 데이터 조회를 참고할 수 있습니다. 이것은 이전에 쓴 코드에 재구성하여 이런 복잡한 플러그인 조회를 더욱 잘 지원합니다.질의 조건 정의(열에 대한 작업)
public class Condition {
  private String fieldName;// 
  private Object fieldValue;// 
  private FieldOperator operator;// 

  public String getFieldName() {
    return fieldName;
  }

  public void setFieldName(String fieldName) {
    this.fieldName = fieldName;
  }

  public Object getFieldValue() {
    return fieldValue;
  }

  public void setFieldValue(Object fieldValue) {
    this.fieldValue = fieldValue;
  }

  public FieldOperator getOperator() {
    return operator;
  }

  public void setOperator(FieldOperator operator) {
    this.operator = operator;
  }
}


중첩된 쿼리, 하위 쿼리의
public class HybridCondition extends Condition {

  private Connector connector;
  private List subConditions=new ArrayList<>();

  public HybridCondition(Connector connector) {
    this.connector = connector;
  }

  public Connector getConnector() {
    return connector;
  }

  public void setConnector(Connector connector) {
    this.connector = connector;
  }

  public List getSubConditions() {
    return subConditions;
  }
  // 
  public void setSubConditions(
      List subConditions) {
    this.subConditions = subConditions;
  }
  public HybridCondition addCondition(Condition condition) {
    // , 
    if (condition == this) {
      return this;
    }
    subConditions.add(condition);
    return this;
  }
}

public enum Connector {
  AND("and"),
  OR("or"),
  EMPTY("");

  private String connName;

  Connector(String connName) {
    this.connName = connName;
  }

  public String getConnName() {
    return connName;
  }

  public static boolean isEmpty(Connector conn) {
    return conn == null || conn == EMPTY;
  }

}


반복적으로 이런 플러그인 조회를 실현하다
@Component
public class ElasticSearchQueryBuilder {

  @Autowired
  private FieldObjectHandlerRegistry registry;
  private final static int MAX_DEPTH=200;

  public ESSearchRequest buildQuery(QueryContext queryContext) {
    final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
    final String esIndex = queryContext.getIndex();
    // 
    buildQuery(queryBuilder, queryContext.getFieldObjects());
    ESSearchRequest esSearchRequest = queryContext.getEsClient().buildingESSearchRequest()
        .setIndices(esIndex)
        .setTypes(queryContext.getType())
        .setSize(queryContext.getPageSize())
        .setFrom(queryContext.getOffset())
        .setQuery(queryBuilder);
    // 
    setSearchRequestBuilderSort(esSearchRequest, queryContext);
    return esSearchRequest;
  }

  private void setSearchRequestBuilderSort(ESSearchRequest esSearchRequest,
      QueryContext queryContext) {
    if (queryContext.getSortRules() == null) {
      return;
    }
    for (SortRule sortRule : queryContext.getSortRules()) {
      if (ContentConstant.SORT_FIELD.contains(sortRule.getField())) {
        esSearchRequest.addSort(sortRule.getField(), sortOrder(sortRule.getSort()));
      }
    }
  }

  private SortOrder sortOrder(final String order) {
    return "asc".equalsIgnoreCase(order) ? SortOrder.ASC : SortOrder.DESC;
  }

  private void buildQuery(BoolQueryBuilder queryBuilder, List conditions) {
    if (CollectionUtils.isEmpty(conditions)) {
      return;
    }
    for (Condition object : conditions) {
      buildQueryBuilder(object,queryBuilder,0);
    }
  }

  private void buildQueryBuilder(Condition queryCondition,BoolQueryBuilder queryBuilder,int depth) {
    if (depth > MAX_DEPTH) {
      // , , 
      throw new IllegalArgumentException("query condition loop depth is too big.");
    }
    if (queryCondition instanceof HybridCondition) {
      final BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
      HybridCondition hybridCondition = (HybridCondition) queryCondition;
      if (Connector.AND.equals(hybridCondition.getConnector())) {
        for (Condition condition : hybridCondition.getSubConditions()) {
          buildQueryBuilder(condition,boolQueryBuilder,depth+1);
        }
        queryBuilder.must(boolQueryBuilder);
      }else if (Connector.OR.equals(hybridCondition.getConnector())) {
        for (Condition condition : hybridCondition.getSubConditions()) {
          buildQueryBuilder(condition,boolQueryBuilder,depth+1);
        }
        queryBuilder.should(boolQueryBuilder);
      }
      return;
    }
    registry.getHandler(queryCondition.getOperator().getOperator()).
        buildQuerySyntaxForElasticSearch(queryBuilder, queryCondition);
  }


}


각 조회 조건은builder 모드로 실현된다
public class ConditionBuilder {

  private List conditions;

  public ConditionBuilder() {
    conditions = new ArrayList<>();
  }

  public ConditionBuilder buildName(String name) {
    if (name != null) {
      conditions.add(buildCondition("name",name, FieldOperator.EQ));
    }
    return this;
  }
  
  public ConditionBuilder builderUserIds(List userIds) {
    if (CollectionUtils.isNotEmpty(userIds)) {
      conditions.add(buildCondition("userId", userIds, FieldOperator.IN));
    }
    return this;
  }
  
  // 
  public ConditionBuilder builderAge(Integer equalAge,Integer largeAge) {
    HybridCondition hybridCondition = new HybridCondition(Connector.AND);
    if (equalAge!=null) {
      HybridCondition temp = new HybridCondition(Connector.OR);
      temp.addCondition(buildCondition("age", equalAge, FieldOperator.EQ));
      hybridCondition.addCondition(temp);
    }

    if (largeAge!=null) {
      HybridCondition temp = new HybridCondition(Connector.OR);
      temp.addCondition(buildCondition("age", equalAge, FieldOperator.GE));
      hybridCondition.addCondition(temp);
    }
      if (!hybridCondition.getSubConditions().isEmpty()) {
      this.fieldObjects.add(hybridCondition);
    }
    return this;

  }
  

  public List getConditions() {
    return conditions;
  }

  private Condition buildCondition(String fieldName, Object value, FieldOperator operator) {
    Condition condition = new Condition();

    condition.setFieldName(fieldName);
    condition.setFieldValue(value);
    condition.setOperator(operator);
    return condition;
  }


}

좋은 웹페이지 즐겨찾기