通过java代码实现ES中的常用搜索

目录

测试环境准备

在指定索引下搜索全部(可以指定字段)

通过ids进行搜索

对搜索结果进行分页

match分词搜索

不分词模糊搜索:wildcardQuery与matchPhraseQuery

term 搜索(精确匹配)

multi_match搜索

bool搜索 多条件匹配

filter过滤搜索

sort排序搜索

后续待补充:queryStringQuery,minimumShouldMatch,对检索结果中的关键词进行高亮


测试环境准备

测试环境:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>2.0.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
    <version>2.0.6.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>7.3.0</version>
    <exclusions>
        <exclusion>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>org.elasticsearch</groupId>
    <artifactId>elasticsearch</artifactId>
    <version>7.3.0</version>
</dependency>

2配置 application.yml

spring:
  application:
    name: service-search
eslearn:
  elasticsearch:
    hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔

 3主类代码

@SpringBootApplication
public class SearchApplication {
    public static void main(String[] args) {
        SpringApplication.run(SearchApplication.class,args);
    }
}

配置类:

package com.learn.es.cofig;

import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @author LJM
 * @create 2022/12/10
 */
@Configuration
public class ElasticSearchConfig {
    
    @Value("${eslearn.elasticsearch.hostlist}")
    private String hostList;
    
    @Bean(destroyMethod = "close") //表示连接使用完成后需要关闭
    public RestHighLevelClient restHighLevelClient(){
        String[] split = hostList.split(",");
        //这种写法是考虑到可能会配置多个es节点
        HttpHost[] httpHosts = new HttpHost[split.length];
        for (int i = 0; i < split.length; i++) {
            String item = split[i];
            httpHosts[i] = new HttpHost(item.split(":")[0],Integer.parseInt(item.split(":")[1]),"http");
        }

        return new RestHighLevelClient(RestClient.builder(httpHosts));
    }
    
}

测试项目结构:

 在kibana中把数据插入es中:

delete book   //先删除索引

PUT /book    //往索引中插入数据进行测试,后面在api实现搜索的小节,可以使用从数据库中读取数据 然后把数据插入es库指定的索引

PUT /book/_doc/1
{
"name": "Bootstrap开发",
"description": "Bootstrap是由Twitter推出的一个前台页面开发css框架,是一个非常流行的开发框架,此框架集成了多种页面效果。此开发框架包含了大量的CSS、JS程序代码,可以帮助开发者(尤其是不擅长css页面开发的程序人员)轻松的实现一个css,不受浏览器限制的精美界面css效果。",
"studymodel": "201002",
"price":38.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "bootstrap", "dev"]
}


PUT /book/_doc/2
{
"name": "java编程思想",
"description": "java语言是世界第一编程语言,在软件开发领域使用人数最多。",
"studymodel": "201001",
"price":68.6,
"timestamp":"2019-08-25 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "java", "dev"]
}




PUT /book/_doc/3
{
"name": "spring开发基础",
"description": "spring 在java领域非常流行,java程序员都在用。",
"studymodel": "201001",
"price":88.6,
"timestamp":"2019-08-24 19:11:35",
"pic":"group1/M00/00/00/wKhlQFs6RCeAY0pHAAJx5ZjNDEM428.jpg",
"tags": [ "spring", "java"]
}

测试类代码结构: 

/**
 * @author LJM
 * @create 2022/12/13
 * 测试使用java代码实现es的各种搜索
 */
@SpringBootTest(classes = SearchApplication.class)
@RunWith(SpringRunner.class)
public class TestSearch {
    @Autowired
    RestHighLevelClient client;

// 后面的测试方法会全部写在这个类中 .....

}

在指定索引下搜索全部(可以指定字段)

在kibana中 GET /book/_search 先获取一下本地es库中的book索引下有多少数据。

/**
     *     搜索全部
     *       GET book/_search
     *       {
     *         "query": {
     *          "match_all": {}
     *          }
     *       }
     * @throws IOException
     */
    @Test
    public void testSearchAll() throws IOException {

        //1构建搜索请求  实际生产环境中这个索引名称的获取:①把这个索引名称写在枚举类中,然后从枚举类中获取②从配置文件中获取
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());

        //获取某些字段
        searchSourceBuilder.fetchSource(new String[]{"name"}, new String[]{});

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("score:" + score);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

