8_搭建商城搜索微服务[通俗易懂]

8_搭建商城搜索微服务[通俗易懂]搜索服务的父项目:supergo_search1、建Module:supergo_search2、删除src搜索服务的提供者:supergo_search_service90031、建Module:supergo_search_service90032、改pom<?xmlversion=”1.0″encoding=”UTF-8″?><projectxmlns=”http://maven.apache.org/POM/4.0.0″xmlns:xsi=

大家好,又见面了,我是你们的朋友全栈君。

搜索服务的父项目:supergo_search

1、建Module:supergo_search

2、删除src


搜索服务的提供者:supergo_search_service9003

1、建Module:supergo_search_service9003

2、改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>supergo_search</artifactId>
<groupId>com.supergo</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>supergo_search_service9003</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--spring-es-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>com.supergo</groupId>
<artifactId>supergo-mapper</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.supergo</groupId>
<artifactId>supergo-common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.14.8</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>

3、启动类

package com.supergo.search;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import tk.mybatis.spring.annotation.MapperScan;
@SpringBootApplication
@EnableEurekaClient
@MapperScan("com.supergo.search.mapper")
public class SearchApplication9003 { 

public static void main(String[] args) { 

SpringApplication.run(SearchApplication9003.class, args);
}
}

4、建 yml

本次使用的ElasticSearch版本为5.6.8,需要在yml中配置连接

# 端口
server:
port: 9003
# 名字
spring:
application:
name: supergo-manager # 代表的就是我以什么样的名字入驻进的注册中心
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 当前数据源操作类型
driver-class-name: com.mysql.jdbc.Driver # mysql驱动类
url: jdbc:mysql://127.0.0.1:3306/supergo?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: 123456
# druid 专属配置
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 1000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: select 'x'
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-open-prepared-statements: 50
max-pool-prepared-statement-per-connection-size: 20
# stat是统计,wall是SQL防火墙,防SQL注入的,log4j是用来输出统计数据的
# filters: stat,wall,log4j,config
##是否启用StatFilter默认值true: 排除一些不必要的url
web-stat-filter:
enabled: true
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
#是否启用StatViewServlet默认值true
stat-view-servlet:
allow: 127.0.0.1 #IP 白名单
url-pattern: /druid/* #监控地址,默认 /druid/*
login-username: admin
login-password: admin #
# deny:IP #黑名单
# 最大请求文件的大小
servlet:
multipart:
max-request-size: 5MB
# ElasticSearch连接配置
data:
elasticsearch:
cluster-name: cluster_es
cluster-nodes: 192.168.77.138:9300
eureka:
client:
register-with-eureka: true # 表示将自己注册到 eureka server ,默认为 true
fetch-registry: true # 表示是否从eureka server 抓取已有的注册信息,默认为true。单节点为所谓,集群必须为 true,才能配合ribbon使用负载均衡
service-url:
# 单机版:只用注册进一个服务中心【defaultZone: http://127.0.0.1:7001/eureka/】
defaultZone: http://eureka7001.com:7001/eureka/
# 集群版:需要同时注册进每个注册中心
# defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com/eureka/
# 显示的服务主机名称
instance:
prefer-ip-address: true # 访问路径显示 ip【统一:方便调试】
ip-address: 127.0.0.1
instance-id: ${ 
eureka.instance.ip-address}.${ 
server.port}
lease-renewal-interval-in-seconds: 3
lease-expiration-duration-in-seconds: 10
#网关设置了根路径,默认监控路径发生了变化
health-check-url-path: /api/actuator/health
#actuator服务监控与管理
management:
endpoint:
#开启端点
shutdown:
enabled: true
health:
show-details: always
# 加载所有的端点
endpoints:
web:
exposure:
include: "*"

5、实体类

接口对应的实体类:

package com.supergo.search.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
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;
/** * @Author: xj0927 * @Description: * @Date Created in 2021-01-04 12:26 * @Modified By: */
@Document(indexName = "supergo", type = "goods")
@Data
@AllArgsConstructor
@NoArgsConstructor
public class GoodsEntity { 

@Id
@Field(type = FieldType.Long, store = true)
private Long id;
@Field(type = FieldType.text, store = true, analyzer = "ik_max_word")
private String goods_name;
@Field(type = FieldType.keyword, store = true)
private String seller_id;
@Field(type = FieldType.keyword, store = true)
private String nick_name;
@Field(type = FieldType.Long, store = true)
private long brand_id;
@Field(type = FieldType.keyword, store = true)
private String brand_name;
@Field(type = FieldType.Long, store = true)
private long category1_id;
@Field(type = FieldType.keyword, store = true)
private String cname1;
@Field(type = FieldType.Long, store = true)
private long category2_id;
@Field(type = FieldType.keyword, store = true)
private String cname2;
@Field(type = FieldType.Long, store = true)
private long category3_id;
@Field(type = FieldType.keyword, store = true)
private String cname3;
@Field(type = FieldType.keyword, store = true, index = false)
private String small_pic;
@Field(type = FieldType.Float, store = true)
private double price;
}

