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";
}
}