通过ids进行搜索

 /**
     * 通过ids进行搜索  有的就查询出来,没有的也不会报错
     *     GET /book/_search
     *     {
     *         "query": {
     *            "ids" : {
     *              "values" : ["1", "2", "6"]
     *              }
     *         }
     *     }
     * @throws IOException
     */
    @Test
    public void testSearchIds() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.idsQuery().addIds("1","2","6"));

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

对搜索结果进行分页

    /**
     *     对搜索结果进行分页
     *         GET book/_search
     *         {
     *             "query": {
     *               "match_all": {}
     *            },
     *             "from": 0,
     *             "size": 2
     *         }
     */
    @Test
    public void testSearchPage() throws IOException {
        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchAllQuery());

        //搜索第几页的数据
        int page=1;
        //每页展示几个数据
        //int size=2;  //因为本地es一共就插入了三条数据进行测试,所以可以把大小分别设置为2和3看一下输出效果
        int size=3;
        //下标计算
        int from = (page-1) / size;

        searchSourceBuilder.from(from);
        searchSourceBuilder.size(size);

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

match分词搜索

 /**
     * match搜索  这个是会进行分词搜索的
     *     GET /book/_search
     *     {
     *         "query": {
     *            "match": {
     *             "description": "java程序员"
     *         }
     *       }
     *     }
     * @throws IOException
     */
    @Test
    public void testSearchMatch() throws IOException {
        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        searchSourceBuilder.query(QueryBuilders.matchQuery("description", "java程序员"));


        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

不分词模糊搜索:wildcardQuery与matchPhraseQuery

    /**
     * 不分词模糊搜索   like '%检索词%'
     * @throws IOException
     */
    @Test
    public void testSearchLike() throws IOException {
        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //使用matchPhraseQuery 需要对检索的关键词的前后加 * 否则不是完全的模糊匹配  可以对检索的结果中的检索关键词进行高亮
        searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("description", "*"+"程序员"+"*"));

        //还可以使用wildcardQuery 但是这种就会导致检索的结果不能高亮(这个不太确定,但是我自己试的时候,这样确实是不能对检索结果中的检索词进行高亮)
        //需要在检索的字段名后拼接 ".keyword"  并且也需要对检索词前后添加 *
//        searchSourceBuilder.query(QueryBuilders.wildcardQuery("description"+".keyword","*"+"程序员"+"*"));



        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

term 搜索(精确匹配)

 /**
     * term 搜索  如果字段为keyword那么【存储】和【搜索】都不分词。 搜索的时候相当于在使用 = 符号进行搜索
     *        GET / book / _search
     *         {
     *             "query":{
     *                 "term":{
     *                     "description":"java程序员"
     *                 }
     *             }
     *         }
     * @throws IOException
     */
    @Test
    public void testSearchTerm() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //如果字段的映射是text但是也想要精确匹配,可以这样操作:QueryBuilders.termQuery("description" + ".keyword","java程序员")
        searchSourceBuilder.query(QueryBuilders.termQuery("description", "java语言"));

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

multi_match搜索

    /**
     * multi_match搜索
     *     GET /book/_search
     *     {
     *         "query": {
     *           "multi_match": {
     *             "query": "java程序员",
     *             "fields": ["name", "description"]
     *         }
     *       }
     *     }
     * @throws IOException
     */
    @Test
    public void testSearchMultiMatch() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //第二个参数才是 字段  第一个参数是匹配的内容
        searchSourceBuilder.query(QueryBuilders.multiMatchQuery("java程序员","name","description"));

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

bool搜索 多条件匹配

    /**
     * bool搜索 多条件匹配  and or !=
     *         GET / book / _search
     *         {
     *          "query":{
     *             "bool":{
     *                 "must": [
     *                 {
     *                     "multi_match":{
     *                     "query":"java程序员",
     *                             "fields": ["name", "description"]
     *                     }
     *                 }
     *                 ],
     *                 "should": [
     *                     {
     *                         "match":{
     *                         "studymodel":"201001"
     *                     }
     *                 }
     *                 ]
     *             }
     *         }
     *       }
     * @throws IOException
     */
    @Test
    public void testSearchBool() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //构建multiMatch请求 第一个参数是检索内容 第二个参数是检索的字段
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
        //构建match请求
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", "测试没有的字段");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiMatchQueryBuilder);
        boolQueryBuilder.should(matchQueryBuilder);
        //        boolQueryBuilder.must(matchQueryBuilder); //把should改成must就相当于用 and进行了再一次的过滤 就会查询不到数据

        searchSourceBuilder.query(boolQueryBuilder);
        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

}

