搜索服务
需求
实现整个系统的全站搜索
主要是实现商品相关搜索
分析
技术站:ElasticSearch Spring Data Elasticsearch
索引:
商品信息:micro:products
文档:
商品id 商品名称 商品的关键字 商品的类型
1.数据一致性的保证:
a.采用定时任务实现ES数据的维护
从Redis获取数据:
1个:新增的内容
1个:修改的内容
1个:删除的内容
批处理
b.MQ
后台系统上架商品、下架、修改的时候分别发送MQ消息到队列,消息监听器监听队列的消息变化,更新ES
二、代码实现
public class SystemConfig {
/*
ES用来存储商品信息的索引名称
*/
public static final String ES_PRODUCTS="micro:es:product";
}
package com.lxm.searchservice.model;
import com.lxm.common.config.SystemConfig;
import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
/**
* @Author LXM
* @Date 2020/7/16 0016
*/
@Data
@Document(indexName = SystemConfig.ES_PRODUCTS)
public class ProductModel {
@Id //唯一标记
private Integer id;
@Field(index = true,name = "esname")//设置字段信息,比如分词规则
private String name;
private String type;
private String keywords;
}
package com.lxm.searchservice.service.impl;
import com.lxm.common.config.RedisKeyConfig;
import com.lxm.common.utils.RUtil;
import com.lxm.common.utils.RedissionCore;
import com.lxm.common.vo.R;
import com.lxm.searchservice.dao.ProductDao;
import com.lxm.searchservice.model.ProductModel;
import com.lxm.searchservice.service.ProductService;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.WildcardQueryBuilder;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHit;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.*;
/**
* @Author LXM
* @Date 2020/7/16 0016
*/
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private ProductDao dao;
@Resource
private RedissionCore redissionCore;
@Resource
private ElasticsearchRestTemplate restTemplate;
/**
* 批量向es添加修改删除数据
* @return
*/
@Override
public R batchData() {
List<ProductModel> productModels=new ArrayList<>();
//批量新增
Map<Object,Object> add=redissionCore.getMap(RedisKeyConfig.ES_PRODUCT_ADD);
add.values().stream().forEach(o->{
if (o instanceof ProductModel){
productModels.add((ProductModel) o);
}
});
//批量修改
Map<Object,Object> update=redissionCore.getMap(RedisKeyConfig.ES_PRODUCT_UPDATE);
update.values().stream().forEach(o -> {
if (o instanceof ProductModel){
productModels.add((ProductModel) o);
}
});
dao.saveAll(productModels);
//批量删除
Map<Object,Object> del=redissionCore.getMap(RedisKeyConfig.ES_PROUCT_DEL);
del.values().stream().forEach(o -> {
if (o instanceof ProductModel){
productModels.add((ProductModel) o);
}
});
dao.deleteAll(productModels);
return RUtil.ok();
}
/**
* 按照关键字进行搜索
* @param msg
* @return
*/
@Override
public R search(String msg) {
//设定模糊查询查询条件
//根据商品类型进行模糊匹配
WildcardQueryBuilder wildcard1= QueryBuilders.wildcardQuery("type","*"+msg+"*");
//根据商品标题进行模糊匹配
WildcardQueryBuilder wildcard2= QueryBuilders.wildcardQuery("name","*"+msg+"*");
//根据商品关键字进行模糊匹配
WildcardQueryBuilder wildcard3= QueryBuilders.wildcardQuery("keywords","*"+msg+"*");
//bool查询
BoolQueryBuilder boolQueryBuilder=QueryBuilders.boolQuery();
//拼接查询条件
boolQueryBuilder.should(wildcard1).should(wildcard2).should(wildcard3);
//实例化查询对象
NativeSearchQueryBuilder queryBuilder=new NativeSearchQueryBuilder();
queryBuilder.withQuery(boolQueryBuilder);
SearchHits<ProductModel> hits=restTemplate.search(queryBuilder.build(),ProductModel.class);
HashMap<String,Integer> map=new HashMap<>();
for (SearchHit<ProductModel> s:hits){
ProductModel p=s.getContent();
//校验类型
if (p.getType().contains(msg)){
if (map.containsKey(p.getType())){
map.put(p.getType(),map.get(p.getType())+1);
}else {
map.put(p.getType(),1);
}
}
//校验关键字
if (p.getKeywords().contains(msg)){
if (map.containsKey(p.getKeywords())){
map.put(p.getKeywords(),map.get(p.getKeywords())+1);
}else {
map.put(p.getKeywords(),1);
}
}
if (map.containsKey(msg)){
map.put(msg,map.get(msg)+1);
}else {
map.put(msg,1);
}
}
return RUtil.ok(map);
}
}
package com.lxm.searchservice.dao;
import com.lxm.searchservice.model.ProductModel;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
public interface ProductDao extends ElasticsearchRepository<ProductModel,Integer> {
}
package com.lxm.searchservice.service;
import com.lxm.common.vo.R;
public interface ProductService {
/**
* 批量向es添加修改删除数据
* @return
*/
R batchData();
/**
* 按照关键字进行搜索
* @param msg
* @return
*/
R search(String msg);
}
定时任务——每2小时执行一次同步一次数据
package com.lxm.searchservice.task;
import com.lxm.searchservice.service.ProductService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* @Author LXM
* @Date 2020/7/17 0017
*/
@Component
@Slf4j
public class EsProductTask {
@Resource
private ProductService service;
@Scheduled(cron = "0 0 0/2 * * ? ")
public void task(){
log.info("定时任务-ES-mysql:开始执行:"+System.currentTimeMillis());
service.batchData();
log.info("定时任务-ES-mysql:结束执行:"+System.currentTimeMillis());
}
}
本文介绍了如何使用ElasticSearch实现全站搜索,重点在商品搜索。通过Spring Data Elasticsearch与ElasticSearch集成,建立'micro:products'索引。为了保证数据一致性,采取了两种策略:定时任务每2小时从Redis同步数据,包括新增、修改和删除的内容;后台系统操作如上架、下架、修改商品时,通过MQ发送消息到队列,由消息监听器监听并更新ES。
3190

被折叠的 条评论
为什么被折叠?



