032 搭建搜索微服务01----向ElasticSearch中导入数据--通过Feign实现微服务之间的相互调用
1.创建搜索服务
创建module:
Pom文件:
<?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>lucky.leyou.parent</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion> <groupId>lucky.leyou.search</groupId>
<artifactId>leyou-search</artifactId> <dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- elasticsearch -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<!-- 因为要leyou-search模块也是一个微服务,必须引入eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- leyou-search模块需要调用其他微服务,需要使用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> <dependency>
<groupId>lucky.leyou.item</groupId>
<artifactId>leyou-item-interface</artifactId>
</dependency> <dependency>
<groupId>lucky.leyou.common</groupId>
<artifactId>leyou-common</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
</dependencies> </project>
application.yml:
server:
port: 8083
spring:
application:
name: search-service
data:
elasticsearch:
cluster-name: leyou
cluster-nodes: 127.0.0.1:9300 # 程序连接es的端口号是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 # 每隔5秒发送一次心跳
lease-expiration-duration-in-seconds: 10 # 10秒不发送就过期
引导类:
package lucky.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 // 开启feign客户端
public class LeyouSearchService { public static void main(String[] args) {
SpringApplication.run(LeyouSearchService.class, args);
}
}
2.索引库数据格式分析
接下来,我们需要商品数据导入索引库,便于用户搜索。
那么问题来了,我们有SPU和SKU,到底如何保存到索引库?
<1>以结果为导向
大家来看下搜索结果页:
可以看到,每一个搜索结果都有至少1个商品,当我们选择大图下方的小图,商品会跟着变化。
因此,搜索的结果是SPU,即多个SKU的集合。
既然搜索的结果是SPU,那么我们索引库中存储的应该也是SPU,但是却需要包含SKU的信息。
<2>需要什么数据
再来看看页面中有什么数据:
直观能看到的:图片、价格、标题、副标题
暗藏的数据:spu的id,sku的id
另外,页面还有过滤条件:
这些过滤条件也都需要存储到索引库中,包括:
商品分类、品牌、可用来搜索的规格参数等
综上所述,我们需要的数据格式有:
spuId、SkuId、商品分类id、品牌id、图片、价格、商品的创建时间、sku信息集、可搜索的规格参数
<3>最终的数据结构
我们创建一个实体类,封装要保存到索引库的数据,并设置映射属性:
package lucky.leyou.domain; import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType; import java.util.Date;
import java.util.List;
import java.util.Map; /**
* elasticsearch索引对应的实体类
* @Document 作用在类,标记实体类为文档对象,一般有四个属性:indexName:对应索引库名称 type:对应在索引库中的类型,
* shards:分片数量,默认5,replicas:副本数量,默认1
* @Id 作用在成员变量,标记一个字段作为id主键
* @Field 作用在成员变量,标记为文档的字段,并指定字段映射属性
* 注意:String类型的数据要加注解,因为String有两种类型:keywords、text
*/
@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;// 卖点,将subTitle注解为keyword,不需要索引即分词
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是参数名,值是参数值 public Long getId() {
return id;
} public void setId(Long id) {
this.id = id;
} public String getAll() {
return all;
} public void setAll(String all) {
this.all = all;
} public String getSubTitle() {
return subTitle;
} public void setSubTitle(String subTitle) {
this.subTitle = subTitle;
} public Long getBrandId() {
return brandId;
} public void setBrandId(Long brandId) {
this.brandId = brandId;
} public Long getCid1() {
return cid1;
} public void setCid1(Long cid1) {
this.cid1 = cid1;
} public Long getCid2() {
return cid2;
} public void setCid2(Long cid2) {
this.cid2 = cid2;
} public Long getCid3() {
return cid3;
} public void setCid3(Long cid3) {
this.cid3 = cid3;
} public Date getCreateTime() {
return createTime;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} public List<Long> getPrice() {
return price;
} public void setPrice(List<Long> price) {
this.price = price;
} public String getSkus() {
return skus;
} public void setSkus(String skus) {
this.skus = skus;
} public Map<String, Object> getSpecs() {
return specs;
} public void setSpecs(Map<String, Object> specs) {
this.specs = specs;
}
}
一些特殊字段解释:
all:用来进行全文检索的字段,里面包含标题、商品分类信息
price:价格数组,是所有sku的价格集合。方便根据价格进行筛选过滤
skus:用于页面展示的sku信息,不索引,不搜索。包含skuId、image、price、title字段
specs:所有规格参数的集合。key是参数名,值是参数值。
例如:我们在specs中存储 内存:4G,6G,颜色为红色,转为json就是:
{
"specs":{
"内存":[4G,6G],
"颜色":"红色"
}
}
当存储到索引库时,elasticsearch会处理为两个字段:
specs.内存:[4G,6G]
specs.颜色:红色
另外, 对于字符串类型,还会额外存储一个字段,这个字段不会分词,用作聚合。
specs.颜色.keyword:红色
3.商品微服务提供接口
索引库中的数据来自于数据库,我们不能直接去查询商品的数据库,因为真实开发中,每个微服务都是相互独立的,包括数据库也是一样。所以我们只能调用商品微服务提供的接口服务。
先思考我们需要的数据:
SPU信息
SKU信息
SPU的详情
商品分类名称(拼接all字段)
品牌名称
规格参数
再思考我们需要哪些服务:
第一:分批查询spu的服务,已经写过。
第二:根据spuId查询sku的服务,已经写过
第三:根据spuId查询SpuDetail的服务,已经写过
第四:根据商品分类id,查询商品分类名称,没写过
第五:根据商品品牌id,查询商品的品牌,没写过
第六:规格参数接口
因此我们需要额外提供一个查询商品分类名称的接口。
(1)根据分类id集合查询商品分类名称
在CategoryController中添加接口:
/**
* 根据分类id查询分类名称
* @param ids 分类id
* @return
*/
@GetMapping("names")
public ResponseEntity<List<String>> queryNamesByIds(@RequestParam("ids")List<Long> ids){ List<String> names = this.iCategoryService.queryNamesByIds(ids);
if (CollectionUtils.isEmpty(names)) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(names);
}
启动微服务进行测试,访问http://localhost:8081/category/names?ids=1,2,3
(2)通过id查询品牌信息
BrandController.java中添加接口:
/**
* 通过id查询品牌信息
* @param id
* @return
*/
@GetMapping("{id}")
public ResponseEntity<Brand> queryBrandById(@PathVariable("id") Long id){
Brand brand=this.brandService.queryBrandById(id);
if (brand==null) {
return ResponseEntity.notFound().build();
}
return ResponseEntity.ok(brand);
}
BrandServiceImpl.java中添加如下方法:
/**
* 通过id查询品牌信息
* @param id 主键id
* @return
*/
@Override
public Brand queryBrandById(Long id) {
//selectByPrimaryKey通过主键:id查询品牌信息
return this.brandMapper.selectByPrimaryKey(id);
}
重启商品微服务测试:
浏览器中输入:http://localhost:8081/brand/1528
在Navicat中查看真实数据表信息
(3)编写FeignClient
现在,我们要在搜索微服务调用商品微服务的接口。
<1>feign使用环境搭建
第1步要在leyou-search工程中的pom文件中,引入feign的启动器、分页工具模块和商品微服务依赖:leyou-item-interface
。
<!-- leyou-search模块需要调用其他微服务,需要使用feign -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency> <dependency>
<groupId>lucky.leyou.item</groupId>
<artifactId>leyou-item-interface</artifactId>
</dependency> <dependency>
<groupId>lucky.leyou.common</groupId>
<artifactId>leyou-common</artifactId>
</dependency>
第2步要在leyou-search工程中的引导类上添加注解:@EnableFeignClients ,开启feign客户端
<2>Feign在代码中使用
问题引出:
@FeignClient(value = "item-service")
public interface GoodsClient { /**
* 分页查询商品
* @param page
* @param rows
* @param saleable
* @param key
* @return
*/
@GetMapping("/spu/page")
PageResult<SpuBo> querySpuByPage(
@RequestParam(value = "page", defaultValue = "1") Integer page,
@RequestParam(value = "rows", defaultValue = "5") Integer rows,
@RequestParam(value = "saleable", defaultValue = "true") Boolean saleable,
@RequestParam(value = "key", required = false) String key); /**
* 根据spu商品id查询详情
* @param id
* @return
*/
@GetMapping("/spu/detail/{id}")
SpuDetail querySpuDetailById(@PathVariable("id") Long id); /**
* 根据spu的id查询sku
* @param id
* @return
*/
@GetMapping("sku/list")
List<Sku> querySkuBySpuId(@RequestParam("id") Long id);
}
以上的这些代码直接从商品微服务中拷贝而来,完全一致。差别就是没有方法的具体实现。大家觉得这样有没有问题?
而FeignClient代码遵循SpringMVC的风格,因此与商品微服务的Controller完全一致。这样就存在一定的问题:
代码冗余。尽管不用写实现,只是写接口,但服务调用方要写与服务controller一致的代码,有几个消费者就要写几次。
增加开发成本。调用方还得清楚知道接口的路径,才能编写正确的FeignClient。
问题解决:
因此,一种比较友好的实践是这样的:
我们的服务提供方不仅提供实体类,还要提供api接口声明
调用方不用自己编写接口方法声明,直接继承提供方给的Api接口即可,
第一步:服务的提供方在leyou-item-interface
中提供API接口,并编写接口声明:
注意:商品服务接口Api,api编写技巧:直接从leyou-item-service2模块中拷贝所需要的接口方法,返回值不再使用ResponseEntity
CategoryApi:
package lucky.leyou.item.api; import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import java.util.List; @RequestMapping("category")
public interface CategoryApi { @GetMapping("names")
List<String> queryNameByIds(@RequestParam("ids") List<Long> ids);
}
GoodsApi :
package lucky.leyou.item.api; import lucky.leyou.common.domain.PageResult;
import lucky.leyou.item.bo.SpuBo;
import lucky.leyou.item.domain.Sku;
import lucky.leyou.item.domain.SpuDetail;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /**
* 商品服务的接口:供feign使用
*/public interface GoodsApi { /**
* 分页查询商品
* @param page
* @param rows
* @param saleable
* @param key
* @return
*/
@GetMapping(path = "/spu/page")
public PageResult<SpuBo> querySpuBoByPage(
@RequestParam(value = "key", required = false)String key,
@RequestParam(value = "saleable", required = false)Boolean saleable,
@RequestParam(value = "page", defaultValue = "1")Integer page,
@RequestParam(value = "rows", defaultValue = "5")Integer rows
); /**
* 根据spu商品id查询详情
* @param id
* @return
*/
@GetMapping("/spu/detail/{id}")
SpuDetail querySpuDetailById(@PathVariable("id") Long id); /**
* 根据spu的id查询sku
* @param id
* @return
*/
@GetMapping("sku/list")
List<Sku> querySkuBySpuId(@RequestParam("id") Long id);
}
BrandApi:
package lucky.leyou.item.api; import lucky.leyou.item.domain.Brand;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping; @RequestMapping("brand")
public interface BrandApi { @GetMapping("{id}")
public Brand queryBrandById(@PathVariable("id") Long id);
}
SpecificationApi:
package lucky.leyou.item.api; import lucky.leyou.item.domain.SpecParam;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam; import java.util.List; /**
* 规格参数的接口:供feign使用
*/
@RequestMapping("spec")
public interface SpecificationApi { @GetMapping("params")
public List<SpecParam> queryParams(
@RequestParam(value = "gid", required = false) Long gid,
@RequestParam(value = "cid", required = false) Long cid,
@RequestParam(value = "generic", required = false) Boolean generic,
@RequestParam(value = "searching", required = false) Boolean searching
); }
第二步:在调用方leyou-search
中编写FeignClient,但不要写方法声明了,直接继承leyou-item-interface
提供的api接口:
商品的FeignClient:
package lucky.leyou.client; import lucky.leyou.item.api.GoodsApi;
import org.springframework.cloud.openfeign.FeignClient; @FeignClient(value = "item-service")
public interface GoodsClient extends GoodsApi {
}
商品分类的FeignClient:
package lucky.leyou.client; import lucky.leyou.item.api.CategoryApi;
import org.springframework.cloud.openfeign.FeignClient; @FeignClient(value = "item-service")
public interface CategoryClient extends CategoryApi {
}
品牌的FeignClient:
package lucky.leyou.client; import lucky.leyou.item.api.BrandApi;
import org.springframework.cloud.openfeign.FeignClient; @FeignClient("item-service")
public interface BrandClient extends BrandApi {
}
规格参数的FeignClient:
package lucky.leyou.client; import lucky.leyou.item.api.SpecificationApi;
import org.springframework.cloud.openfeign.FeignClient; @FeignClient("item-service")
public interface SpecificationClient extends SpecificationApi {
}
第三步:
在leyou-search中引入springtest依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
在接口上按快捷键:Ctrl + Shift + T
package lucky.leyou.client; import lucky.leyou.LeyouSearchServiceApplication;
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.test.context.junit4.SpringRunner; import java.util.Arrays;
import java.util.List; import static org.junit.Assert.*; @RunWith(SpringRunner.class)
@SpringBootTest(classes = LeyouSearchServiceApplication.class)
public class CategoryClientTest { @Autowired
private CategoryClient categoryClient; @Test
public void testQueryCategories() {
List<String> names = this.categoryClient.queryNameByIds(Arrays.asList(1L, 2L, 3L));
names.forEach(System.out::println);
}
}
启动工程报错,feign启动报错
***************************
APPLICATION FAILED TO START
***************************
Description: The bean 'pigx-upms-biz.FeignClientSpecification', defined in null, could not be registered. A bean with that name has already been defined in null and overriding is disabled. Action: Consider renaming one of the beans or enabling overriding by setting spring.main.allow-bean-definition-overriding=true Process finished with exit code 1
解决方案:
在模块的application.yml文件中添加
启动成功:
执行结果:
总结:在leyou-search这个微服务中通过Feign调用的leyou-item这个微服务
4.导入数据
(1)创建GoodsRepository
java代码:
package lucky.leyou.reponsitory; import lucky.leyou.domain.Goods;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; public interface GoodsRepository extends ElasticsearchRepository<Goods, Long> {
}
(2)导入数据
导入数据其实就是查询数据,然后把查询到的Spu转变为Goods来保存,因此我们先编写一个SearchService,然后在里面定义一个方法, 把Spu转为Goods
package lucky.leyou.service; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import lucky.leyou.client.BrandClient;
import lucky.leyou.client.CategoryClient;
import lucky.leyou.client.GoodsClient;
import lucky.leyou.client.SpecificationClient;
import lucky.leyou.domain.Goods;
import lucky.leyou.item.domain.*;
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 BrandClient brandClient; @Autowired
private CategoryClient categoryClient; @Autowired
private GoodsClient goodsClient; @Autowired
private SpecificationClient specificationClient; private static final ObjectMapper MAPPER = new ObjectMapper(); /**
* 把Spu转为Goods
* @param spu
* @return
* @throws IOException
*/
public Goods buildGoods(Spu spu) throws IOException { // 创建goods对象
Goods goods = new Goods(); // 根据品牌id查询品牌
Brand brand = this.brandClient.queryBrandById(spu.getBrandId()); // 查询分类名称,Arrays.asList该方法能将方法所传参数转为List集合
List<String> names = this.categoryClient.queryNameByIds(Arrays.asList(spu.getCid1(), spu.getCid2(), spu.getCid3())); // 根据spuid查询spu下的所有sku
List<Sku> skus = this.goodsClient.querySkuBySpuId(spu.getId());
//初始化一个价格集合,收集所有的sku的价格
List<Long> prices = new ArrayList<>();
//收集sku的必要的字段信息
List<Map<String, Object>> skuMapList = new ArrayList<>();
// 遍历skus,获取价格集合
skus.forEach(sku ->{
prices.add(sku.getPrice());
Map<String, Object> skuMap = new HashMap<>();
skuMap.put("id", sku.getId());
skuMap.put("title", sku.getTitle());
skuMap.put("price", sku.getPrice());
//获取sku中的图片,数据库中的图片可能是多张,多张是以,分隔,所以也以逗号进行切割返回图片数组,获取第一张图片
skuMap.put("image", StringUtils.isNotBlank(sku.getImages()) ? StringUtils.split(sku.getImages(), ",")[0] : "");
skuMapList.add(skuMap);
}); // 以tb_spec_param表中的分类cid字段和searching字段为查询条件查询出tb_spec_param表中所有的搜索规格参数
//将每一个查询结果封装成SpecParam这个bean对象中,将bean对象放入map中构成查询结果集
List<SpecParam> params = this.specificationClient.queryParams(null, spu.getCid3(), null, true);
// 根据spuid查询spuDetail(即数据库表tb_spu_detail中的一行数据)。获取规格参数值
SpuDetail spuDetail = this.goodsClient.querySpuDetailById(spu.getId());
// 获取通用的规格参数,利用jackson工具类json转换为object对象(反序列化),参数1:要转化的json数据,参数2:要转换的数据类型格式
Map<Long, Object> genericSpecMap = MAPPER.readValue(spuDetail.getGenericSpec(), new TypeReference<Map<Long, Object>>() {
});
// 获取特殊的规格参数
Map<Long, List<Object>> specialSpecMap = MAPPER.readValue(spuDetail.getSpecialSpec(), new TypeReference<Map<Long, List<Object>>>() {
});
// 定义map接收{规格参数名,规格参数值}
Map<String, Object> paramMap = new HashMap<>();
params.forEach(param -> {
// 判断是否通用规格参数
if (param.getGeneric()) {
// 获取通用规格参数值
String value = genericSpecMap.get(param.getId()).toString();
// 判断是否是数值类型
if (param.getNumeric()){
// 如果是数值的话,判断该数值落在那个区间
value = chooseSegment(value, param);
}
// 把参数名和值放入结果集中
paramMap.put(param.getName(), value);
} else {
paramMap.put(param.getName(), specialSpecMap.get(param.getId()));
}
}); // 设置参数
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());
goods.setAll(spu.getTitle() +" "+ StringUtils.join(names, " ")+" "+brand.getName());
//获取spu下的所有sku的价格
goods.setPrice(prices);
//获取spu下的所有sku,并使用jackson包下ObjectMapper工具类,将任意的Object对象转化为json字符串
goods.setSkus(MAPPER.writeValueAsString(skuMapList));
//获取所有的规格参数{name:value}
goods.setSpecs(paramMap); return goods;
} /**
* 判断value值所在的区间
* 范例:value=5.2 Segments:0-4.0,4.0-5.0,5.0-5.5,5.5-6.0,6.0-
* @param value
* @param p
* @return
*/
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;
} }
然后编写一个测试类,循环查询Spu,然后调用IndexService中的方法,把SPU变为Goods,然后写入索引库:
package lucky.leyou.elasticsearch; import lucky.leyou.client.GoodsClient;
import lucky.leyou.common.domain.PageResult;
import lucky.leyou.domain.Goods;
import lucky.leyou.item.bo.SpuBo;
import lucky.leyou.reponsitory.GoodsRepository;
import lucky.leyou.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.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 ElasticSearchTest1 {
@Autowired
private ElasticsearchTemplate elasticsearchTemplate; @Autowired
private GoodsRepository goodsRepository; @Autowired
private GoodsClient goodsClient; @Autowired
private SearchService searchService; @Test
public void test(){
//创建索引,会根据Goods类的@Document注解信息来创建
this.elasticsearchTemplate.createIndex(Goods.class);
//配置映射,会根据Goods类中的id、Field等字段来自动完成映射
elasticsearchTemplate.putMapping(Goods.class); Integer page = 1;
Integer rows = 100; do {
// 分页查询spuBo,获取分页结果集
PageResult<SpuBo> pageResult = this.goodsClient.querySpuBoByPage(null, null, page, rows);
// 遍历spubo集合转化为List<Goods>
List<Goods> goodsList = pageResult.getItems().stream().map(spuBo -> {
try {
return this.searchService.buildGoods(spuBo);
} catch (IOException e) {
e.printStackTrace();
}
return null;
}).collect(Collectors.toList()); //执行新增数据的方法
this.goodsRepository.saveAll(goodsList); // 获取当前页的数据条数,如果是最后一页,没有100条
rows = pageResult.getItems().size();
// 每次循环页码加1
page++;
} while (rows == 100); }
}
执行后
打开postman进行测试,输入http://localhost:9200/goods/_search
032 搭建搜索微服务01----向ElasticSearch中导入数据--通过Feign实现微服务之间的相互调用的更多相关文章
- 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据操作(二)
CSSDesk body { background-color: #2574b0; } /*! zybuluo */ article,aside,details,figcaption,figure,f ...
- 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据操作
http://www.cnblogs.com/wgp13x/p/4934521.html 内容一样,样式好的版本. 使用Hive或Impala执行SQL语句,对存储在Elasticsearch中的数据 ...
- Elasticsearch:如何把Elasticsearch中的数据导出为CSV格式的文件
本教程向您展示如何将数据从Elasticsearch导出到CSV文件. 想象一下,您想要在Excel中打开一些Elasticsearch中的数据,并根据这些数据创建数据透视表. 这只是一个用例,其中将 ...
- 使用Hive读取ElasticSearch中的数据
本文将介绍如何通过Hive来读取ElasticSearch中的数据,然后我们可以像操作其他正常Hive表一样,使用Hive来直接操作ElasticSearch中的数据,将极大的方便开发人员.本文使用的 ...
- elasticsearch river 从数据库中导入数据
Elasticsearch中使用reiver-jdbc导入数据 2014-05-13 15:10 本站整理 浏览(3384) Elasticsearch中使用reiver-jdbc导入数据,有 ...
- 01从DataGrid中导入到Excel
01网络上有很多导出数据到Excel的方法,我在网上找到了一种比较简单实用的方法(参考了网友的方法) string fileName = ""; Microsoft.Win32.S ...
- spring cloud各个微服务之间如何相互调用(Feign、Feign带token访问服务接口)
1.首先先看什么是Feign. 这里引用“大漠知秋”的博文https://blog.csdn.net/wo18237095579/article/details/83343915 2.若其他服务的接口 ...
- Solr之搭建Solr5.2.1服务并从Mysql上导入数据
原文地址:http://www.bkjia.com/webzh/1026243.html
- ES 20 - 查询Elasticsearch中的数据 (基于DSL查询, 包括查询校验match + bool + term)
目录 1 什么是DSL 2 DSL校验 - 定位不合法的查询语句 3 match query的使用 3.1 简单功能示例 3.1.1 查询所有文档 3.1.2 查询满足一定条件的文档 3.1.3 分页 ...
随机推荐
- A MacFUSE-Based Process File System for Mac OS X
referer: http://osxbook.com/book/bonus/chapter11/procfs/ Processes as Files The process file system ...
- django rest_framework vue 实现用户登录
django rest_framework vue 实现用户登录 后端代码就不介绍了,可以参考 django rest_framework 实现用户登录认证 这里介绍一下前端代码,和前后端的联调过程 ...
- python快速生成验证码
利用python库random,string生成大小写字母和数字的随机验证码 import random import string def generate_code(bit_num): ''' : ...
- JMETER 使用JDBC查找数据作为参数
有些情况下我们需要读取平台中的用户作为参数,比如用户ID作为参数进行压力测试,我们可以使用CSV文件,如果这样做,需要每一次 都构造文件,比较麻烦.如果可以查询数据库,将查出的数据作为参数循环传入的话 ...
- snowflake时间回退问题思考
算法比较简单,每个id-generator负责生成的ID由3部分组成,41位时间戳可以表示到毫秒,10bit worker-id内部可自行划分,比如3位表示IDC,7位表示机器.最后12位是在一毫秒的 ...
- nginx 安装第三方模块(lua)并热升级
需求: nginx上将特定请求拒绝,并返回特定值. 解决办法: 使用lua脚本,实现效果. 操作步骤: 安装Luajit环境 重新编译nginx(目标机器上nginx -V 配置一致,并新增两个模块n ...
- windows 给mysql安装innodb引擎
1.启用InnoDB 打开my.ini文件,找到[skip-innodb],在前面加# 2.更改数据库默认引擎为InnoDB 打开my.ini文件,更改[default-storage-e ...
- 使用docker创建mongodb
1.创建 MongoDB 数据卷 docker volume create mongo_data_yapi 2.启动 MongoDB docker run -d --name mongo-yapi - ...
- Linux下远程连接MySQL数据库
估计搞了一个多小时才把这个远程连接搞好.一台本地电脑,一台云服务器,都是linux系统. 步骤 在服务器端开启远程访问 首先进入mysql数据库,然后输入下面两个命令: grant all privi ...
- linux 利器
ncat nc strace dmesg 很重要,好多错误从程序的角度无法得到答案