es常用方法–开发
微服务模块搭建
创建一个搜索微服务 qc_service-search-api 作为 feign 调用的api
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AC5vEHlZ-1625378745907)(img\项目搭建1.png)]
在 es api导入es data包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
</dependencies>
//lombok的支持
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
es api pojo 需要导入的 商品 类
Data
@Document(indexName = "skuinfo", type = "docs")
public class SkuInfo implements Serializable {
//商品id,同时也是商品编号
@Id
private Long id;
//SKU名称
@Field(type = FieldType.Text, analyzer = "ik_smart")
private String name;
//商品价格,单位为:元
@Field(type = FieldType.Double)
private Double price;
//库存数量
private Integer num;
//商品图片
private String image;
//商品状态,1-正常,2-下架,3-删除
private String status;
//创建时间
private Date createTime;
//更新时间
private Date updateTime;
//是否默认
private String isDefault;
//SPUID
private Long spuId;
//类目ID
private Long categoryId;
//类目名称
@Field(type = FieldType.Keyword)
private String categoryName;
//品牌名称
@Field(type = FieldType.Keyword)
private String brandName; //1
//规格
// @Field(type =FieldType.Text,fielddata = true)
private String spec;
//规格参数
private Map<String, Object> specMap;
}
商品导入es api 的 feign 的接口
@FeignClient(name = "service-goods")
@RequestMapping(value = "goods/sku")
public interface SkuFeign {
/***
* 根据审核状态查询Sku
* @param status
* @return
*/
@GetMapping("/q/{status}/p/{pageNum}/s/{pageSize}")
ServerResponse findByStatus(@PathVariable String status, @PathVariable Integer pageNum,
@PathVariable Integer pageSize);
/**
* 通过spuid 查询 SpuEntity
*/
@GetMapping("getSpu/{skuid}")
SpuEntity getSpu(@PathVariable String skuid);
}
2 創建ES 搜索微服务的service
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Z6YKql2-1625378745909)(\img\项目搭建2.png)]
启动类的实现
package com.qc_shop.search;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
@ComponentScan({"com.qc_shop.search.feign","com.qc_shop.search"})
public class SearchApplication {
public static void main(String[] args) {
/**
* Springboot整合Elasticsearch 在项目启动前设置一下的属性,防止报错
* 解决netty冲突后初始化client时还会抛出异常
* availableProcessors is already set to [12], rejecting [12]
***/
//System.setProperty("es.set.netty.runtime.available.processors", "false");
SpringApplication.run(SearchApplication.class, args);
}
}
package com.qc_shop.search.dao;
import com.qc_shop.search.pojo.SkuInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SkuFeignDao extends ElasticsearchCrudRepository<SkuInfo,Long> {
}
contrller 中的实现
@RestController
@RequestMapping("/search/sku")
public class SkuFeignController {
@Autowired
private SkuFeignService skuFeignService;
//导入数据
@GetMapping("/ipt/q/{status}/p/{pageNum}/s/{pageSize}")
private ServerResponse importSku(@PathVariable String status, @PathVariable Integer pageNum,
@PathVariable Integer pageSize) {
return skuFeignService.importSku(status,pageNum,pageSize);
}
//检索数据
@PostMapping("/searchMap")
private ServerResponse getSku(@RequestBody Map searchMap) {
System.out.println(searchMap);
return skuFeignService.searchSku(searchMap);
}
//检索spec 的数据
@PostMapping("/searchSpecMap")
private ServerResponse getSkuSpec(@RequestBody(required = false) Map searchMap) {
return skuFeignService.searchSkuSpec(searchMap);
}
/* @PostMapping("/searchSkuPrice")
private ServerResponse getSkuPrice(@RequestBody(required = false) Map searchMap) {
return skuFeignService.getSkuPrice(searchMap);
}*/
/**
* 组合条件筛选
* @param
* @return
*/
@PostMapping("/searchSpecAndCategoryMap")
private ServerResponse searchSpecAndCategoryMap(@RequestBody(required = false) Map searchMap) {
return skuFeignService.searchSpecAndCategoryMap(searchMap);
}
}
service 的实现
导入数据到es
contrller
@RestController
@RequestMapping("/search/sku")
public class SkuFeignController {
@Autowired
private SkuFeignService skuFeignService;
@GetMapping("/ipt/q/{status}/p/{pageNum}/s/{pageSize}")
private ServerResponse importSku(@PathVariable String status, @PathVariable Integer pageNum,
@PathVariable Integer pageSize) {
return skuFeignService.importSku(status,pageNum,pageSize);
}
导入数据的serviceimpl 的实现类
@SneakyThrows
@Override
public ServerResponse importSku(String status, Integer pageNum, Integer pageSize) {
// 将feign注入 调用feign接口
ServerResponse skuMap = skuFeign.findByStatus(status, pageNum, pageSize);
//创建一个list 来封装 SkuInfo类个es
LinkedList<SkuInfo> skuInfos = new LinkedList<>();
//将封装在ServerResponse的数据page取出来
List<Object> skuEntities = (List<Object>) skuMap.getpage();
//取出来的格式需要转换 当前 为 List<Map<>> 类型的
for (Object skuEntity : skuEntities) {
// getSkuInfo((Map) skuEntity)); 封装skuInfo
skuInfos.add(getSkuInfo((Map) skuEntity));
}
if (skuInfos.size() < 1) {
log.info("待导入数据不存在");
return ServerResponse.createBySuccess("待导入数据不存在");
}
skuFeignDao.saveAll(skuInfos);
log.info("导入成功");
return ServerResponse.createBySuccess("导入成功");
}
/**
* 封装skuInfo
*
* @param skuEntity
* @return
*/
@SneakyThrows
private SkuInfo getSkuInfo(Map skuEntity) {
//需要将数据从map 取出来强转为skuinfo需要的数据类型
SkuInfo skuInfo = new SkuInfo();
skuInfo.setBrandName((String) skuEntity.get("brandName"));
skuInfo.setCategoryId(Long.valueOf((Integer) skuEntity.get("categoryId")));
skuInfo.setCategoryName((String) skuEntity.get("categoryName"));
skuInfo.setImage((String) skuEntity.get("image"));
//时间转换
String createTime = (String) skuEntity.get("createTime");
skuInfo.setCreateTime(MyDateTimeUtil.strToDate_No_H_m_s(createTime));
String updateTime = (String) skuEntity.get("updateTime");
skuInfo.setUpdateTime(MyDateTimeUtil.strToDate_No_H_m_s(updateTime));
skuInfo.setId(Long.valueOf((String) skuEntity.get("id")));
//查询spu 拿到IsDefault 的数据设置进去
SpuEntity spuEntity = skuFeign.getSpu((String) skuEntity.get("id"));
skuInfo.setIsDefault(spuEntity.getIsDelete());
skuInfo.setPrice(Double.valueOf((Integer) skuEntity.get("price")));
skuInfo.setNum((Integer) skuEntity.get("num"));
skuInfo.setSpec((String) skuEntity.get("spec"));
skuInfo.setStatus((String) skuEntity.get("status"));
skuInfo.setSpuId(Long.valueOf((String) skuEntity.get("spuId")));
skuInfo.setName((String) skuEntity.get("name"));
String spec = (String) skuEntity.get("spec");
spec = spec.replaceAll("\'", "\"");
Map<String, Object> specMap = objectMapper.readValue(spec, new TypeReference<HashMap<String, Object>>() {
});
skuInfo.setSpecMap(specMap);
return skuInfo;
}
skufeign调用接口 这个接口一定加上@FeignClient 直接去找到服务
并且一下的feign的接口函数和调用服务的接口函数要一致 sku 下的controller
@RestController
@RequestMapping("/goods/sku")
public class SkuController {
@Autowired
private SkuService skuService;
/**
* 列表
*/
@GetMapping("/q/{status}/p/{pageNum}/s/{pageSize}")
public ServerResponse<List<SkuEntity>> findByStatus(@PathVariable String status, @PathVariable Integer pageNum,
@PathVariable Integer pageSize) {
return skuService.findByStatus(status, pageNum, pageSize);
}
}
import com.qc_shop.goods_api.entity.SpuEntity;
import com.qc_shop.util.ServerResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Random;
import java.util.Scanner;
@FeignClient(name = "service-goods")
@RequestMapping(value = "goods/sku")
public interface SkuFeign {
/***
* 根据审核状态查询Sku
* @param status
* @return
*/
@GetMapping("/q/{status}/p/{pageNum}/s/{pageSize}")
ServerResponse findByStatus(@PathVariable String status, @PathVariable Integer pageNum,
@PathVariable Integer pageSize);
/**
* 通过spuid 查询 SpuEntity
*/
@GetMapping("getSpu/{skuid}")
SpuEntity getSpu(@PathVariable String skuid);
}
对商品聚合搜索,通过分类名称,和产品名称
/**
* sku产品 聚合检索
*
* @param map
* @return
*/
@Override
public ServerResponse searchSku(Map<String, String> map) {
String keywords = map.get("keywords");
if (StringUtils.isEmpty(keywords)) {
//设置一个默认值
keywords = "娃娃";
}
//构建查询对象---> 的构建对象
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder();
//设置主关键字查询
nativeSearchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery(keywords, "name", "brandName", "categoryName"));
nativeSearchQueryBuilder.withFilter(boolQueryBuilder);
//设置查询条件 分类名称查询
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuCategoryGroup").field("categoryName").size(50));
//设置品牌查询条件
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuBrandGroup").field("brandName").size(50));
//设置规格查询条件
nativeSearchQueryBuilder.addAggregation(AggregationBuilders.terms("skuSpecGroup").field("spec.keyword").size(50));
// 设置查询添加 价格查询
nativeSearchQueryBuilder.withQuery(QueryBuilders.matchQuery("name", keywords));
//构建查询对象
NativeSearchQuery query = nativeSearchQueryBuilder.build();
//执行查询
AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(query, SkuInfo.class);
if (skuInfos.isEmpty()) {
return ServerResponse.createByError("查询数据不存在");
}
//获取分组
StringTerms skuCategoryGroup = (StringTerms) skuInfos.getAggregation("skuCategoryGroup");
//获取categoryList 分类列表
List categoryList = getGroupTOList(skuCategoryGroup);
//获取brandList 分类列表
StringTerms skuBrandGroup = (StringTerms) skuInfos.getAggregation("skuBrandGroup");
List brandList = getGroupTOList(skuBrandGroup);
//spec{"KYE","VALUE"} 分类 map
StringTerms skuSpecGroup = (StringTerms) skuInfos.getAggregation("skuSpecGroup");
Map skuSpecMap = getGroupToMap(skuSpecGroup);
HashMap rs = new HashMap<>();
rs.put("categoryList", categoryList);
rs.put("brandList", brandList);
rs.put("row", skuInfos.getContent());
rs.put("total", skuInfos.getTotalPages());
rs.put("totalPages", skuInfos.getTotalPages());
rs.put("pageSize", skuInfos.getSize());
rs.put("scroll", skuInfos.getScrollId());
return ServerResponse.createBySuccess("查询成功", rs);
}
通过条件筛选在上面代碼加入以下筛选条件
通过QueryBuilders.boolQuery();
// 该方法底层页数和上面一样,是一个 new BoolQueryBuilder() 返回一个条件而已
public static BoolQueryBuilder boolQuery() {
return new BoolQueryBuilder();
}
/*添加过滤条件*/
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//类型名称做过滤
if (map.get("categoryName") != null) {
boolQueryBuilder.must(QueryBuilders.matchQuery("categoryName", map.get("categoryName")));
log.info("----------------------------------categoryName" + map.get("categoryName"));
}
//品牌名称做过滤
if (map.get("brandName") != null) {
boolQueryBuilder.must(QueryBuilders.matchQuery("brandName", map.get("brandName")));
log.info("----------------------------------brandName" + map.get("categoryName"));
}
//产品规格做过滤 QueryBuilders.matchQuery 匹配的第一个参数名称必须做匹配es 中导入 实体的字段
for (String key : map.keySet()) {
if (key.startsWith("spec_")) {
boolQueryBuilder.must(QueryBuilders.matchQuery("specMap." + key.substring(5) + ".keyword", map.get(key)));
log.info("specMap." + key.substring(5) + ".keyword" + "-------------------------");
}
}
//价格过滤 gte :大于等于 lte 小于等于
if (map.get("price") != null) {
String price = map.get("price");
String[] split = price.split("-");
if (split.length > 1) {
boolQueryBuilder.must(QueryBuilders.rangeQuery("price").from(split[0], true).to(split[1], true));
log.info("price------------------");
} else {
boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(split[0]));
}
}
//增加分页
if (map.get("pageNum") != null || map.get("pageSize") != null) {
Integer pageNum = Integer.valueOf(map.get("pageNum"));
Integer pageSize = Integer.valueOf(map.get("pageSize"));
log.info(pageNum + "----------------" + pageSize);
nativeSearchQueryBuilder.withPageable(PageRequest.of(pageNum - 1, pageSize));
}
//增加 排序ortBuilders.fieldSort("需要排序的字段").order("排序的规则 升序还是降序")
//根据?排序;比如根据价格排序sortFiled=price
if (map.get("sortRule") != null || map.get("sortFiled") != null) {
nativeSearchQueryBuilder.withSort(SortBuilders.fieldSort(map.get("sortFiled")).
order(map.get("sortRule").equals("DESC") ? SortOrder.DESC : SortOrder.ASC));
}
//设置高亮条件 .field("需要高亮的字段").preTags("选择的标签").postTags(结束标签)
// .fragmenter("20") 分段,可有可以无
nativeSearchQueryBuilder.withHighlightBuilder(new HighlightBuilder()
.field("name")
.preTags("<i style=\"color:red\">")
.postTags("</i>")
.fragmenter("20")
);
elasticsearchTemplate 的高亮处理
AggregatedPage<SkuInfo> skuInfos = elasticsearchTemplate.queryForPage(query, SkuInfo.class, myHighlight);
创建一个MyHighlight接口来实现
package com.qc_shop.search.service.Impl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qc_shop.search.pojo.SkuInfo;
import lombok.SneakyThrows;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.springframework.beans.factory.annotation.Autowired;
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 org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Map;
@Component
public class MyHighlight implements SearchResultMapper {
@Autowired
private ObjectMapper objectMapper;
@SneakyThrows
@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) {
ArrayList<T> skuInfoList = new ArrayList<>();
//做空判断
if (searchResponse.getHits()==null){
return new AggregatedPageImpl<>(skuInfoList);
}
for (SearchHit hit : searchResponse.getHits()) {
//获取到高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//拿到高亮的值
HighlightField higValue = highlightFields.get("name");
//if 高亮的值为null 返回空
if (null==higValue) {
return new AggregatedPageImpl<>(skuInfoList);
}
StringBuffer sb = new StringBuffer();
for (Text fragment : higValue.getFragments()) {
sb.append(fragment);
}
SkuInfo skuInfo = objectMapper.readValue(hit.getSourceAsString(), SkuInfo.class);
skuInfo.setName(sb.toString());
skuInfoList.add((T) skuInfo);
}
return new AggregatedPageImpl<T>(skuInfoList, pageable, searchResponse.getHits().getTotalHits(),searchResponse.getAggregations());
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
return null;
}
}
serviceimpl需要注入
@Autowired
private MyHighlight myHighlight;
dao
package com.qc_shop.search.dao;
import com.qc_shop.search.pojo.SkuInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SkuFeignDao extends ElasticsearchCrudRepository<SkuInfo,Long> {
}
s().getTotalHits(),searchResponse.getAggregations());
}
@Override
public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
return null;
}
}
serviceimpl需要注入
```java
@Autowired
private MyHighlight myHighlight;
dao
package com.qc_shop.search.dao;
import com.qc_shop.search.pojo.SkuInfo;
import org.springframework.data.elasticsearch.repository.ElasticsearchCrudRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface SkuFeignDao extends ElasticsearchCrudRepository<SkuInfo,Long> {
}