在实际工作中,我们会经常遇到一些and 与 or 以及非相关嵌套的查询方式,本文采用递归方式来实现这种相对复杂的嵌套查询。
如下我们要查询用户表中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());
}
}
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;
}
}