搜索结果实体类

package com.supergo.search.entity;
import java.io.Serializable;
import java.util.List;
/** * @Author: xj0927 * @Description: * @Date Created in 2021-01-04 12:28 * @Modified By: */
public class SearchResult implements Serializable { 

private List<GoodsEntity> goodsList;
private List<?> aggs;
public List<GoodsEntity> getGoodsList() { 

return goodsList;
}
public void setGoodsList(List<GoodsEntity> goodsList) { 

this.goodsList = goodsList;
}
public List<?> getAggs() { 

return aggs;
}
public void setAggs(List<?> aggs) { 

this.aggs = aggs;
}
}

6、接口

mysql操作相关的接口:

package com.supergo.search.mapper;
import com.supergo.search.entity.GoodsEntity;
import org.apache.ibatis.annotations.Select;
import java.util.List;
/** * @Author: xj0927 * @Description: * @Date Created in 2021-01-04 12:27 * @Modified By: */
public interface GoodsMapper { 

@Select("SELECT\n" +
"\ta.id,\n" +
"\ta.goods_name,\n" +
"\ta.seller_id,\n" +
"\tb.nick_name,\n" +
"\ta.brand_id,\n" +
"\tc.name brand_name,\n" +
"\ta.category1_id,\n" +
"\td.NAME cname1,\n" +
"\ta.category2_id,\n" +
"\te.NAME cname2,\n" +
"\ta.category3_id,\n" +
"\tf.NAME cname3,\n" +
"\ta.small_pic,\n" +
"\ta.price\n" +
"FROM\n" +
"\ttb_goods a\n" +
"LEFT JOIN tb_seller b ON a.seller_id = b.seller_id\n" +
"LEFT JOIN tb_brand c ON a.brand_id = c.id\n" +
"LEFT JOIN tb_item_cat d ON a.category1_id = d.id\n" +
"LEFT JOIN tb_item_cat e ON a.category2_id = e.id\n" +
"LEFT JOIN tb_item_cat f ON a.category3_id = f.id\n" +
"WHERE\n" +
"\ta.is_delete = 0\n" +
"AND a.is_marketable = 1")
List<GoodsEntity> getGoodsList();
}

elasticsearch操作相关的接口:

package com.supergo.search.repository;
import com.supergo.search.entity.GoodsEntity;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
/** * @Author: xj0927 * @Description: * @Date Created in 2021-01-04 12:31 * @Modified By: */
public interface GoodsRepository extends ElasticsearchRepository<GoodsEntity, Long> { 

}

7、导入索引库

service

从mysql数据库查询数据,然后再导入es

@Service
public class SearchService { 

@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsRepository goodsRepository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
/*** * 查询数据库将所有商品数据导入到索引库 */
public HttpResult importGoods() { 

//查询数据库
List<GoodsEntity> goodsList = goodsMapper.getGoodsList();
System.out.println(goodsList);
//把商品数据写入索引库
System.out.println("数据导入开始。。。");
goodsList.forEach(g -> goodsRepository.save(g));
System.out.println("数据导入完成!");
return HttpResult.ok();
}
}

controller

@RestController
public class SearchController { 

@Autowired
private SearchService searchService;
@RequestMapping("/goods/import")
public HttpResult goodsImport() { 

new Thread(() -> searchService.importGoods()).start();
return HttpResult.ok();
}
}

测试

浏览器输入:

http://localhost:9003/goods/import

查看索引:

在这里插入图片描述

数据成功导入ElasticSearch


8、搜索索引库

数据导入Es后,下面开始搜索服务的创建

先看京东的搜索方式:

在输入栏搜索“苹果”,会出现按不同方式的聚合结果

在这里插入图片描述

然后在分类栏,选择”苹果”,

在这里插入图片描述

对地址url进行转义解析:

在这里插入图片描述

本次也是使用类型方案:关键词使用查询,限制条件使用过滤

service

