es全文检索+feign+集成SpringCloud的使用
1、es的使用
注意:
es在微服务中有很多地方都在使用, 所以es也是一个单独的微服务,其他微服务在使用es,那么就需要通过feign来调用es这个微服务
1.1 导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
1.2 es的配置文件 application.yml
eureka:
client:
serviceUrl:
defaultZone: http://localhost:1010/eureka/ #注册中心服务端的注册地址
instance:
prefer-ip-address: true #使用ip进行注册
instance-id: es-server:2030 #服务注册到注册中心的id
server:
port: 2030
#应用的名字
spring:
application:
name: es-server
#es的主要配置
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 127.0.0.1:9300 #9200是图形界面端,9300代码端
#超时
ribbon: #ribbon超时
ReadTimeout: 50000
ConnectTimeout: 50000
hystrix: #hystrix超时
command:
default:
execution:
isolation:
thread:
timeoutInMilliseconds: 50000
1.3 创建CourseDoc
注意:
要上线放到es里的数据,那么其中的索引那些就是通过自定义doc类来配置的(这个doc类里面就包含了映射规则,索引库,分词和不分词)。类似于前台网站要进行查询、排序展示的数据就是通过这个规则来执行的。
一般会把CourseDoc对象 抽取成公共的hrm-es-common
/**
* 针对于 Course表的文档映射
* indexName:索引库
* type:类型(表类型)
*/
//针对于CourseDoc课程的ES文档对象 数据库/类型/id
@Document(indexName = "hrm",type = "employee")
public class CourseDoc{
//文档的ID,同时也是数据的id
@Id
private Long id;
//标题 标题 要分词 FieldType.Text
@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String name;
//适用人群
@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String users;
//课程类型ID
@Field(type = FieldType.Long)
private Long courseTypeId;
//等级名字
// @Field(type = FieldType.Keyword)
private String gradeName;
//课程等级
private Long gradeId;
//机构id
private Long tenantId;
//机构名字
@Field(type =FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_max_word")
private String tenantName;
//开课时间
private Date startTime;
//结课时间
private Date endTime;
//封面
private String pic;
//免费、收费
private String chargeName;
//qq
private String qq;
//价格
private Float price;
//原价
private Float priceOld;
//课程介绍
private String description;
//上线时间
private Date onlineDate = new Date();
//浏览数
private Integer viewCount;
//购买数
private Integer buyCount;
//------------get/set---------
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
.........
}
1.4 创建CourseDocRepository
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/**
* ElasticsearchRepository<要操做的映射类的类型,主键类型>
* 这个接口里面有基础的crud,所以需要继承这个接口
*/
//使用的时候就直接注入这个接口就可以了
@Repository
public interface CourseDocRepository extends ElasticsearchRepository<CourseDoc,Long> {
}
1.5 普通的测试(可有可无)
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Es_App_2030.class) //主配置类
public class EsTest {
//创建索引,并且建立类型映射
@Autowired
private ElasticsearchTemplate elasticsearchTemplate; //直接注入,springboot已经做了自动配置
@Autowired
private CourseDocRepository courseDocRepository;
@Test
public void testPre() throws Exception{
//创建索引
elasticsearchTemplate.createIndex(CourseDoc.class);
//类型映射-自定义映射
elasticsearchTemplate.putMapping(CourseDoc.class);
}
@Test
public void test() throws Exception{
CourseDoc courseDoc = new CourseDoc();
courseDoc.setId(1L);
courseDoc.setName("欢迎来到非诚勿扰");
courseDoc.setGradeName("黄金30秒");
courseDocRepository.save(courseDoc);
}
/**
* 获取全部
* @throws Exception
*/
@Test
public void testGetAll() throws Exception{
Iterable<CourseDoc> all = courseDocRepository.findAll();
all.forEach(e-> System.out.println(e));
}
//获取一个
@Test
public void testGetOne() throws Exception{
System.out.println(courseDocRepository.findById(1L));
}
//修改:先查询出来,在进行修改
@Test
public void testUpdate() throws Exception{
Optional<CourseDoc> byId = courseDocRepository.findById(1L);
//将查询出来的数据进行修改,然后在将这个数据保存
CourseDoc doc = byId.get();
doc.setName("你好啊");
courseDocRepository.save(doc);
}
//删除
@Test
public void testDel() throws Exception{
courseDocRepository.deleteById(1L);
}
//高级查询分页排序: DSL查询+DSL过滤
// 需求:查询 name中包含 “Java”的课程,
// 并且课程分类 CourseTypeId 在 1 - 10
//课程等级 GradeName为“神级”
//查询第2页,每页2条
//按照id倒排序
@Test
public void testSearchCourse(){
//查询对象构建器
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//查询条件start======================================================================================================
//组合查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//查询 name中包含 “Java”的课程 : matchQuery , termQuery
boolQueryBuilder.must(QueryBuilders.matchQuery("name","Java"));
List<QueryBuilder> filter = boolQueryBuilder.filter();
//分类 CourseTypeId 在 1 - 10 : rangeQuery
filter.add(QueryBuilders.rangeQuery("courseTypeId").gte(1).lte(20) );
//等级 GradeName为“神级” : termQuery
filter.add(QueryBuilders.termQuery("gradeName","神级"));
//添加查询条件
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
//查询条件end======================================================================================================
//添加分页
nativeSearchQueryBuilder.withPageable(PageRequest.of(0,2));
//添加排序
nativeSearchQueryBuilder.withSort(new FieldSortBuilder("id").order(SortOrder.DESC));
//查询对象
SearchQuery searchQuery = nativeSearchQueryBuilder.build();
//查询结果:page相当于是以前的PageList
Page<CourseDoc> page = courseElasticsearchRepository.search(searchQuery);
//总条数 PageList: count ,list
long count = page.getTotalElements();
//总 页数
int totalPages = page.getTotalPages();
//结果列表
List<CourseDoc> list = page.getContent();
System.out.println("总条数:"+count);
System.out.println("totalPages:"+totalPages);
System.out.println("数据列表:");
list.forEach(c->{
System.out.println(c);
});
}
}
1.6 项目使用(es集成一个controller接口)
类似于redis,方便其他微服务调用
import com.alibaba.fastjson.JSON;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.data.elasticsearch.core.query.SearchQuery;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/es")
public class EsController {
@Autowired
private CourseDocRepository courseDocRepository;
//保存
@PostMapping("/save")
public AjaxResult save(@RequestBody CourseDoc courseDoc){
courseDocRepository.save(courseDoc);
return AjaxResult.me();
}
//删除
@DeleteMapping("/delete/{id}")
public AjaxResult del(@PathVariable("id")Long id){
courseDocRepository.deleteById(id);
return AjaxResult.me();
}
//----------------高级查询--------------------
@PostMapping("/search")
public PageList<CourseDoc> search(@RequestParam("queryJson") String queryJson){
//查询JSON字符串,对象转成查询对象
NativeSearchQuery nativeSearchQueryBuilder = JSON.parseObject(queryJson, NativeSearchQuery.class);
//执行查询
Page<CourseDoc> page = courseDocRepository.search(nativeSearchQueryBuilder);
//返回结果 ,page转成 PageList
return new PageList<>(page.getTotalElements() , page.getContent());
}
@PostMapping("/searchCourse")
public PageList<CourseDoc> searchCourse(@RequestBody CourseQuery courseQuery){
//查询JSON字符串,对象转成查询对象
NativeSearchQuery nativeSearchQueryBuilder = createCourseQueryForES(courseQuery).build();
//执行查询
Page<CourseDoc> page = courseDocRepository.search(nativeSearchQueryBuilder);
//返回结果 ,page转成 PageList
return new PageList<>(page.getTotalElements() , page.getContent());
}
//拼接查询对象courseQuery -> NativeSearchQueryBuilder
private NativeSearchQueryBuilder createCourseQueryForES(CourseQuery courseQuery) {
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//添加条件===============================================================================================================
//组合查询
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// keyword: "java" : 查询 MathchQuery - DSL查询
if(StringUtils.hasLength(courseQuery.getKeyword())){
boolQueryBuilder.must(QueryBuilders.matchQuery("name",courseQuery.getKeyword()));
}
//过滤
List<QueryBuilder> filter = boolQueryBuilder.filter();
//productType: :termQuery
if(courseQuery.getProductType() != null){
filter.add(QueryBuilders.termQuery("courseTypeId",courseQuery.getProductType()));
}
//priceMax: null : rangeQuery
if(courseQuery.getPriceMax() != null){
filter.add(QueryBuilders.rangeQuery("price").lte(courseQuery.getPriceMax()));
}
//priceMin: null :rangeQuery
if(courseQuery.getPriceMin() != null){
filter.add(QueryBuilders.rangeQuery("price").gte(courseQuery.getPriceMin()));
}
nativeSearchQueryBuilder.withQuery(boolQueryBuilder);
//添加分页
nativeSearchQueryBuilder.withPageable(PageRequest.of(courseQuery.getPage() - 1,courseQuery.getRows()));
//添加排序===============================================================================================================
// sortField: ; sortType:==
if(StringUtils.hasLength(courseQuery.getSortField())){
String sortField = null;
switch (courseQuery.getSortField().toLowerCase()){
case "xl": sortField = "buyCount" ; break;
case "xp": sortField = "onlineDate" ; break;
case "jg": sortField = "price" ; break;
case "rq": sortField = "viewCount" ; break;
case "pl": sortField = "comment" ; break;
default:sortField = "price" ;
}
//排序方式
SortOrder sortOrder = SortOrder.DESC;
if(StringUtils.hasLength(courseQuery.getSortType()) && courseQuery.getSortType().toLowerCase().equals("asc")){
sortOrder = SortOrder.ASC;
}
nativeSearchQueryBuilder.withSort(new FieldSortBuilder(sortField).order(sortOrder));
}
return nativeSearchQueryBuilder;
}
}
其他微服务业务调用数据,保存到es
//拷贝数据 要放到es里的数据,拷贝到映射类courseDoc里面,最终保存映射类
BeanUtils.copyProperties(course, courseDoc);
BeanUtils.copyProperties(courseDetail, courseDoc);
BeanUtils.copyProperties(courseMarket, courseDoc);
//保存到es
esFeign.save(courseDoc);
2、feign集成es
2.1 feign接口
//调用目标服务名
@FeignClient(value="es-server",fallbackFactory=EsFeignFactory.class)
public interface EsFeign {
//保存
@PostMapping("/es/save")
AjaxResult save(@RequestBody CourseDoc courseDoc);
//删除
@DeleteMapping("/es/delete/{id}")
AjaxResult del(@PathVariable("id")Long id);
@PostMapping("/es/search")
PageList<CourseDoc> search(@RequestParam("queryJson") String queryJson);
@PostMapping("/es/searchCourse")
PageList<CourseDoc> searchCourse(@RequestBody CourseQuery courseQuery);
}
2.2 托底数据
@Component
public class EsFeignFactory implements FallbackFactory<EsFeign> {
// 托底类
@Override
public EsFeign create(Throwable throwable) {
return new EsFeign() {
//保存失败返回的托底数据
@Override
public AjaxResult save(CourseDoc courseDoc) {
return AjaxResult.me().setSuccess(false).setMessage("保存到es失败");
}
@Override
public AjaxResult del(Long id) {
return AjaxResult.me().setSuccess(false).setMessage("删除es失败");
}
@Override
public PageList<CourseDoc> search(@RequestParam("queryJson") String queryJson) {
throwable.printStackTrace();
return null;
}
@Override
public PageList<CourseDoc> searchCourse(CourseQuery courseQuery) {
throwable.printStackTrace();
return new PageList();
}
};
}
}