0. 概述
本文主要介绍ElasticSearch 基本的查询方式,包括in、等于、不等于、大于等于、小于等于等类似SQL查询方式。主要使用BooleanQuery,所谓的BooleanQuery 就是在搜索时候使用 AND,OR或NOT运算符多个查询结果的文件(must:AND,must_not,NOT,should:OR)。
1.定义操作符
package com.hsc.study;
import java.util.*;
/**
* Created by hsc on 17/4/30.
*/
public enum FieldOperator {
/**
* 等于
*/
EQ("=", "等于"),
/**
* 不等于
*/
NE("!=", "不等于"),
/**
*
*/
IN("in", "包含"),
/**
*
*/
NOT_IN("!in", "不包含"),
/**
* 区间值 大于多少 小于多少
*/
RANGE("range", "区间值"),
/**
* 大于等于
*/
GE(">=", "大于等于"),
/**
* 小于等于
*/
LE("<=", "小于等于");
/**
* 运算符
*/
private final String operator;
/**
* 描述
*/
private final String description;
FieldOperator(String operator, String description) {
this.operator = operator;
this.description = description;
}
public String getOperator() {
return this.operator;
}
public String getDescription() {
return this.description;
}
public static final Map<String, FieldOperator> enumOperatorsMap;
static {
final FieldOperator[] operators = FieldOperator.values();
Map<String, FieldOperator> innerEnumInsHolder =new HashMap<>(operators.length);
for (FieldOperator operator : operators) {
innerEnumInsHolder.put(operator.operator, operator);
}
enumOperatorsMap = Collections.unmodifiableMap(innerEnumInsHolder);
}
}
2. 查询处理
FieldObject 类用于描述在使用哪个字段按照什么样操作方式进行查询。
public class FieldObject {
private String fieldName;//字段名称
private Object fieldValue;//字段值
private FieldOperator operator;//操作类型 in 查询 还是 等于查询
public FieldObject() {
}
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;
}
}
字段处理接口,构造ElasticSearch查询语法。
public interface FieldHandler {
/**
* 针对单个FieldObject构建查询语法
* @param queryBuilder BoolQueryBuilder
* @param fieldObject
*/
void buildQuerySyntaxForElasticSearch(BoolQueryBuilder queryBuilder, FieldObject fieldObject);
}
字段处理抽象类,提供了一个注册功能,也就是FieldOperator中的操作符和对应的handler做个映射处理。
public abstract class AbstractFieldHandler implements FieldHandler {
protected abstract String registryKey();
@Autowired
protected FieldHandlerRegistry register;
@PostConstruct
protected void registry() {
/**
* 注册key
*/
String registryKey = registryKey();
/**
* registryKey不是允许的定义值,异常通知
*/
if (!FieldOperator.enumOperatorsMap.containsKey(registryKey)) {
return;
}
register.register(registryKey, this);
}
}
@Component
public class FieldHandlerRegistry {
private final Map<String, FieldHandler> ruleFieldValueHandlerMap = new ConcurrentHashMap<>(FieldOperator.enumOperatorsMap.size());
public void register(String registryKey, FieldHandler handler) {
FieldHandler oldHandler = ruleFieldValueHandlerMap.get(registryKey);
if (oldHandler != null) {
return;
}
synchronized (ruleFieldValueHandlerMap) {
ruleFieldValueHandlerMap.put(registryKey, handler);
}
}
public FieldHandler getHandler(String registryKey) {
return ruleFieldValueHandlerMap.get(registryKey);
}
}
等于查询:值得强调的是这里使用的是matchQuery,因为matchQuery会对这个字段值做分词,如果是term查询则不会做分词处理,这个具体可以见官方文档。
@Component
public class EQFieldHandler extends AbstractFieldHandler {
@Override
protected String registryKey() {
return FieldOperator.EQ.getOperator();
}
@Override
public void buildQuerySyntaxForElasticSearch(BoolQueryBuilder queryBuilder, FieldObject fieldObject) {
//matchQuery 会分词
queryBuilder.must(QueryBuilders.matchQuery(fieldObject.getFieldName(), fieldObject.getFieldValue()).operator(Operator.AND));
}
}
不等于处理,如果字段没有分词可以用termQuery,比如用户Id 用户名没有分词的字段。
@Component
public class NEFieldHandler extends AbstractFieldHandler {
@Override
protected String registryKey() {
return FieldOperator.NE.getOperator();
}
@Override
public void buildQuerySyntaxForElasticSearch(BoolQueryBuilder queryBuilder, FieldObject fieldObject) {
queryBuilder.mustNot(QueryBuilders.matchQuery(fieldObject.getFieldName(), fieldObject.getFieldValue()));
}
}
大于等于处理
@Component
public class GEFieldHandler extends AbstractFieldHandler {
@Override
protected String registryKey() {
return FieldOperator.GE.getOperator();
}
@Override
public void buildQuerySyntaxForElasticSearch(BoolQueryBuilder queryBuilder, FieldObject fieldObject) {
queryBuilder.filter(QueryBuilders.rangeQuery(fieldObject.getFieldName()).from(fieldObject.getFieldValue()));
}
}
小于等于处理
@Component
public class LEFieldHandler extends AbstractFieldHandler {
@Override
protected String registryKey() {
return FieldOperator.LE.getOperator();
}
@Override
public void buildQuerySyntaxForElasticSearch(BoolQueryBuilder queryBuilder, FieldObject fieldObject) {
queryBuilder.filter(QueryBuilders.rangeQuery(fieldObject.getFieldName()).to(fieldObject.getFieldValue()));
}
}
3.查询语法构建
@Component
public class ElasticSearchQueryBuilder {
@Autowired
private FieldHandlerRegistry registry;
public SearchRequestBuilder buildQuery(QueryContext queryContext) {
final BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
final String esIndex = queryContext.getIndex();
//构建查询语法
buildQuery(queryBuilder, queryContext.getFieldObjects());
return queryContext.getEsClient().prepareSearch()
.setIndices(esIndex)
.setTypes(queryContext.getType())
.setSize(queryContext.getPageSize())
.setFrom(queryContext.getOffset())
.setQuery(queryBuilder);
}
private void buildQuery(BoolQueryBuilder queryBuilder, List<FieldObject> fieldObjects) {
if (fieldObjects == null) {
return;
}
for (FieldObject object : fieldObjects) {
registry.getHandler(object.getOperator().getOperator()).buildQuerySyntaxForElasticSearch(queryBuilder, object);
}
}
}
4 查询类
@Component
public class SearchComponent {
private static final Logger logger = LoggerFactory.getLogger(SearchComponent.class);
@Autowired
private ElasticSearchQueryBuilder elasticSearchQueryBuilder;
public SearchHits elasticSearch(QueryContext queryContext) {
// 构造ES 查询条件
SearchRequestBuilder esSearchRequest = elasticSearchQueryBuilder.buildQuery(queryContext);
// 实行数据查询
SearchResponse esResponse = esSearchRequest.get();
/* 查询ES (数据和总数)*/
return esResponse.getHits();
}
}