引言

业务系统接入Elasticsearch搜索服务后,开始了写查询接口的任务,本篇博客将总结Java操作Elasticsearch一些基本查询的实现。

基础代码

构建查询条件,对应Elasticsearch其实就是构建SearchRequest对象,指定索引库Name,指定索引库Type,创建SearchSourceBuilder对象,根据需求创建QueryBuilder对象,下面是创建和使用构建对象的基础代码:
SearchRequest searchRequest = new SearchRequest();
searchRequest.indices("indexName");
searchRequest.types("indexType");
SearchSourceBuilder builder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
builder.query(boolQueryBuilder);
searchRequest.source(builder);

构造查询条件代码

1. termQuery:精确查询(不分词)

//termQuery 精确匹配,参数(Es中对应字段的keyword,匹配的值)
boolQueryBuilder.filter(QueryBuilders.termQuery("gradeName.keyword", dto.getGradeName()));
使用termQuery要注意的是,Elasticsearch5之后,取消了string类型,将原来的string类型拆分为text和keyword两种类型,他们的区别在于text会对字段进行分词处理,而keyword则不会。下面通过例子来看一下两者的区别:
使用’gradeName.keyword’字段查询,查询条件“六年级”,查询结果如下:

java es 查询按某列排序 java elasticsearch查询_elasticsearch

如果在Elasticsearch5之后,查询条件年级传入"六年级",使用’gradeName’字段查询,是查询不到结果的:
//termQuery 精确匹配,参数(Es中的字段key,匹配的值)
boolQueryBuilder.filter(QueryBuilders.termQuery("gradeName", dto.getGradeName()));

java es 查询按某列排序 java elasticsearch查询_字段_02

原因是在查询中,如果使用’gradeName’查询,是将gradeName作为text类型查询,而使用’gradeName.keyword’,则是将其作为keyword类型查询,前者会对查询内容做分词处理之后再匹配,而后者则是直接精确匹配。
当我们没有对索引字段预先指定mapping,Elasticsearch会进行动态映射,如传入价格字段的值为12,会映射为long类型;对传入ip字段的值为’192.168.0.12’,会映射为ip类型。对于普通字符串,elasticsearch将其映射为text类型,为了保证对这些字段做精确查询和聚合的能力,又同时做了keyword类型的映射,作为该字段的fields属性写到_mapping中。
Elasticsearch的termQuery做的是精确查询而不是分词查询,因此对text类型的字段做term查询则是查不到结果的(除非字段本身经过分词器处理后不变,未被转换或分词)。此时,必须使用gradeName.keyword来对gradeName字段以keyword类型进行精确匹配。

2. matchQuery:匹配查询(分词)

boolQueryBuilder.filter(QueryBuilders.matchQuery("gradeName", dto.getGradeName()));
match query搜索的时候,首先会解析查询字符串,进行分词,然后查询,所以假如我搜索的条件输入的是"六年级",则会把各个年级(一年级至九年级)的数据都查询出来,因为其中都包含’年级’,结果如下:

java es 查询按某列排序 java elasticsearch查询_java_03

而上面提到的term query,输入的查询内容是什么,就会按照什么去查询,并不会解析查询内容,对它分词。

3. queryString:精确查询

QueryStringQueryBuilder queryStringQueryBuilder = new QueryStringQueryBuilder(dto.getGradeName()).field("gradeName").defaultOperator(Operator.AND);
 boolQueryBuilder.must(queryStringQueryBuilder);
这里使用QueryStringQueryBuilder构造查询条件,并没有用keyword类型字段’gradeName.keyword’,也可以查询到结果,并且使用defaultOperator拼接操作条件AND或者OR即可。

4. wildcardQuery:模糊查询

//wildcardQuery 模糊匹配,参数(Es中的字段key,匹配的值)
boolQueryBuilder.filter(QueryBuilders.wildcardQuery("phone.keyword", String.format("*%s*", dto.getPhone())));

5. rangeQuery:范围查询

//范围查询(小学阶段:大于等于1,小于等于6)
boolQueryBuilder.filter(QueryBuilders.rangeQuery("gradeCode").from(1, true).to(6, true));
from和to的第二个参数表明是否包含上限值或下限值。

6. Condition 1 OR Condition 2 的实现

BoolQueryBuilder fuzzyQueryBuilder = new BoolQueryBuilder();
fuzzyQueryBuilder.should(QueryBuilders.wildcardQuery("nickName.keyword", String.format("*%s*", dto.getFuzzyCondition())))
	.should(QueryBuilders.wildcardQuery("phone.keyword", String.format("*%s*", dto.getFuzzyCondition())))
这里的should相当于OR,多个字段之间用should拼接,可实现多个字段模糊查询。

7. (Condition 1 OR Condition 2) AND Condition3 的实现

//构造条件3使用
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//构造条件1和条件2使用
BoolQueryBuilder fuzzyQueryBuilder = new BoolQueryBuilder();
//手机号或昵称
if (StringUtils.isNotBlank(dto.getFuzzyCondition())) {
    //must相当于and,拼接条件,should相当于or,多个字段模糊查询条件拼接
    boolQueryBuilder.must(fuzzyQueryBuilder.should(QueryBuilders.wildcardQuery("nickName.keyword", String.format("*%s*", dto.getFuzzyCondition())))
                          .should(QueryBuilders.wildcardQuery("phone.keyword", String.format("*%s*", dto.getFuzzyCondition()))));
}
//学员类型:付费/未付费
if (dto.getType() != null) {
    boolQueryBuilder.filter(QueryBuilders.termQuery("type", dto.getType()));
}
这里我们需要构建两个QueryBuilder,在QueryBuilder之间,使用must进行拼接,相当于AND条件。

总结

以上总结的都属于一些基本的查询,能满足目前业务系统的需求,以后遇到高级查询再单独进行总结。