欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Spring boot 整合Elasticsearch相关内容及排坑

程序员文章站 2022-04-25 19:46:16
...

一.环境配置

(一)父工程依赖

<!--spring boot 父启动器依赖,使用2.1.6版本-->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
    </parent>
 
 
    <dependencyManagement>
        <dependencies>
 
            <!--SCN-->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!--SCA -->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.1.0.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
 
 
 
    <dependencies>
        <!--web依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--日志依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-logging</artifactId>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <!--lombok工具-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.4</version>
            <scope>provided</scope>
        </dependency>
        <!-- Actuator可以帮助你监控和管理Spring Boot应用-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--热部署-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
 
        <!--Apache开源组织提供的用于操作JAVA BEAN的工具包-->
        <dependency>
            <groupId>commons-beanutils</groupId>
            <artifactId>commons-beanutils</artifactId>
            <version>1.9.1</version>
        </dependency>
 
        <!--引入Jaxb,开始-->
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-core</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>2.2.11</version>
        </dependency>
        <dependency>
            <groupId>org.glassfish.jaxb</groupId>
            <artifactId>jaxb-runtime</artifactId>
            <version>2.2.10-b140310.1920</version>
        </dependency>
        <dependency>
            <groupId>javax.activation</groupId>
            <artifactId>activation</artifactId>
            <version>1.1.1</version>
        </dependency>
        <!--引入Jaxb,结束-->
  </dependencies>
 
    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>11</source>
                    <target>11</target>
                    <encoding>utf-8</encoding>
                </configuration>
            </plugin>
            <!--打包插件-->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <goals>
                            <goal>repackage</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

(二)子工程依赖

<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lagou-parent</artifactId>
        <groupId>com.lagou</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>lagou-service-ecsearch</artifactId>

    <dependencies>

        <!-- 数据库 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.32</version>
        </dependency>
        <dependency>
            <groupId>com.lagou</groupId>
            <artifactId>lagou-service-common</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <!--ES高级Rest Client-->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.4.3</version>
        </dependency>

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

        <!--ES所用到的依赖结束-->

        <!--Spring cloud alibaba nacos客户端的依赖-->
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
    </dependencies>

</project>

(三)子工程yml配置

server:
  port: 9202    #微服务的集群环境中,通常会为每一个微服务叠加。
  tomcat:
    uri-encoding: UTF-8  #解决url参数中文的问题。
spring:
  application:
    name: lagou-service-ecsearch
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://192.168.148.160:3307/lagou?useUnicode=true&characterEncoding=utf8&serverTimezone=UTC
    username: root
    password: 123456
  data:
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.148.160:9300
      repositories:
        enabled: true
  elasticsearch:
    rest:
      uris: ["192.168.148.160:9200"]
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.159.1:8848 #nacos server 地址

注意:

1. elasticsearch:
    rest:
      uris: ["192.168.148.160:9200"]

此项的配置,主要原因是,添加elasticsearch依赖后,Spring boot 启动时,会自动扫描本地的elasticsearch地址并进行http端口的连接,而我们的elasticsearch服务器往往不在本地,如此以来并会报错导致启动失败,因此在yml配置文件里须加上以上配置,指定我方elasticsearch服务器的真实IP及暴露的HTTP端口号。

2. 我们在做索引库微服务时,往往业务代码会与elasticsearch索引库产生联系,有时候并不会与mysql等关系型数据库连接,但我们父工程内往往整合了mysql等依赖。因此在此类单纯使用elasticsearch索引库的微服务启动类上方,不要加mapper扫描的注解,且启动类上方的@SpringBootApplication注解应写成如下方式,避免启动时自动配置:@SpringBootApplication(exclude= {DataSourceAutoConfiguration.class})

二.queryBuilder的应用案例

(一)查询需求

  • 查询条件:name

  • 分页查询:每页5条

  • 排序规则:price,升序

  • 结果过滤:商品价格范围过滤

  • 实现高亮效果

(二)查询结果高亮显示工具类

package com.lagou.ecsearch.resultmapper;

import com.google.gson.Gson;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.data.domain.Pageable;
import org.springframework.data.elasticsearch.core.SearchResultMapper;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.aggregation.impl.AggregatedPageImpl;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 自定义结果映射,处理高亮
 */
