一、缓存管理:
1.1 为zyg-content-service添加依赖:
<!--3.引入redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>
1.2 配置redis
spring:redis:host: 192.168.56.10
1.3 配置redis的序列化机制(默认使用jdk的序列化器进行序列化)
@Configurationpublic class RedisTemplateSerializerConfig {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(factory);//1. 设置key的序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());//2. 设置value的序列化器//方法一:// redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());//方法二:redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}}
1.4 查看序列化后的结果:
1.5 同步数据库与缓存库:
1.5.1 zyg-content-service中新增如下方法:
/*** 功能: 添加广告* 参数:* 返回值: void* 时间: 2021/8/2 15:00*/@Overridepublic void add(ContentEntity content) {//1. 删除redis中的缓存redisTemplate.delete("contents");//2. 添加到数据库中this.save(content);}@Overridepublic void update(ContentEntity content) {//1. 删除redis中的缓存redisTemplate.delete("contents");//2. 调用 修改方法this.updateById(content);}@Overridepublic void delete(List<Long> ids) {//1. 删除redis中的缓存redisTemplate.delete("contents");//2. 根据id删除广告this.removeByIds(ids);}
1.5.2 zyg-manager-web中修改如下方法:
/*** 保存*/@RequestMapping("/save")public R save(@RequestBody ContentEntity content){contentService.add(content);return R.ok();}/*** 修改*/@RequestMapping("/update")public R update(@RequestBody ContentEntity content){contentService.update(content);return R.ok();}/*** 删除*/@RequestMapping("/delete")public R delete(@RequestBody Long[] ids){contentService.delete(Arrays.asList(ids));return R.ok();}
二、搜索(ES)
2.1、新建模块zyg-search-interface与zyg-search-service服务层
2.1.1 zyg-search-interface 模块添加依赖:
<dependencies><dependency><groupId>com.zelin</groupId><artifactId>zyg-pojo</artifactId><version>2.0</version></dependency></dependencies>
2.1.2 zyg-search-service模块添加依赖:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.zelin</groupId><artifactId>zyg-search-interface</artifactId><version>2.0</version></dependency><dependency><groupId>com.zelin</groupId><artifactId>zyg-dao</artifactId><version>2.0</version></dependency><!--3.引入redis--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!--4.引入单元测试--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-test</artifactId></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId></dependency></dependencies>
2.1.3 添加application.yml,配置如下:
server:port: 7003logging:level:com.zelin: debugspring:dubbo:application:name: zyg-search-serviceregistry:address: zookeeper://192.168.56.10:2181base-package: com.zelin.search.serviceprotocol:name: dubboport: 21883redis:host: 192.168.56.10elasticsearch:host: 192.168.56.10port: 9200
2.1.4 添加ES的相关配置:
/*** 描述: ES的配置* 作者: WF* 创建时间: 2021/7/20 15:20*/@ConfigurationProperties(prefix = "elasticsearch")@Configuration //相当于:applicationContext.xml@Datapublic class ElasticSearchConfig {private String host ;private Integer port ;@Bean //相当于:<bean>标签public RestHighLevelClient highLevelClient(){return new RestHighLevelClient(RestClient.builder(new HttpHost(host, port, "http")));}}
2.2、新建zyg-search-web模块:
2.2.1 引入依赖:
<dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.zelin</groupId><artifactId>zyg-search-interface</artifactId><version>2.0</version></dependency><!--1.添加thymeleaf依赖--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!--2.添加devtools--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><optional>true</optional></dependency><dependency><groupId>com.zelin</groupId><artifactId>zyg-dao</artifactId><version>2.0</version></dependency></dependencies>
2.2.2 添加application.yml文件,配置如下:
server:port: 9004logging:level:com.zelin: debugspring:thymeleaf:cache: falseprefix: classpath:/templates/suffix: .htmldubbo:registry:address: zookeeper://192.168.56.10:2181base-package: com.zelin.search.webapplication:name: zyg-search-web
2.2.3 在resources下新建templates目录:
注意:在此目录下放search.html这个搜索文件。
2.2.4 将关于前端搜索的资源添加到/mydat/nginx/html/static/search目录下
2.2.5 配置nginx服务器:
第一步:在/mydata/nginx/conf/nginx.conf文件下添加上游服务器:
第二步:在/mydata/nginx/conf/下定义static.conf文件
第三步:在/mydata/nginx/conf/conf.d/下的zeyigou.conf下添加配置
2.3 导入数据到索引库中
@RunWith(SpringRunner.class)@SpringBootTestpublic class TestES {@Autowiredprivate ItemDao itemDao;@Autowiredprivate ElasticsearchRestTemplate restTemplate;/*** 功能: 导入数据到索引库中* 参数:* 返回值: void* 时间: 2021/8/2 16:39*/@Testpublic void importData(){List<ItemEntity> itemEntities = itemDao.selectList(null);List<ItemEntity> collect = itemEntities.stream().filter(f -> f.getStatus().equals("1")).collect(Collectors.toList());for (ItemEntity itemEntity : collect) {//1. 得到原来的spec字符串String spec = itemEntity.getSpec();//2. 将字符串转换为mapMap specMap = JSON.parseObject(spec, Map.class);//3. 绑定到实体类中itemEntity.setSpecMap(specMap);}restTemplate.save(collect);System.out.println("保存到索引库成功!");}}
2.4 在kibana中查看数据:
三、统一调整所有的项目:
3.1 对于所有的服务层,web层都要引入zyg-dao依赖:
<dependency><groupId>com.zelin</groupId><artifactId>zyg-dao</artifactId><version>2.0</version></dependency>
3.2 对于所有的服务层,必须添加如下的dubbo配置:
spring:dubbo:application:name: zyg-search-serviceregistry:address: zookeeper://192.168.56.10:2181base-package: com.zelin.search.serviceprotocol:name: dubboport: 21883
3.3 对于所有的web必须添加如下的dubbo配置:
spring:dubbo:registry:address: zookeeper://192.168.56.10:2181base-package: com.zelin.search.webapplication:name: zyg-search-web
3.4 在服务层使用alibaba的@Service注解注册服务:
3.5 在web层使用@Reference(timeout=5000)引入服务
四、进行查询
4.1 根据关键字进行分类查询:

说明: 此时查询时必须使用”字段名.keyword”这种形式【将查询关键字当作一个整体,不会分词】,否则,报错!

说明: 此时是对聚合结果进行分析,得到我们想要的结果。
4.2 在redis存放数据(分类、品牌、规格)
分析: ① 当我们查询分类时,可以以分类名称为key,以分类的模板id为值,将所有的分类放到redis中 ② 当我们查询模板时,可以以模板id为key,以品牌列表为值(List
4.2.1 在zyg-sellergoods-service的ItemCatServiceImpl中:(存放分类到redis中)
/*** 功能: 根据父id查询分类列表* 参数:* 返回值: java.util.List<com.zelin.entity.ItemCatEntity>* 时间: 2021/7/27 16:48*/@Overridepublic List<ItemCatEntity> findByParentId(Long pid) {//1. 根据父id查询所有分类List<ItemCatEntity> itemCatEntities = baseMapper.selectList(new QueryWrapper<ItemCatEntity>().eq("parent_id", pid));//2. 将所有的分类放到redis中for (ItemCatEntity entity : list()) {//以分类名称为key,以模板id为值放到redis中redisTemplate.boundHashOps("itemCats").put(entity.getName(),entity.getTypeId());}return itemCatEntities;}
4.2.2 在zyg-sellergoods-service的typeTemplateServiceImpl中:(存放品牌及规格列表)
/*** 功能: 以模板id为key,以规格列表及品牌列表为值放到redis中* 参数:* 返回值: com.zelin.utils.PageUtils* 时间: 2021/8/3 14:55*/@Overridepublic PageUtils list(Map<String, Object> params) {for (TypeTemplateEntity entity : this.list()) {//1. 得到模板的品牌字符串String brandIds = entity.getBrandIds();//2. 将字符串转换为List<Map>List<Map> brandMap = JSON.parseArray(brandIds, Map.class);//3. 这个品牌列表放到redis中redisTemplate.boundHashOps("brandList").put(entity.getId(),brandMap);//4. 得到规格字符串String specIds = entity.getSpecIds();//5. 将规格字符串转换为List<Map>List<Map> specMap = JSON.parseArray(specIds, Map.class);//6. 对每个规格添加规格选项for (Map map : specMap) {String id = (String) map.get("id");List<SpecificationOptionEntity> options = optionService.list(new QueryWrapper<SpecificationOptionEntity>().eq("spec_id", id));map.put("options",options);}//7. 将规格列表保存到redis中redisTemplate.boundHashOps("specList").put(entity.getId(),specMap);}return queryPage(params);}
4.2.3 在zyg-sellergoods-service中对redis进行序列化配置:(存放时序列化)
/*** ------------------------------* 功能:* 作者:WF* 微信:hbxfwf13590332912* 创建时间:2021/7/31-21:55* ------------------------------*/@Configurationpublic class RedisTemplateSerializerConfig {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(factory);//1. 设置key的序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());//2. 设置value的序列化器redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());// redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//3. 如果值是hash,需要使用如下的方式设置序列化器(redis中的hash类型中的小key与小value的序列化)redisTemplate.setHashKeySerializer(new FastJsonRedisSerializer(Long.class));redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}}
重点强调: 1、序列化不光时在存放时要进行,在从redis中取数据时,也需要执行同样的序列化,否则,得不到值!
4.2.4 在zyg-search-service中,进行的redis的序列化:
/*** ------------------------------* 功能:* 作者:WF* 微信:hbxfwf13590332912* 创建时间:2021/7/31-21:55* ------------------------------*/@Configurationpublic class RedisTemplateSerializerConfig {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory factory){RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(factory);//1. 设置key的序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());//2. 设置value的序列化器redisTemplate.setValueSerializer(new GenericFastJsonRedisSerializer());// redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());//3. 如果值是hash,需要使用如下的方式设置序列化器(redis中的hash类型中的小key与小value的序列化)redisTemplate.setHashKeySerializer(new FastJsonRedisSerializer(Long.class));redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());return redisTemplate;}}
4.2.5 执行如下方法,就可以向redis存放数据了:
添加分类在redis:http://localhost:9001/itemcat/findByParentId/0 添加品牌及规格在redis:http://localhost:9001/typetemplate/list
4.3 展示品牌及规格:
4.3.1 在itemSearchServiceImpl中定义从redis中得到品牌及规格列表
/*** 功能: 根据分类名称得到品牌列表及规格列表* 参数:* 返回值: java.util.Map* 时间: 2021/8/3 8:39*/private Map findBrandAndSpecList(String category) {//1. 根据分类名称得到模板idLong typeId = (Long) redisTemplate.boundHashOps("itemCats").get(category);//2. 根据模板id得到品牌列表List<Map> brandList = (List<Map>) redisTemplate.boundHashOps("brandList").get(typeId);//3. 根据模板id得到规格列表List<Map> specList = (List<Map>) redisTemplate.boundHashOps("specList").get(typeId);//4. 定义结果mapMap brandAndSpecmap = new HashMap();//5. 将上面得到的品牌列表及规格列表都放到map中brandAndSpecmap.put("brandList",brandList);brandAndSpecmap.put("specList",specList);//6. 返回return brandAndSpecmap;}
4.3.2 在itemSearchServiceImpl的search()方法中调用:
4.3.3 在前端展示页面:
4.3.4 最后的页面效果:
4.3.5 定义用于在前端传递数据到后端的实体类:
@Data@AllArgsConstructor@NoArgsConstructorpublic class ItemVo implements Serializable {//1. 查询关键字对象private String keywords;}
4.4 进行高亮查询:
4.4.1 前端:
4.4.2 后端itemSearchServiceImpl
@Servicepublic class ItemSearchServiceImpl implements ItemSearchService {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate ElasticsearchRestTemplate restTemplate;/*** 功能: 根据查询参数得到查询结果* 参数:* 返回值: java.util.Map* 时间: 2021/8/2 16:06* @param params*/@Overridepublic Map<String, Object> search(ItemVo params) {//1. 得到查询关键字String keyword = params.getKeywords();if(StringUtils.isBlank(keyword)){keyword = "华为";}System.out.println("keyword = " + keyword);//2. 定义返回结果Map<String, Object> resultMap = new HashMap<>();//3. 定义NativeSearchQueryBuilder对象NativeSearchQueryBuilder searchQueryBuilder = new NativeSearchQueryBuilder().withPageable(PageRequest.of(0,10));//4. 按关键字进行分组查询searchQueryBuilder.addAggregation(AggregationBuilders.terms("categoryGroup").field("category.keyword").size(50));//5. 添加高亮查询//5.1 设置高亮查询searchQueryBuilder.withHighlightBuilder(new HighlightBuilder().field("title") //设置高亮字段.preTags("<span style='color:red'>") //设置高亮显示内容的前缀部分.postTags("</span>")); //设置高亮显示内容的后缀部分//5. 添加查询searchQueryBuilder.withQuery(QueryBuilders.multiMatchQuery( keyword,"title","brand","category"));//6. 得到查询对象NativeSearchQuery searchQuery = searchQueryBuilder.build();//7. 得到命中对象SearchHits<ItemEntity> searchHits = restTemplate.search(searchQuery, ItemEntity.class, IndexCoordinates.of("item"));//8. 得到命中的结果List<SearchHit<ItemEntity>> list = searchHits.getSearchHits();//9. 得到命中结果的选项long total = searchHits.getTotalHits(); //总记录数int totalPage = (int) Math.ceil(total/10.0);//总页数//10. 处理命中结果(原始的关键字查询)//List<ItemEntity> collect = list.stream().map(m -> m.getContent()).collect(Collectors.toList());//11. 得到分组数据Aggregations aggregations = searchHits.getAggregations();//12. 得到关于分类的分组ParsedStringTerms categoryGroupResult = aggregations.get("categoryGroup");//13. 处理分类分组的结果数据List<String> categoryList = categoryGroupResult.getBuckets().stream().map(m -> m.getKeyAsString()).collect(Collectors.toList());System.out.println("categoryList = " + categoryList);//14. 得到高亮查询的结果//14.1 定义存放高亮字段的内容List<ItemEntity> highlights = new ArrayList<>();//14.2 遍历所有命中的数据,从中挑选出高亮数据for (SearchHit<ItemEntity> searchHit : list) { //list: 所有命中的结果对象//14.3 可以直接通过高亮字段名称得到此高亮字段的值List<String> title = searchHit.getHighlightField("title");//14.4 得到未高亮前的数据ItemEntity itemEntity = searchHit.getContent();//14.5 定义存放高亮字段的字符串StringBuffer buffer = new StringBuffer();//14.6 组合高亮字段的值for (String s : title) {buffer.append(s);}//14.7 将高亮字段的值重新设置回原来的对象itemEntity.setTitle(buffer.toString());//14.8 将高亮对象添加到集合中highlights.add(itemEntity);}System.out.println("highlights = " + highlights);//15. 从redis中得到品牌及规格列表Map brandAndSpecMap = new HashMap();//16. 得到用户选择的分类String category = params.getCategory();if(StringUtils.isBlank(category)){if(categoryList != null && categoryList.size() > 0) {category = categoryList.get(0);}}//17. 根据分类进行查询品牌及规格brandAndSpecMap = findBrandAndSpecList(category);//18. 将品牌及规格列表放到大集合中resultMap.putAll(brandAndSpecMap);resultMap.put("rows",highlights); //当前分页记录集合resultMap.put("total",total); //总记录数resultMap.put("categoryList",categoryList); //分类resultMap.put("totalPage",totalPage); //总页数return resultMap;}/*** 功能: 根据分类名称得到品牌列表及规格列表* 参数:* 返回值: java.util.Map* 时间: 2021/8/3 8:39*/private Map findBrandAndSpecList(String category) {//1. 根据分类名称得到模板idObject typeId = redisTemplate.boundHashOps("itemCats").get(category);//2. 根据模板id得到品牌列表List<Map> brandList = (List<Map>) redisTemplate.boundHashOps("brandList").get(typeId);//3. 根据模板id得到规格列表List<Map> specList = (List<Map>) redisTemplate.boundHashOps("specList").get(typeId);//4. 定义结果mapMap brandAndSpecmap = new HashMap();//5. 将上面得到的品牌列表及规格列表都放到map中brandAndSpecmap.put("brandList",brandList);brandAndSpecmap.put("specList",specList);//6. 返回return brandAndSpecmap;}}
4.4.3 查看结果:




