Spring boot集成elasticsearch实现全局搜索

spring boot+elasticsearch+canal-adapter+canal-deployer+MySQL实现全局搜索

我们在项目开发中搜索是绝对绕不开的一个问题,当我们数据量小的时候我们可以直接构建条件SQL来得到我们想要的数据,但是当数据积累到一定数量时,普通的条件SQL就会有一个致命的问题(查询效率),没错,当数据量积累到一定的地步普通的SQL语句查询效率会变的及其不理想,这个时候就有了SQL优化,什么建立SQL索引等等一系列的优化方案就出来了,但是还是无法应对大数据查询,这个时候就有了个各种搜索引擎服务器了,常用有solr,elasticsearch,lucene等,选型自己查找数据本文不做详细分析值得一提的时solr和elasticsearch都是基于Lucene的搜索服务器Solr支持更多格式的数据,而Elasticsearch仅支持json格式。
第一步:环境准备
我使用的是spring boot2.3.0.RELEASE+elasticsearch7.7.1+canal-adapter 1.1.5+canal-deployer 1.1.5
首先安装elasticsearch服务器canal-adapter和canal-deployer(我使用的是centos7)
安装elasticsearch我用的是docker安装所以首先需要安装docker安装教程参考以下文章docker安装安装好之后安装elasticsearch首先使用docker搜索命令搜素镜像搜索命令docker search elasticsearch找到我们需要安装的版本使用docker pull docker.io/elasticsearch:7.7.1安装(7.7.1可替换成自己需要安装的版本)然后安装canal canal下载地址canal下载地址下载好解压运行启动脚本即可使用
第二步:spring boot集成实现
创建一个spring boot项目在pom.xml文件里面添加elasticsearch依赖
如下:

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

注意要修改版本为我们安装的版本
如下:

    <properties>
        <elasticsearch.version>7.7.1</elasticsearch.version>
    </properties>

接下来我们需要对elasticsearch进行一些基础配置在application.yml文件中配置(或者application.properties中配置)
配置如下:

elasticsearch:
  hostname: 192.168.149.128   #elasticsearch服务器地址
  port: 9200                  #elasticsearch服务器端口
  index: index                #elasticsearch文档索引

编写ElasticSearchConfig配置文件
如下:

@Configuration
public class ElasticSearchConfig {
    @Value("${elasticsearch.hostname}")
    private String hostname;

    @Value("${elasticsearch.port}")
    private int port;

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        return new RestHighLevelClient(RestClient.builder(new HttpHost(hostname, port, "http")));
    }
}

准备PO对象例如下面这样,就是你要在elasticsearch中储存的字段

@Data
public class ElasticSearchParams {
    private String name;
    private String enName;
    private String subhead;
    private String icon;
    private String uri;
    private Long id;
    private Long pid;
    private Integer weightid;
    private Long count;
    private String addTime;
    private Integer type;
    private String video;
    private String content;
    private String cover;
    private String label;
    private String skip;
    private String extend;
    private String fileUrl;
    private String fileUrlEn;
    private String imgs;
    private String title;
    private Long downloadCount;
}

编写elasticsearch服务类接口

public interface ESService {

    /**
     * 添加数据
     *
     * @return
     * @throws Exception
     */
    Boolean addData() throws Exception;

    /**
     * 搜索
     *
     * @param keyword
     * @param pageNo
     * @param pageSize
     * @return
     * @throws Exception
     */
    List<Map<String, Object>> searchData(String keyword, int pageNo, int pageSize) throws Exception;

    /**
     * 删除索引
     *
     * @return
     * @throws Exception
     */
    Boolean deleteIndex() throws Exception;

    /**
     * 查询索引是否存在
     *
     * @return
     * @throws Exception
     */
    Boolean searchIndex() throws Exception;

    /**
     * 创建索引
     *
     * @return
     * @throws Exception
     */
    String createIndex() throws Exception;
}

编写实现类

@Service
public class ESServiceImpl implements ESService {
    private final static Logger logger = LoggerFactory.getLogger(ESServiceImpl.class);

    @Value("${elasticsearch.index}")
    private String index;

    @Autowired
    private ESMapper esMapper;

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient restHighLevelClient;