public class ESSearchResultMapper implements SearchResultMapper {
    /**
     * 完成结果映射
     * 操作的重点应该是将原有的结果:_source取出来,放入高亮的数据
     *
     * @param response
     * @param clazz
     * @param pageable
     * @param <T>
     * @return AggregatedPage需要三个参数进行构建:pageable,List<product>,总记录数
     */
    @Override
    public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {
        //获得总记录数
        long totalHits = response.getHits().getTotalHits();
        //记录列表
        List<T> list = new ArrayList<>();
        //获取原始的搜索结果
        SearchHits hits = response.getHits();
        for (SearchHit hit : hits) {
            if (hits.getHits().length <= 0) {
                return null;
            }
            //获取_source属性中的所有数据
            Map<String, Object> map = hit.getSourceAsMap();
            //获得高亮的字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
            //每个高亮字段都需要进行设置
            for(Map.Entry<String,HighlightField> highlightField : highlightFields.entrySet()){
                //获得高亮的key:高亮字段
                String key = highlightField.getKey();
                //获得value:高亮之后的效果
                HighlightField value = highlightField.getValue();
                //将高亮字段和文本效果放入到map中
                map.put(key,value.getFragments()[0].toString());
            }
            //将map转换为对象
            Gson gson = new Gson();
            //map-->jsonString-->对象
            T t = gson.fromJson(gson.toJson(map), clazz);
            list.add(t);
        }
        return new AggregatedPageImpl<>(list,pageable,totalHits);
    }
}

(三)构建queryBuilder多条件查询

package com.lagou.ecsearch.service.impl;

import com.lagou.ecsearch.dao.EcsearchRepository;
import com.lagou.common.pojo.Product;
import com.lagou.ecsearch.resultmapper.ESSearchResultMapper;
import com.lagou.ecsearch.service.ESService;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ESServiceImpl implements ESService {

    @Autowired
    private ElasticsearchTemplate template;


    @Override
    public List<Product> nativeQuery(Integer size, String name,Double priceM,Double priceL) {
        //1.构架一个原生查询器
        NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
        //2.source过滤
        //2.1 参数:final String[] includes, final String[] excludes
        //如果不想执行source过滤可以将该行注释
        //查询所有
        queryBuilder.withSourceFilter(new FetchSourceFilter(new String[0],new String[0]));

        //包含title字段的查询
        //queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"price"},null));
        //3.多条件查询
        queryBuilder.withQuery(QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery("name",name))
                .must(QueryBuilders.rangeQuery("price")
                        .gte(priceM)
                        .lte(priceL)
                        .includeLower(true)
                        .includeUpper(true)));

        //4.设置分页和排序规则
        queryBuilder
                .withPageable(PageRequest.of(0,size,Sort.by(Sort.Direction.DESC,"price")));
        //5.高亮
        HighlightBuilder.Field field = new HighlightBuilder.Field("name");
        field.preTags("<font style='color:red'>");
        field.postTags("</font>");
        queryBuilder.withHighlightFields(field);
        //6.聚合
        queryBuilder.addAggregation(AggregationBuilders.terms("brandAgg").field("brand"));
        //8.范围查询
        //7.查询
        AggregatedPage<Product> result = template.queryForPage(queryBuilder.build(), Product.class, new ESSearchResultMapper());
        //获取结果
        long total = result.getTotalElements();
        //页码
        int totalPages = result.getTotalPages();
        //获得本页的数据集合
        List<Product> content = result.getContent();
        System.out.println("查询出总记录数: " + total+"  "+ "总页数: " + totalPages);
        //lambda表达式
        content.stream().forEach(product -> System.out.println(product));

        /*//获得聚合的结果
        Aggregations aggregations = result.getAggregations();
        Terms terms = aggregations.get("brandAgg");
        //获取桶,并且遍历桶中的内容
        terms.getBuckets().forEach(b->{
            System.out.println("品牌:" + b.getKeyAsString());
            System.out.println("个数:" + b.getDocCount());
        });*/
        return content;
    }


}

三.elasticsearch普通查询

(一)ElasticsearchRepository接口定义

package com.lagou.ecsearch.dao;

import com.lagou.common.pojo.Product;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

import java.util.List;

/**
 * 当SDE访问索引库时,需要定义一个持久层的接口去继承ElasticsearchRepository接口即可,无需实现
 */
public interface EcsearchRepository extends ElasticsearchRepository<Product,Long> {


}

(二)业务代码

package com.lagou.ecsearch.service.impl;

import com.lagou.ecsearch.dao.EcsearchRepository;
import com.lagou.common.pojo.Product;
import com.lagou.ecsearch.resultmapper.ESSearchResultMapper;
import com.lagou.ecsearch.service.ESService;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.aggregation.AggregatedPage;
import org.springframework.data.elasticsearch.core.query.FetchSourceFilter;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class ESServiceImpl implements ESService {

    @Autowired
    private EcsearchRepository ecsearchRepository;


    @Override
    public String addProduct(Product product) {

        ecsearchRepository.save(product);

        return "ES save success";
    }


}