[ElasticSearch从入门到场景实战]深入搜索之结构化搜索实战

9150e4e5ly1fiox2y5169j20gy0io768.jpg

人生起起伏伏,有风光无限日,也有落魄失魂时,人在低谷时,唯有“熬过去,才会赢”


前言

上一篇文章我们讲了,如何整合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.withFilter(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大法。


小手一抖,点赞就有



猜你喜欢

转载自juejin.im/post/5e9438d46fb9a03c387f44d1