【ES】Springboot整合ElasticSearch

Springboot整合ElasticSearch(全文搜索引擎)

先简单聊几句

es的基础知识或者说大家使用es过程中有问题的话可以看看这篇博客
我个人感觉博主写的很好,图文并茂【https://www.cnblogs.com/hong-fithing/p/11221020.html】
而且也写了自己遇到的一些问题(其实我就瞥了几眼,毕竟我也没遇到什么问题)

我个人不喜欢在自己博客里插图,因为插图太麻烦了,我也就偶尔笔记里插几张图(之前每次写笔记很多图,最后放博客的时候都很累人)

不闲聊不闲聊说正事了

以下所有代码我都一一测试过了,没有问题(不过测试代码我没有放上来 ,因为写的太随意了)

需要大家注意以下几点
1 在启动Springboot测试之前必须先点击elasticsearch.bat,打开elasticsearch;同时在cmd中使用npm run start打开可视化界面(注意文件夹路径必须在elasticsearch-head-master下)
2 可能还有人没有elasticsearch的资源,去我网盘拿吧
    链接:【https://pan.baidu.com/s/1sucTQgMH8JHpMT8ISmXa5Q】
    提取码:tgs4
    里面包含四个压缩包:我们主要用的是
        elasticsearch-7.6.1-windows-x86_64 就是我们的elasticsearch
        elasticsearch-head-master 就是elasticsearch的可视化界面
        elasticsearch-analysis-ik-7.6.1 就是我们的ik分词器,需要放在elasticsearch下的plugins(插件)里
    kibana-7.6.1-windows-x86_64 也是一个可视化页面,我们可以使用它直接进行增删改查操作,我们测试过程中就不用这个了,大家平时可以用它去练习
3 我所写的工具类的返回值大家可以根据需求进行修改,稍微注意一点就行,不要太死板
4 说一说最重要的一个问题,就是搜索问题,如果大家确定好哪个字段以后要高亮,可以按照我实体类中做的那样,指明所使用的的ik分词器,这个我强烈建议
5 最后再强调一点,模糊查询的话使用matchQuery,精确查询使用matchPhraseQuery(查询关键字中英文均可),不推荐termQuery(好像有bug)
6 大家可以去看看代码了。如果大家从我网盘下载东西了,希望大家点赞收藏我的博客,有问题的话可以留讨论区,也可以私信我。谢谢大家!
1.pom.xml配置文件导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.kuang</groupId>
    <artifactId>es</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>es</name>
    <description>Project for Spring Boot</description>

    <properties>
        <java.version>11</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>
2.实体类 可以配置存储时和搜索时使用的分词器
package com.kuang.pojo;

import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.stereotype.Component;

