人生起起伏伏,有风光无限日,也有落魄失魂时,人在低谷时,唯有“熬过去,才会赢”
前言
上一篇文章我们讲了,如何整合spring data elasticsearch,并且通过spring data来操作elasticsearch做简单的增删改查,这一期呢我们来利用spring data来做一些复杂查询,我们会用官方文档的一些例子,将他们的查询表达式来用spring data实现,告诉大家如何来互相转换,授人以鱼不如授人以渔。
01精确值查找
term 查询,可以说是我们最常用的查询,它不支持分词,可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)和不分词文本(keyword)。
query DSL:
{
"term" : {
"name.keyword" : "cc09c406-df37-4a48-9265-a8bcc2d756e3" }
}
query SQL:
SELECT * FROM demo WHERE name = "cc09c406-df37-4a48-9265-a8bcc2d756e3"
spring data elasticsearch:
public void term() {
QueryBuilder queryBuilder = QueryBuilders.termQuery("name.keyword", "cc09c406-df37-4a48-9265-a8bcc2d756e3");
out(queryBuilder);
}
通常,我们做精确查询的时候,是不需要对查询进 行评分计算,我们可以使用constant_score 查询来以非评分模式来执行 term 查询并以1作为统一评分。
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"name.keyword" : "cc09c406-df37-4a48-9265-a8bcc2d756e3"
}
}
}}
需要注意的是,查询text类型的字段是要加上 .keyword的,否则是无法查询的,字符串字段不加 .keyword 代表分词查询,而term是不支持分词查询的。分词查询指的是按es的分词器将字符串分为了多个字符串,只要其中一个被匹配就是为true。字段无需分词可以用keyword
02组合过滤器
在数据日常的查询中,我们经常会朋友组合条件,例如一群人中找出名字叫小熊,年龄为11岁的人,在elasticsearch中,这个时候就需要用到组合过滤器了。
现在有一条SQL:
SELECT * FROM demo WHERE (name = "小熊" OR age = 11) AND (nickName != "乖乖熊")
DSL:
"query" : {"filtered" : {"filter" : {"bool" : {"should" : [{ "term" : {"name" : "小熊"}}, { "term" : {"age" : 11}}], "must_not" : {"term" : {"nickName" : "乖乖熊"}}}}}}
pring data elasticsearch:
public void combinationFilter() {
QueryBuilder termQuery = QueryBuilders.termQuery("name.keyword", "小熊");
QueryBuilder termQuery1 = QueryBuilders.termQuery("age", 11);
QueryBuilder termQuery2 = QueryBuilders.termQuery("nickName", "乖乖熊");
QueryBuilder queryBuilder = QueryBuilders.boolQuery().should(termQuery).should(termQuery1).mustNot(termQuery2);
out(queryBuilder);
}
嵌套布尔查询
bool 是一个复合的过滤器,可以接受多个子过滤器,我们可以将一个 bool 过滤器放在其他 bool 过滤器内部,以便我们做一些复杂的处理。
现在有一条SQL:
SELECT * FROM demo WHERE name = "小熊" OR (nickName = "小笨熊" AND age = 30)
DSL:
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"name" : "小熊"}},
{ "bool" : {
"must" : [
{ "term" : {"nickName" : "小笨熊"}},
{ "term" : {"age" : 30}}
]
}}
]
}
}
}
}
spring data elasticsearch:
public void nestedBooleanQuery() {
QueryBuilder termQuery = QueryBuilders.termQuery("name.keyword", "小熊");
QueryBuilder termQuery1 = QueryBuilders.termQuery("nickName", "小笨熊");
QueryBuilder termQuery2 = QueryBuilders.termQuery("age", 10);
QueryBuilder queryBuilder1 = QueryBuilders.boolQuery().must(termQuery1).must(termQuery2);
QueryBuilder queryBuilder = QueryBuilders.boolQuery().must(termQuery).must(queryBuilder1);
out(queryBuilder);
}
03查找多个精确值
我们往往在一个字段,可能需要匹配多个精确值,例如,我现在去找个名字叫小熊和叫小笨熊的11岁的人,那么我们首先想到会用两个term,但是实际上elasticsearch已经给我们提过了terms,顾名思义就是允许多参数的term。
DSL:
{
"terms" : {
"name.keyword" : ["小熊", "小笨熊"] }
}
spring data elasticsearch:
public void termsQuery() {
QueryBuilder queryBuilder = QueryBuilders.termsQuery("name.keyword", "小熊", "小笨熊");
out(queryBuilder);
}
04范围搜索
现在,我们要找出10-30岁的人,那么就必然要用到范围搜索了,范围搜索在日常还是很常见的,不多BB了,直接上菜。
SQL:
SELECT * FROM demo WHERE age BETWEEN 10 AND 30
DSL:
"range" : {
"age" : {
"gte" : 10,
"lte" : 30 }
}
spring data elasticsearch:
public void rangeQuery() {
QueryBuilder queryBuilder = QueryBuilders.rangeQuery("age").gte(10).lte(30);
out(queryBuilder);
}
注:range 查询同样可以处理字符串字段,字符串范围采用的是字典顺序。同时,由于数字和时间是采用索引的方式查询,所以它们的查询是高效的,但是字符串是将词语分词,每个词都通过term过滤查询,所以效率会低很多。
05非Null搜索
数据总是多变的,往往会存在空值,elasticsearch是使用的倒排索引来存储数据,而如果一个字段的值是null,它是不会存入倒排索引的,倒排索引存储的只是一个 token 列表和与之相关的文档信息,如果字段不存在,就没有文档信息,那么就拿不到token,就跟不可能存入倒排索引中了。对于这种情况,elasticsearch提供了exists来方便查询,我们直接上菜,看SQL与DSL的转换,更容易懂。
SQL:
SELECT * FROM demo WHERE name IS NOT NULL
DSL:
"query" : {
"constant_score" : {
"filter" : {
"exists" : { "field" : "name.keyword" }
}
}
}
spring data elasticsearch:
public void notNullQuery() {
QueryBuilder queryBuilder = QueryBuilders.existsQuery("name.keyword");
out(queryBuilder);
}
文末,贴出代码中out方法的代码:
private void out(QueryBuilder queryBuilder) {
nativeSearchQueryBuilder = nativeSearchQueryBuilder.withQuery(queryBuilder);
SearchQuery searchQuery = nativeSearchQueryBuilder.build();
List<DemoDO> demoDOList = elasticsearchTemplate.queryForList(searchQuery, DemoDO.class);
logger.info("总数:" + demoDOList.size());
for (DemoDO demoDO : demoDOList) {
System.out.println("id: " + demoDO.getId() + " name: " + demoDO.getName() + " age: " + demoDO.getAge() + " nickName: " + demoDO.getNickName());
}
}
总 结
本章讲解了使用elasticsearch来做深入搜索,贴上了SQL DSL 和 代码,方便大家的学习和理解,我立志于更好的帮助大家学习技术,希望能用最简单的方式帮助大家提升。文章中涉及到的代码,会在整部实操课程结束,放到github上开源,到时候大家不懂的可以下载看看,也可以保存下来,随时cv大法。