    @Override
    public Boolean addData() throws Exception {
        List<ElasticSearchParams> esParams = esMapper.getAllDataList();
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        boolean exists = restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
        //判断索引是否存在  不存在就创建索引
        if (!exists) {
            createIndex();
        }
        //把查询的数据放入es中
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueMinutes(5));
        for (ElasticSearchParams esParam : esParams) {
            bulkRequest.add(new IndexRequest(index)
                    .id("" + esParam.getId())
                    .source(JSON.toJSONString(esParam), XContentType.JSON));
        }
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return !bulkResponse.hasFailures();
    }

    @Override
    @Retryable(value = Exception.class)
    public List<Map<String, Object>> searchData(String keyword, int pageNo, int pageSize) throws Exception {

        //条件搜索
        SearchRequest searchRequest = new SearchRequest(index);
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //分页
        sourceBuilder.from(pageNo - 1);
        sourceBuilder.size(pageSize);

        // 首先构造多关键字查询条件
        MultiMatchQueryBuilder matchQueryBuilder = QueryBuilders.multiMatchQuery(keyword, "title", "content").field("title", 10);
        sourceBuilder.query(matchQueryBuilder);
        sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));

        //高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.fields().add(new HighlightBuilder.Field("title")); // 高亮字段
        highlightBuilder.fields().add(new HighlightBuilder.Field("content")); // 高亮字段
        highlightBuilder.requireFieldMatch(false);
        highlightBuilder.preTags("<span style=\"color:red\">");   //高亮设置
        highlightBuilder.postTags("</span>");
        sourceBuilder.highlighter(highlightBuilder);
        //执行搜索
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        //解析结果
        ArrayList<Map<String, Object>> arrayList = new ArrayList<>();
        for (SearchHit documentFields : searchResponse.getHits().getHits()) {
            Map<String, HighlightField> highlightFields = documentFields.getHighlightFields();
            HighlightField title = highlightFields.get("title");
            HighlightField content = highlightFields.get("content");
            Map<String, Object> sourceAsMap = documentFields.getSourceAsMap();
            //解析高亮字段
            if (title != null) {
                Text[] fragments = title.fragments();
                String n_title = "";
                for (Text text : fragments) {
                    n_title += text;
                }
                sourceAsMap.put("title", n_title);  //高亮字段替换掉原来的内容即可
            }
            if (content != null) {
                Text[] fragments = content.fragments();
                String n_content = "";
                for (Text text : fragments) {
                    n_content += text;
                }
                sourceAsMap.put("content", n_content);  //高亮字段替换掉原来的内容即可
            }
            arrayList.add(sourceAsMap);
        }
        return arrayList;
    }

    @Override
    public Boolean deleteIndex() throws Exception {
        return restHighLevelClient.indices().delete(new DeleteIndexRequest(index), RequestOptions.DEFAULT).isAcknowledged();
    }

    @Override
    public Boolean searchIndex() throws Exception {
        return restHighLevelClient.indices().exists(new GetIndexRequest(index), RequestOptions.DEFAULT);
    }

    @Override
    public String createIndex() throws Exception {
        if (searchIndex()) {
            return "索引已存在";
        }

        //1.创建索引请求
        CreateIndexRequest request = new CreateIndexRequest(index);
        //2.设置索引信息
        request.settings(
                Settings.builder()
                        .put("index.number_of_shards", 5)
                        .put("index.number_of_replicas", 1)
        );
        request.mapping(
                XContentFactory.jsonBuilder()
                        .startObject()
                        .startObject("properties")
                        .startObject("id").field("type", "keyword").field("index", false).endObject()
                        .startObject("pid").field("type", "long").field("index", false).endObject()
                        .startObject("weightid").field("type", "long").field("index", false).endObject()
                        .startObject("count").field("type", "long").field("index", false).endObject()
                        .startObject("addTime").field("type", "text").field("index", false).endObject()
                        .startObject("type").field("type", "integer").field("index", false).endObject()
                        .startObject("video").field("type", "text").field("index", false).endObject()
                        .startObject("content").field("type", "text").field("analyzer", "ik_max_word").field("search_analyzer", "ik_smart").endObject()
                        .startObject("cover").field("type", "text").field("index", false).endObject()
                        .startObject("label").field("type", "text").field("index", false).endObject()
                        .startObject("skip").field("type", "text").field("index", false).endObject()
                        .startObject("extend").field("type", "text").field("index", false).endObject()
                        .startObject("fileUrl").field("type", "text").field("index", false).endObject()
                        .startObject("fileUrlEn").field("type", "text").field("index", false).endObject()
                        .startObject("imgs").field("type", "text").field("index", false).endObject()
                        .startObject("title").field("type", "text").field("analyzer", "ik_max_word").field("search_analyzer", "ik_smart").endObject()
                        .startObject("downloadCount").field("type", "long").field("index", false).endObject()
                        .startObject("name").field("type", "text").field("index", false).endObject()
                        .startObject("enName").field("type", "text").field("index", false).endObject()
                        .startObject("subhead").field("type", "text").field("index", false).endObject()
                        .startObject("icon").field("type", "text").field("index", false).endObject()
                        .startObject("uri").field("type", "text").field("index", false).endObject()
                        .endObject()
                        .endObject()
        );
        //3.执行创建请求
        CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
        //4.返回索引名
        return createIndexResponse.index();
    }
}

编写ESMapper文件

public interface ESMapper {
    //查询数据
    List<ElasticSearchParams> getAllDataList();
}