@Component
public class User {
    
    
    // index:是否设置分词  analyzer:存储时使用的分词器  searchAnalyze:搜索时使用的分词器  store:是否存储  type: 数据类型
    @Field(analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
    private String name;
    private int age;

    public User() {
    
    
    }

    public User(String name, int age) {
    
    
        this.name = name;
        this.age = age;
    }

    public String getName() {
    
    
        return name;
    }

    public void setName(String name) {
    
    
        this.name = name;
    }

    public int getAge() {
    
    
        return age;
    }

    public void setAge(int age) {
    
    
        this.age = age;
    }

    @Override
    public String toString() {
    
    
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
3.EsConfig配置类用于提供RestHighLevelClient对象
package com.kuang.config;

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

@Configuration
public class EsConfig {
    
    
    @Bean
    public RestHighLevelClient restHighLevelClient(){
    
    
        RestHighLevelClient client = new RestHighLevelClient(
                RestClient.builder(
                        new HttpHost("127.0.0.1", 9200, "http")));
        return client;
    }
}
4.索引的操作(工具类)
package com.kuang.utils;

import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class EsIndexUtils {
    
    

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    // 创建索引  index参数为要创建的索引名(或者说数据库名)
    public boolean createIndex(String index) throws IOException {
    
    
        // 1.创建索引请求
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        // 2.执行请求 获取响应
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
        // 3.返回响应
        return createIndexResponse.isAcknowledged();// 创建成功则返回true
    }

    // 获取索引  即判断索引是否存在  index参数为要判断的索引名(或者说数据库名)
    public boolean isIndexExist(String index) throws IOException {
    
    
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        return exists;// 存在则返回true
    }

    // 删除索引  index参数为要删除的索引名(或者说数据库名)
    public boolean deleteIndex(String index) throws IOException {
    
    
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse delete = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return delete.isAcknowledged();// 删除成功则返回true
    }
}
5.文档的操作(工具类)
package com.kuang.utils;

import com.alibaba.fastjson.JSON;
import com.kuang.pojo.User;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.MatchPhraseQueryBuilder;
import org.elasticsearch.index.query.MatchQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Component
public class EsDataUtils {
    
    
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    // 添加文档  user为实体类对象  index参数为要保存到的索引名(或者说数据库名)
    public void addData(User user,String index) throws IOException {
    
    
        // 创建请求
        IndexRequest indexRequest = new IndexRequest(index);
        // 设置规则  put /index/_doc/1
        indexRequest.id("1");
        indexRequest.timeout("1s");
        // 数据放入请求
        IndexRequest source = indexRequest.source(JSON.toJSONString(user), XContentType.JSON);
        // 执行请求  获取响应
        IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
        System.out.println(indexResponse.status());// 成功则返回CREATED
    }

    // 判断文档是否存在  id为要获取的文档编号
    public boolean isDataExist(String index,String id) throws IOException {
    
    
        GetRequest getRequest = new GetRequest(index, id);
        // 不获取返回的上下文 即_source 可以节省时间
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);
        return exists;// 成功则返回true
    }

    // 获取文档内容
    public Map<String,Object> getData(String index, String id) throws IOException {
    
    
        GetRequest getRequest = new GetRequest(index, id);
        GetResponse documentFields = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
        return documentFields.getSourceAsMap();
    }

    // 更新文档内容
    public void updateData(String index, String id, User user) throws IOException {
    
    
        UpdateRequest updateRequest = new UpdateRequest(index, id);
        updateRequest.timeout("1s");
        // 设置规则  POST /index/_doc/id/_update
        updateRequest.doc(JSON.toJSONString(user),XContentType.JSON);
        UpdateResponse updateResponse = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
        System.out.println(updateResponse.status());// 成功则输出ok
    }

    // 删除文档
    public void deleteData(String index,String id) throws IOException {
    
    
        DeleteRequest deleteRequest = new DeleteRequest(index,id);
        deleteRequest.timeout("1s");
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        System.out.println(deleteResponse.status());// 成功则输出ok
    }

    // 批量插入文档  userList存储实体类数据
    public void bulkAddData(String index,List<User> userList) throws IOException {
    
    
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("10s");
        for(int i=0;i<userList.size();i++){
    
    
            bulkRequest.add(
                    new IndexRequest(index)
                    .source(JSON.toJSONString(userList.get(i)),XContentType.JSON)
            );
        }
        BulkResponse bulkItemResponses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        System.out.println(!bulkItemResponses.hasFailures());
    }

    // 查询文档
    public  List<Map<String,Object>> searchData(String index,String keyword,int pageNo,int pageSize) throws IOException {
    
    
        SearchRequest searchRequest = new SearchRequest(index);
        // 构建搜索条件
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // matchPhraseQuery精确查询(也就是说不会分词)  matchQuery是模糊查询(先分词再查询)    推荐:相关度查询使用matchQuery,精确字段查询使用matchPhraseQuery(中英文均可)
        // 参数一:es中存储的字段 参数二:要搜索的关键字
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("name", keyword);
        searchSourceBuilder.query(matchQueryBuilder);
        //MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("name", keyword);
        //searchSourceBuilder.query(matchPhraseQueryBuilder);
        searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS));
        // 高亮显示
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("name");// 高亮的字段
        highlightBuilder.requireFieldMatch(false);// 高亮字段中,如果需要高亮显示的字段值有多个,则均高亮显示
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");// 高亮的样式
        searchSourceBuilder.highlighter(highlightBuilder);
        // 分页
        searchSourceBuilder.from(pageNo);
        searchSourceBuilder.size(pageSize);
        // 执行请求
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        // 解析结果
        List<Map<String,Object>> userList=new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
    
    
            // 获取高亮字段
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            HighlightField title = highlightFields.get("name");
            // 获取高亮前的结果
            Map<String,Object> sourceAsMap=documentFields.getSourceAsMap();
            // 置换高亮字段  也就是更新操作
            if(title!=null){
    
    
                Text[] fragments = title.fragments();
                String n_name="";
                for (Text fragment : fragments) {
    
    
                    n_name+=fragment;
                }
                sourceAsMap.put("name",n_name);// 执行替换操作
            }
            userList.add(sourceAsMap);
        }
        return userList;
    }
}

猜你喜欢

转载自blog.csdn.net/kieson_uabc/article/details/107717579