目录
新建模块
1.新建一个maven模块
设置文件路径
2.导入maven依赖
<?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>leyou</artifactId>
<groupId>com.leyou.parent</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>com.leyou.search</groupId>
<artifactId>leyou-search</artifactId>
<version>1.0.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>com.leyou.item</groupId>
<artifactId>leyou-item-interface</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.leyou.common</groupId>
<artifactId>leyou-common</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies>
</project>
3.配置yml文件
server:
port: 8083
spring:
application:
name: search-service
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.62.128:9300
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
registry-fetch-interval-seconds: 10
instance:
lease-renewal-interval-in-seconds: 5
lease-expiration-duration-in-seconds: 15
4.新建引导类
package com.leyou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LeyouSearchApplication {
public static void main(String[] args) {
SpringApplication.run(LeyouSearchApplication.class);
}
}
新增实体类
1.新增实体类,并设置映射
记得生成get和set方法
@Document(indexName = "goods", type = "docs", shards = 1, replicas = 0)
public class Goods {
@Id
private Long id; // spuId
@Field(type = FieldType.Text, analyzer = "ik_max_word")
private String all; // 所有需要被搜索的信息,包含标题,分类,甚至品牌
@Field(type = FieldType.Keyword, index = false)
private String subTitle;// 卖点
private Long brandId;// 品牌id
private Long cid1;// 1级分类id
private Long cid2;// 2级分类id
private Long cid3;// 3级分类id
private Date createTime;// 创建时间
private List<Long> price;// 价格
@Field(type = FieldType.Keyword, index = false)
private String skus;// List<sku>信息的json结构
private Map<String, Object> specs;// 可搜索的规格参数,key是参数名,值是参数值
}
使用Feign调用所需要的服务
如果要调用接口,我们首先要封装好接口,在Interface微服务中抽取具体的接口
我们在search微服务只需要新建一个接口,让这个接口继承“接口微服务项目中暴露的接口类”即可使用之
存取map
我们需要在search微服务中新建service,这里面的大致逻辑是你要把所有需要从数据库中查询的信息放在一个map里面,准备之后把这个map存入到索引库里面,这里举个例子。
package com.leyou.search.service;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.leyou.item.api.CategoryApi;
import com.leyou.item.pojo.*;
import com.leyou.search.client.BrandClient;
import com.leyou.search.client.CategoryClient;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.client.SpecificationClient;
import com.leyou.search.pojo.Goods;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.util.*;
@Service
public class SearchService {
@Autowired
private CategoryClient categoryClient;
@Autowired
private BrandClient brandClient;
@Autowired
private GoodsClient goodsClient;
@Autowired
private SpecificationClient specificationClient;
private static final ObjectMapper MAPPER = new ObjectMapper();
public Goods buildGoods(Spu spu) throws IOException {
Goods goods = new Goods();
//根据分类的id查询分类名称
List<String> names = this.categoryClient.queryNamesByIds(Arrays.asList(spu.getCid1(),spu.getCid2(),spu.getCid3()));
//根据品牌id查询品牌名称
Brand brand = this.brandClient.queryBrandById(spu.getBrandId());
//根据spuId查询所有sku
List<Sku> skus = this.goodsClient.querySkusBySpuId(spu.getId());
//初始化一个价格集合,搜集所有sku的价格
List<Long> prices = new ArrayList<>();
//收集展示出的sku所必要的字段信息
List<Map<String, Object>> skuMapList = new ArrayList<>();
skus.forEach(sku -> {
prices.add(sku.getPrice());
Map<String,Object> map = new HashMap<>();
map.put("id", sku.getId());
map.put("title", sku.getTitle());
map.put("price", sku.getPrice());
//获取sku中的图片,数据库的图片可能是多张,并且是以“,”分隔,所以我们也以逗号来切割返回图片数组,获取第一张图片
map.put("image", StringUtils.isBlank(sku.getImages()) ? "" : StringUtils.split(sku.getImages(), ",")[0]);
skuMapList.add(map);
});
//根据spu中的cid3查询出所有的搜索规格参数
List<SpecParam> params = this.specificationClient.queryParams(null, spu.getCid3(), null, true);
//根据spuId查询spuDetail
SpuDetail spuDetail = this.goodsClient.querySpuDetailBySpuId(spu.getId());
//把通用的规格参数和特殊的规格参数进行反序列化
Map<String,Object> genericSpecMap = MAPPER.readValue(spuDetail.getGenericSpec(), new TypeReference<Map<String, Object>>(){});
Map<String,List<Object>> specialSpecMap = MAPPER.readValue(spuDetail.getSpecialSpec(), new TypeReference<Map<String, List<Object>>>(){});
Map<String, Object> specs = new HashMap<>();
params.forEach(param ->{
//判断规格参数的类型,是否为通用的规格参数
if(param.getGeneric()) {
//如果是通用类型的参数,从genericSpecMap获取规格参数值
String value = genericSpecMap.get(param.getId().toString()).toString();
//判断是否是数值类型,如果是数值类型,应该返回一个区间
if(param.getNumeric()) {
value = chooseSegment(value,param);
}
specs.put(param.getName(), value);
} else {
//如果是特殊的规格参数,从specialSepcMap中获取值
List<Object> value = specialSpecMap.get(param.getId().toString());
specs.put(param.getName(),value);
}
});
goods.setId(spu.getId());
goods.setCid1(spu.getCid1());
goods.setCid2(spu.getCid2());
goods.setCid3(spu.getCid3());
goods.setBrandId(spu.getBrandId());
goods.setCreateTime(spu.getCreateTime());
goods.setSubTitle(spu.getSubTitle());
//拼接all字段,需要分类名称和品牌名称
goods.setAll(spu.getTitle() + " " + StringUtils.join(names, " ") + " " + brand.getName());
//获取spu下的所有sku价格
goods.setPrice(prices);
//获取spu下所有sku,并转化为json字符串
goods.setSkus(MAPPER.writeValueAsString(skuMapList));
//获取所有查询的规格参数{name:value}
goods.setSpecs(specs);
return goods;
}
private String chooseSegment(String value, SpecParam p) {
double val = NumberUtils.toDouble(value);
String result = "其它";
// 保存数值段
for (String segment : p.getSegments().split(",")) {
String[] segs = segment.split("-");
// 获取数值范围
double begin = NumberUtils.toDouble(segs[0]);
double end = Double.MAX_VALUE;
if(segs.length == 2){
end = NumberUtils.toDouble(segs[1]);
}
// 判断是否在范围内
if(val >= begin && val < end){
if(segs.length == 1){
result = segs[0] + p.getUnit() + "以上";
}else if(begin == 0){
result = segs[1] + p.getUnit() + "以下";
}else{
result = segment + p.getUnit();
}
break;
}
}
return result;
}
}
把查询到的信息存入索引库
1.新建一个repository操作接口
方便之后使用这个接口存放索引库
package com.leyou.search.repository;
import com.leyou.search.pojo.Goods;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface GoodsRepository extends ElasticsearchRepository<Goods, Long> {
}
2.新建测试类
把数据存入elasticsearch
package com.leyou.elasticsearch.test;
import com.leyou.common.pojo.PageResult;
import com.leyou.item.bo.SpuBo;
import com.leyou.search.client.GoodsClient;
import com.leyou.search.pojo.Goods;
import com.leyou.search.repository.GoodsRepository;
import com.leyou.search.service.SearchService;
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.elasticsearch.core.ElasticsearchTemplate;
import org.springframework.test.context.junit4.SpringRunner;
import java.io.IOException;
import java.util.List;
import java.util.stream.Collectors;
@SpringBootTest
@RunWith(SpringRunner.class)
public class ElasticsearchTest {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Autowired
private GoodsRepository goodsRepository;
@Autowired
private SearchService searchService;
@Autowired
private GoodsClient goodsClient;
@Test
public void test(){
this.elasticsearchTemplate.createIndex(Goods.class);
this.elasticsearchTemplate.putMapping(Goods.class);
Integer page = 1;
Integer rows = 100;
do {
//分页查询spu,获取分页结果集
PageResult<SpuBo> result = this.goodsClient.querySpuByPage(null,null, page, rows);
//获取当前页的数据
List<SpuBo> items = result.getItems();
//处理List<SpuBo> ==> List<Goods>
List<Goods> goodsList = items.stream().map(spuBo -> {
try {
return this.searchService.buildGoods(spuBo);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}).collect(Collectors.toList());
//执行新增数据的方法
this.goodsRepository.saveAll(goodsList);
rows = items.size();
page++;
}while (rows == 100);
}
}
3.重启微服务,启动测试方法
如图所示代表启动成功
我们在kibana上查看下goods索引库
GET /goods/_search
可以看到新数据已经被添加到了索引库中