编写Mapper

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mebay.mapper.TMebayESMapper">
    <resultMap id="esDataListMap" type="com.mebay.entity.ElasticSearchParams">
        <id column="id" jdbcType="BIGINT" property="id" />
        <result column="pid" jdbcType="BIGINT" property="pid" />
        <result column="weightid" jdbcType="INTEGER" property="weightid" />
        <result column="count" jdbcType="BIGINT" property="count" />
        <result column="add_time" jdbcType="TIMESTAMP" property="addTime" />
        <result column="type" jdbcType="INTEGER" property="type" />
        <result column="video" jdbcType="VARCHAR" property="video" />
        <result column="content" jdbcType="VARCHAR" property="content" />
        <result column="cover" jdbcType="VARCHAR" property="cover" />
        <result column="label" jdbcType="VARCHAR" property="label" />
        <result column="skip" jdbcType="VARCHAR" property="skip" />
        <result column="extend" jdbcType="VARCHAR" property="extend" />
        <result column="file_url" jdbcType="VARCHAR" property="fileUrl" />
        <result column="imgs" jdbcType="VARCHAR" property="imgs" />
        <result column="title" jdbcType="VARCHAR" property="title" />
        <result column="download_count" jdbcType="BIGINT" property="downloadCount" />
        <result column="name" jdbcType="VARCHAR" property="name" />
        <result column="enName" jdbcType="VARCHAR" property="enName" />
        <result column="subhead" jdbcType="VARCHAR" property="subhead" />
        <result column="icon" jdbcType="VARCHAR" property="icon" />
        <result column="uri" jdbcType="VARCHAR" property="uri" />
    </resultMap>

    <select id="getAllDataList" resultType="com.mebay.entity.ElasticSearchParams">
        SELECT
         a.id id,
         a.pid pid,
         a.weightid weightid,
         a.count count,
         a.add_time add_time,
         a.type type,
         a.video video,
         a.content content,
         a.cover cover,
         a.label label,
         a.skip skip,
         a.extend extend,
         a.file_url file_url,
         a.imgs imgs,
         a.title title,
         a.download_count download_count,
         m.name name,
         m.en_name en_name,
         m.subhead subhead,
         m.icon icon,
         m.uri uri
         from t_mebay_articles a LEFT JOIN t_mebay_menu m on a.pid = m.id
    </select>
</mapper>

到了这一步已经集成完毕了,但是我们在实际的项目中肯定会涉及到数据的修改、添加、删除,如何才能在我们修改数据库数据时同步数据到es库里面呢这个时候就需要用到canal了
第三步:数据同步
canal介绍:canal是Alibaba的一款开源项目,纯Java开发。基于数据库增量日志解析,提供增量数据订阅&消费,目前主要支持了MySQL(也支持mariaDB)。canal地址配置canal deployer 进入在这里插入图片描述
修改instance.properties文件
vim instance.properties
修改这三个信息
然后进入bin目录启动 执行下面圈中的脚本(./start.sh)
在这里插入图片描述
修改adapter配置
在这里插入图片描述
在这里插入图片描述
刚刚配置中的name对应下面这个文件夹,网上很多教程都是直接写的es但是在新版本中有es6和es7所以我们要根据版本来选择对应的
在这里插入图片描述
然后进入es7文件夹新建一个yml文件

文件内容如下:
在这里插入图片描述
然后保存退出进入bin目录启动adapter
至此我们就实现了elasticsearch实现全局搜索并且数据实时更新
因为我直接用的工程配置展示给大家看的所以很多地方我都马赛克了 最后按照这个步骤行不通的可以联系我:联系方式2511217211

  • 2
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java实现应用程序的全局搜索可以使用Elasticsearch进行实现。引用中的代码片段展示了如何在Java中使用Elasticsearch进行商品的添加操作。你可以通过创建一个Elasticsearch的客户端对象,在该对象中使用IndexRequest来添加商品数据到索引中。 另外,引用中的代码片段展示了如何清除Elasticsearch中的所有商品数据。你可以使用DeleteByQueryRequest来构建一个删除请求,并设置查询条件为匹配所有商品数据。然后使用Elasticsearch的客户端对象执行该删除请求。 除了添加和清除数据,还可以使用Elasticsearch进行搜索操作。你可以创建一个Controller类,如引用中的ElasticSearchController,通过调用ElasticSearchService中的selectItems方法来进行搜索。在该方法中,你可以传入关键字和排序方式作为参数,并返回搜索结果。 总结起来,你可以使用ElasticsearchJava实现应用程序的全局搜索功能。通过添加商品数据、清除数据和执行搜索操作,你可以满足全局搜索的需求。123 #### 引用[.reference_title] - *1* *2* *3* [【Java 实战】通过ElasticSearch实现全局搜索功能](https://blog.csdn.net/qq_34383510/article/details/128296562)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v92^chatsearchT3_1"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值