JavaWeb_LeadNews_Day7-ElasticSearch, Mongodb
- elasticsearch
- 安装配置
- app文章搜索
- 创建索引库
- app文章搜索
- 思路分析
- 具体实现
- 新增文章创建索引
- 思路分析
- 具体实现
- MongoDB
- 安装配置
- SpringBoot集成MongoDB
- app文章搜索记录
- 保存搜索记录
- 思路分析
- 具体实现
- 查询搜索历史
- 删除搜索历史
- 搜索联想词
- 来源
- Gitee
elasticsearch
安装配置
https://blog.csdn.net/Y_cen/article/details/131856995
app文章搜索
创建索引库
- 使用postman添加映射
Put请求, Json格式 : http://192.168.174.133:9200/app_info_article{"mappings":{"properties":{"id":{"type":"long"},"publishTime":{"type":"date"},"layout":{"type":"integer"},"images":{"type":"keyword","index": false},"staticUrl":{"type":"keyword","index": false},"authorId": {"type": "long"},"authorName": {"type": "text"},"title":{"type":"text","analyzer":"ik_smart"},"content":{"type":"text","analyzer":"ik_smart"}}} }
- 依赖
<!--elasticsearch--> <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version> </dependency> <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-client</artifactId><version>7.12.1</version> </dependency> <dependency><groupId>org.elasticsearch</groupId><artifactId>elasticsearch</artifactId><version>7.12.1</version> </dependency>
- 导入数据
@Autowired private ApArticleMapper apArticleMapper;@Autowired private RestHighLevelClient restHighLevelClient;/*** 注意:数据量的导入,如果数据量过大,需要分页导入* @throws Exception*/ @Test public void init() throws Exception {// 1. 查询所有符合条件的文章数据List<SearchArticleVo> searchArticleVos = apArticleMapper.loadArticleList();// 2. 批量导入到es索引库BulkRequest bulkRequest = new BulkRequest("app_info_article");for (SearchArticleVo searchArticleVo : searchArticleVos) {IndexRequest indexRequest = new IndexRequest().id(searchArticleVo.getId().toString()).source(JSON.toJSONString(searchArticleVo), XContentType.JSON);// 批量添加数据bulkRequest.add(indexRequest);}restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT); }
app文章搜索
思路分析
具体实现
- 配置
没有使用数据库, 所以无需数据源的自动配置spring:autoconfigure:exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration elasticsearch:host: 192.168.174.133port: 9200
- 业务代码
@Service public class ArticleSearchServiceImpl implements ArticleSearchService {@Autowiredprivate RestHighLevelClient restHighLevelClient;/*** es文章分页检索* @param dto* @return*/@Overridepublic ResponseResult search(UserSearchDto dto) throws IOException {// 1. 检查参数if(dto == null || StringUtils.isBlank(dto.getSearchWords())){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}// 2. 设置查询条件SearchRequest searchRequest = new SearchRequest("app_info_article");SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();// 布尔查询BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();// 2.1 关键字的分词之后查询QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(dto.getSearchWords()).field("title").field("content").defaultOperator(Operator.OR);boolQueryBuilder.must(queryStringQueryBuilder);// 2.2 查询小于mindate的数据RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime().getTime());boolQueryBuilder.filter(rangeQueryBuilder);// 2.3 分页查询searchSourceBuilder.from(0);searchSourceBuilder.size(dto.getPageSize());// 2.4 按照发布时间倒序查询searchSourceBuilder.sort("publishTime", SortOrder.DESC);// 2.5 设置高亮 titleHighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("title");highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");highlightBuilder.postTags("</font>");searchSourceBuilder.highlighter(highlightBuilder);searchSourceBuilder.query(boolQueryBuilder);searchRequest.source(searchSourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);// 3. 结果封装返回List<Map> list = new ArrayList();SearchHit[] hits = searchResponse.getHits().getHits();for (SearchHit hit : hits) {String json = hit.getSourceAsString();Map map = JSON.parseObject(json, Map.class);// 处理高亮if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){Text[] titles = hit.getHighlightFields().get("title").getFragments();String title = StringUtils.join(titles);// 高亮标题map.put("h_title", title);}else{// 原始标题map.put("h_title", map.get("title"));}list.add(map);}return ResponseResult.okResult(list);} }
- 网关配置
app端网关#搜索微服务 - id: leadnews-searchuri: lb://leadnews-searchpredicates:- Path=/search/**filters:- StripPrefix= 1
新增文章创建索引
思路分析
具体实现
- Producer
SearchArticleVo需要静态地址, 所以需要在FreeMarker生成静态文章, 上传到MinIo之后再发送创建索引的消息@Service @Slf4j @Transactional public class ArticleFreemarkerServiceImpl implements ArticleFreemarkerService {...@Override@Asyncpublic void buildArticleToMinIO(ApArticle article, String content) {if(StringUtils.isNotBlank(content)) {...// kafka: 新增文章创建索引, 发送消息createArticleESIndex(article, content, path);}}@Autowiredprivate KafkaTemplate<String, String> kafkaTemplate;private void createArticleESIndex(ApArticle article, String content, String path) {SearchArticleVo searchArticleVo = new SearchArticleVo();BeanUtils.copyProperties(article, searchArticleVo);searchArticleVo.setContent(content);searchArticleVo.setStaticUrl(path);kafkaTemplate.send(ArticleConstants.ARTICLE_ES_SYNC_TOPIC, JSON.toJSONString(searchArticleVo));} }
- Listener
@Component @Slf4j public class SyncArticleListener {@Autowiredprivate RestHighLevelClient restHighLevelClient;@KafkaListener(topics = ArticleConstants.ARTICLE_ES_SYNC_TOPIC)public void onMessage(String message){if(StringUtils.isNotBlank(message)){log.info("SyncArticleListener, message={}", message);SearchArticleVo vo = JSON.parseObject(message, SearchArticleVo.class);IndexRequest indexRequest = new IndexRequest("app_info_article");indexRequest.id(vo.getId().toString());indexRequest.source(message, XContentType.JSON);try {restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);} catch (IOException e) {log.error("sync es error={}", e);throw new RuntimeException(e);}}}}
MongoDB
安装配置
# 拉取镜像
docker pull mongo
# 创建容器
docker run -di --name mongo -p 27017:27017 -v ~/data/mongodata:/data mongo
SpringBoot集成MongoDB
- 依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-mongodb</artifactId> </dependency>
- 配置
spring:data:mongodb:host: 192.168.174.133port: 27017database: leadnews-history
- 使用
public class MongoTest {@Autowiredprivate MongoTemplate mongoTemplate;//保存@Testpublic void saveTest(){ApAssociateWords apAssociateWords = new ApAssociateWords();apAssociateWords.setAssociateWords("黑马头条");apAssociateWords.setCreatedTime(new Date());mongoTemplate.save(apAssociateWords);}//查询一个@Testpublic void saveFindOne(){ApAssociateWords apAssociateWords = mongoTemplate.findById("64e46fd4f3a760442bf50527", ApAssociateWords.class);System.out.println(apAssociateWords);}//条件查询@Testpublic void testQuery(){Query query = Query.query(Criteria.where("associateWords").is("黑马头条")).with(Sort.by(Sort.Direction.DESC,"createdTime"));List<ApAssociateWords> apAssociateWordsList = mongoTemplate.find(query, ApAssociateWords.class);System.out.println(apAssociateWordsList);}@Testpublic void testDel(){mongoTemplate.remove(Query.query(Criteria.where("associateWords").is("黑马头条")),ApAssociateWords.class);} }
app文章搜索记录
保存搜索记录
思路分析
异步保存
保存数据
具体实现
public class ApUserSearchServiceImpl implements ApUserSearchService {@Autowiredprivate MongoTemplate mongoTemplate;/*** 保存用户搜索记录* @param keyword* @param userId*/@Override@Asyncpublic void insert(String keyword, Integer userId) {// 1. 查询当前用户的搜索关键词Query query = Query.query(Criteria.where("userId").is(userId).and("keyword").is(keyword));ApUserSearch apUserSearch = mongoTemplate.findOne(query, ApUserSearch.class);// 2. 存在, 更新创建时间if(apUserSearch != null){apUserSearch.setCreatedTime(new Date());mongoTemplate.save(apUserSearch);return;}// 3. 不存在, 判断当前历史记录总数量是否超过10apUserSearch = new ApUserSearch();apUserSearch.setUserId(userId);apUserSearch.setKeyword(keyword);apUserSearch.setCreatedTime(new Date());Query query1 = Query.query(Criteria.where("userId").is(userId));query1.with(Sort.by(Sort.Direction.DESC, "createTime"));List<ApUserSearch> apUserSearchList = mongoTemplate.find(query1, ApUserSearch.class);if(apUserSearchList == null || apUserSearchList.size() < 10){mongoTemplate.save(apUserSearch);}else{ApUserSearch lastUserSearch = apUserSearchList.get(apUserSearchList.size() - 1);mongoTemplate.findAndReplace(Query.query(Criteria.where("id").is(lastUserSearch.getId())), apUserSearch);}}
}
在文章搜索中调用, 因为未登录也可能有token(游客), 所以需要判断是否登录.
ApUser user = AppThreadLocalUtil.getUser();
// 异步调用, 保存搜索记录
if(user!=null && dto.getFromIndex() == 0){apUserSearchService.insert(dto.getSearchWords(), user.getId());
}
查询搜索历史
public ResponseResult findUserSearch() {// 获取当前用户ApUser user = AppThreadLocalUtil.getUser();if(user == null){return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);}// 根据用户查询数据, 根据时间排序Query query = Query.query(Criteria.where("userId").is(user.getId()));query.with(Sort.by(Sort.Direction.DESC, "createdTime"));List<ApUserSearch> apUserSearchList = mongoTemplate.find(query, ApUserSearch.class);return ResponseResult.okResult(apUserSearchList);
}
删除搜索历史
public ResponseResult delUserSearch(HistorySearchDto dto) {// 检查参数if(dto.getId() == null){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}// 获取当前用户ApUser user = AppThreadLocalUtil.getUser();if(user == null){return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);}// 删除Query query = Query.query(Criteria.where("id").is(dto.getId()).and("userId").is(user.getId()));mongoTemplate.remove(query, ApUserSearch.class);return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}
搜索联想词
public class ApAssociateWordsServiceImpl implements ApAssociateWordsService {@Autowiredprivate MongoTemplate mongoTemplate;/*** 搜索联想词* @param dto* @return*/@Overridepublic ResponseResult search(UserSearchDto dto) {// 1. 检查参数if(StringUtils.isBlank(dto.getSearchWords())){return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);}// 2. 分页检索if(dto.getPageSize() > 20){dto.setPageSize(20);}// 3. 执行查询, 模糊查询Query query = Query.query(Criteria.where("associateWords").regex(".*?\\" + dto.getSearchWords() + ".*"));query.limit(dto.getPageSize());List<ApAssociateWords> apAssociateWordsList = mongoTemplate.find(query, ApAssociateWords.class);return ResponseResult.okResult(apAssociateWordsList);}
}
来源
黑马程序员. 黑马头条
Gitee
https://gitee.com/yu-ba-ba-ba/leadnews