@Service
public class SearchService { 

@Autowired
private GoodsMapper goodsMapper;
@Autowired
private GoodsRepository goodsRepository;
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
/** * @Description: 商品查询 * 原则:第一个参数使用查询方式,其他参数使用过滤 [提高效率] * @Author: xj0927 * @Date Created in 2021/1/4 19:55 */
public SearchResult search(String keyword, Map<String, String> filters, int page, int size) { 

//设置查询条件
BoolQueryBuilder builder = QueryBuilders.boolQuery()
.should(QueryBuilders.multiMatchQuery(keyword, "goods_name"));
//判断是否有过滤条件
if (filters != null && !filters.isEmpty()) { 

filters.keySet().forEach(key -> { 

builder.filter(QueryBuilders.termQuery(key, filters.get(key)));
});
}
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(builder) //查询条件
.withPageable(PageRequest.of(page, size))//设置分页信息
.addAggregation(AggregationBuilders.terms("category_aggs").field("cname3"))//聚合条件1
.addAggregation(AggregationBuilders.terms("brand_aggs").field("brand_name"))//聚合条件2
.withHighlightFields(new HighlightBuilder.Field("name").preTags("<span style='color:red>").postTags("</span>"))//设置高亮显示
.build(); //创建q
//执行查询
AggregatedPage<GoodsEntity> sResult = elasticsearchTemplate.queryForPage(query, GoodsEntity.class, new SearchResultMapper() { 

@Override
public <T> AggregatedPage<T> mapResults(SearchResponse searchResponse, Class<T> aClass, Pageable pageable) { 

List<GoodsEntity> goodsList = new ArrayList<>();
SearchHits searchHits = searchResponse.getHits();
searchHits.forEach(hits -> { 

//原文档部分
GoodsEntity goodsEntity = new GoodsEntity();
goodsEntity.setId((Long) hits.getSource().get("id"));
goodsEntity.setGoods_name((String) hits.getSource().get("goods_name"));
goodsEntity.setSeller_id((String) hits.getSource().get("seller_id"));
goodsEntity.setNick_name((String) hits.getSource().get("nick_name"));
goodsEntity.setBrand_id((Integer) hits.getSource().get("brand_id"));
goodsEntity.setBrand_name((String) hits.getSource().get("brand_name"));
goodsEntity.setCategory1_id((Integer) hits.getSource().get("category1_id"));
goodsEntity.setCname1((String) hits.getSource().get("cname1"));
goodsEntity.setCategory2_id((Integer) hits.getSource().get("category2_id"));
goodsEntity.setCname2((String) hits.getSource().get("cname2"));
goodsEntity.setCategory3_id((Integer) hits.getSource().get("category3_id"));
goodsEntity.setCname3((String) hits.getSource().get("cname3"));
goodsEntity.setSmall_pic((String) hits.getSource().get("small_pic"));
goodsEntity.setPrice((Double) hits.getSource().get("price"));
//取高亮部分
HighlightField highlightField = hits.getHighlightFields().get("goods_name");
if (highlightField != null) { 

String hl = highlightField.getFragments()[0].string();
goodsEntity.setGoods_name(hl);
}
goodsList.add(goodsEntity);
});
AggregatedPage<T> aggregatedPage = new AggregatedPageImpl<T>((List<T>) goodsList, pageable, searchHits.totalHits, searchResponse.getAggregations());
return aggregatedPage;
}
});
//取查询结果
List<GoodsEntity> content = sResult.getContent();
SearchResult searchResult = new SearchResult();
searchResult.setGoodsList(content);
//取分类聚合结果
Terms termsCat = (Terms) sResult.getAggregation("category_aggs");
List<String> catAggsList = termsCat.getBuckets().stream().map(e -> e.getKeyAsString()).collect(Collectors.toList());
Map catAggsMap = new HashMap();
catAggsMap.put("name", "分类");
catAggsMap.put("field", "cname3");
catAggsMap.put("content", catAggsList);
//取品牌聚合结果
Terms termsBrand = (Terms) sResult.getAggregation("brand_aggs");
List<String> brandAggsList = termsBrand.getBuckets().stream().map(e -> e.getKeyAsString()).collect(Collectors.toList());
Map brandAggsMap = new HashMap();
brandAggsMap.put("name", "品牌");
brandAggsMap.put("field", "brand_name");
brandAggsMap.put("content", brandAggsList);
List aggsList = new ArrayList();
aggsList.add(brandAggsMap);
aggsList.add(catAggsMap);
//设置过滤条件
searchResult.setAggs(aggsList);
return searchResult;
}
}

controller