filter过滤搜索

过滤搜索和范围搜索的区别:

filter,仅仅只是按照搜索条件过滤出需要的数据而已,不计算任何相关度分数,对相关度没有任何影响。

query,会去计算每个document相对于搜索条件的相关度,并按照相关度进行排序。

    /**
     * filter过滤搜索
     *     GET /book/_search
     *     {
     *         "query": {
     *           "bool": {
     *             "must": [
     *             {
     *                 "multi_match": {
     *                 "query": "java程序员",
     *                         "fields": ["name","description"]
     *                                      }
     *             }
     *       ],
     *             "should": [
     *             {
     *                 "match": {
     *                 "studymodel": "201001"
     *             }
     *             }
     *           ],
     *             "filter": {
     *                 "range": {
     *                     "price": {
     *                         "gte": 50,
     *                          "lte": 90
     *                     }
     *                 }
     *
     *             }
     *         }
     *     }
     *  }
     * @throws IOException
     */
    @Test
    public void testSearchFilter() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //构建multiMatch请求
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
        //构建match请求
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("studymodel", "201001");

        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiMatchQueryBuilder);
        boolQueryBuilder.should(matchQueryBuilder);

        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(50).lte(90));

        searchSourceBuilder.query(boolQueryBuilder);

        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");

        }
    }

sort排序搜索

    /**
     * sort排序搜索
     *     GET /book/_search
     *     {
     *         "query": {
     *         "bool": {
     *             "must": [
     *             {
     *                 "multi_match": {
     *                 "query": "java程序员",
     *                         "fields": ["name","description"]
     *             }
     *             }
     *       ],
     *             "should": [
     *             {
     *                 "match": {
     *                 "studymodel": "201001"
     *             }
     *             }
     *       ],
     *             "filter": {
     *                 "range": {
     *                     "price": {
     *                         "gte": 50,
     *                                 "lte": 90
     *                     }
     *                 }
     *
     *             }
     *         }
     *     },
     *         "sort": [
     *         {
     *             "price": {
     *             "order": "asc"
     *         }
     *         }
     *   ]
     *     }
     * @throws IOException
     */
    @Test
    public void testSearchSort() throws IOException {

        //1构建搜索请求
        SearchRequest searchRequest = new SearchRequest("book");

        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //构建multiMatch请求
        MultiMatchQueryBuilder multiMatchQueryBuilder = QueryBuilders.multiMatchQuery("java程序员", "name", "description");
        //构建match请求
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("studymodel", "201001");

        BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
        boolQueryBuilder.must(multiMatchQueryBuilder);
        boolQueryBuilder.should(matchQueryBuilder);

        boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(50).lte(90));

        searchSourceBuilder.query(boolQueryBuilder);

        //按照价格升序
        searchSourceBuilder.sort("price", SortOrder.ASC);


        searchRequest.source(searchSourceBuilder);

        //2执行搜索
        SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);

        //3获取结果
        SearchHits hits = searchResponse.getHits();

        //数据数据
        SearchHit[] searchHits = hits.getHits();
        System.out.println("--------------------------");
        for (SearchHit hit : searchHits) {
            String id = hit.getId();
            float score = hit.getScore();
            //这个source里面就是我们存储的数据
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            String name = (String) sourceAsMap.get("name");
            String description = (String) sourceAsMap.get("description");
            Double price = (Double) sourceAsMap.get("price");
            System.out.println("id:" + id);
            System.out.println("name:" + name);
            System.out.println("description:" + description);
            System.out.println("price:" + price);
            System.out.println("==========================");
        }
    }

后续待补充:queryStringQuery,minimumShouldMatch,对检索结果中的关键词进行高亮