![5](images/5.png)RestController
public class SearchController { 

@Autowired
private SearchService searchService;
@RequestMapping("/goods/search")
public SearchResult search(@RequestParam(required = true) String keyword,
@RequestParam(required = false, value = "ev") String filter,
@RequestParam(defaultValue = "1") int page,
@RequestParam(defaultValue = "5") int size) { 

//filter的参数形式:ev=brand_name-小米|category3_id-255、
Map<String, String> filterMap = null;
try { 

if (StringUtils.isNotBlank(filter)) { 

String[] filters = filter.split("\\|");
filterMap = Stream.of(filters).collect(Collectors.toMap(e -> e.split("-")[0].trim(), e -> e.split("-")[1].trim()));
}
} catch (Exception e) { 

e.printStackTrace();
}
System.out.println(filter);
System.out.println(filterMap);
SearchResult searchResult = searchService.search(keyword, filterMap, page, size);
return searchResult;
}
}

测试

浏览器输入:

http://localhost:9003/goods/search?keyword=手机&ev=brand_name-联想

在这里插入图片描述


版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/152834.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)
blank

相关推荐

  • win7上ModifyStyleEx无效的解决办法

    win7上ModifyStyleEx无效的解决办法创建窗口时,指定窗口样式为WS_EX_TOOLWINDOW;创建完成之后,还要通过以下语句进一步修改窗口属性:ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWIN

  • [CNN] 卷积、反卷积、池化、反池化「建议收藏」

    [CNN] 卷积、反卷积、池化、反池化「建议收藏」之前一直太忙,没时间整理,这两天抽出点时间整理一下卷积、反卷积、池化、反池化的内容。一、卷积1、卷积的简单定义卷积神经网络中的卷积操作可以看做是输入和卷积核的内积运算。其运算过程非常容易理解,下面会有详细解释。2、举例解释(1)为了方便直接解释,我们首先以一个通道为例进行讲解,首先明确概念:1)输入是一个5*5的图片,其像素值如下:[11100011100011100110011…

  • 大数据平台展示可视化效果,echarts图表实战项目(源码50套)「建议收藏」

    大数据平台展示可视化效果,echarts图表实战项目(源码50套)「建议收藏」最近接了个任务需要用H5在前台两个大电视上做两页数据展示公司的产品数据,效果要高大上,充分展示咱们公司的实力,给各位来公司参观的大能们留下深刻的印象。还好之前接触过HTML5,所以第一时间想到就是echarts,这个框架对于数据展示尤其图表类处理的还是非常强大和炫酷的。说干就干,首先到官网上把框架下下来,各组件Demo和API都熟悉下,对于你想要的东西和效果心里有个底,就开始动工了。官网地址是:https://echarts.apache.org,里面的Demo都是代码和效果图文并貌,还可以在线修改

    2022年10月12日
  • eclipse 导入Java项目「建议收藏」

    eclipse 导入Java项目「建议收藏」偶们公司的这个老项目真的是让人头疼,师父们给偶导了N遍,偶还是记不住。俗话说“好记性不如烂笔头”,于是乎,偶决定记下来,便于自己日后查看,同时也可以给同样小白的同胞们一点参考。说的可能有点啰嗦,但是真的很详细,自己一步步来写的。第一步,打开eclipse,选择一个新的workspace,点击“OK”,进入eclipse界面。第二步,新建Java项目:点击file下面的“new”图标,选择“Java…

  • Android中常用的adb shell命令

    Android中常用的adb shell命令注意事项:这里写的命令,网页会重新编辑格式,比如我写了两个减号,发布后变成了一个减号;如果我说的命令不能正确执行,请手动输入命令,切记切换英文状态。android常用shell命令记录下来备忘设置adb环境变量其实就是将adb.exe的路径放到Path中,目的是cmd直接可以使用adb命令比如我的adb.exe路径G:\tools\adt-bundle\sdk\platform-tools

  • C++之Error无法解析的外部符号[通俗易懂]

    C++之Error无法解析的外部符号[通俗易懂]C++之VisualStudio的使用遇到问题解决文章目录C++之VisualStudio的使用遇到问题解决问题一无法解析的外部符号问题二无法打开文件lib问题三debug不可以运行,release可以运行问题一无法解析的外部符号[问题描述]在编译中遇到,viaualstudio无法解析的外部符号该符号在外部函数中被引用[问题处理]1.分析问题,这个错误定义为一个:连接错误。2.根本原因是函数虽然申明了,但是没有定义函数的实现3.排查问题出现的几

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号