这段代码定义了一个 EsSearchComponent
类,主要用于与 Elasticsearch 进行交互,执行一些基本的操作,如创建索引、保存、更新和删除文档,及搜索操作。以下是对每部分代码的详细解释:
1. 类的依赖注入
@Resource
private AppConfig appConfig;@Resource
private RestHighLevelClient restHighLevelClient;@Resource
private UserInfoMapper userInfoMapper;
AppConfig
:存储应用程序的配置信息,如 Elasticsearch 的索引名称。RestHighLevelClient
:Elasticsearch 官方提供的客户端,用于执行与 Elasticsearch 的交互(如查询、索引等)。UserInfoMapper
:用于操作用户信息数据库的 MyBatis 映射器,用于获取与视频相关的用户信息。
2. 检查索引是否存在
private Boolean isExistIndex() throws IOException {GetIndexRequest getIndexRequest = new GetIndexRequest(appConfig.getEsIndexVideoName());return restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
}
此方法检查是否已经存在指定名称的索引。如果存在,返回 true
,否则返回 false
。它通过调用 RestHighLevelClient
的 indices().exists()
方法来执行查询。
3. 创建索引
public void createIndex() {try {Boolean existIndex = isExistIndex();if (existIndex) {return;}CreateIndexRequest request = new CreateIndexRequest(appConfig.getEsIndexVideoName());request.settings("{\"analysis\": {\n" +" \"analyzer\": {\n" +" \"comma\": {\n" +" \"type\": \"pattern\",\n" +" \"pattern\": \",\"\n" +" }\n" +" }\n" +" }}", XContentType.JSON);request.mapping("{\"properties\": {\n" +" \"videoId\":{\n" +" \"type\": \"text\",\n" +" \"index\": false\n" +" },\n" +" \"userId\":{\n" +" \"type\": \"text\",\n" +" \"index\": false\n" +" },\n" +" \"videoCover\":{\n" +" \"type\": \"text\",\n" +" \"index\": false\n" +" },\n" +" \"videoName\":{\n" +" \"type\": \"text\",\n" +" \"analyzer\": \"ik_max_word\"\n" +" },\n" +" \"tags\":{\n" +" \"type\": \"text\",\n" +" \"analyzer\": \"comma\"\n" +" },\n" +" \"playCount\":{\n" +" \"type\":\"integer\",\n" +" \"index\":false\n" +" },\n" +" \"danmuCount\":{\n" +" \"type\":\"integer\",\n" +" \"index\":false\n" +" },\n" +" \"collectCount\":{\n" +" \"type\":\"integer\",\n" +" \"index\":false\n" +" },\n" +" \"createTime\":{\n" +" \"type\":\"date\",\n" +" \"format\": \"yyyy-MM-dd HH:mm:ss\",\n" +" \"index\": false\n" +" }\n" +" }}", XContentType.JSON);CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);boolean acknowledged = createIndexResponse.isAcknowledged();if (!acknowledged) {throw new BusinessException("初始化es失败");}} catch (BusinessException e) {throw e;} catch (Exception e) {log.error("初始化es失败", e);throw new BusinessException("初始化es失败");}
}
createIndex()
方法创建一个 Elasticsearch 索引(如果尚未存在)。它设置索引的配置(如分析器)和映射(字段及类型)。如果索引已存在,则直接返回。
- 设置分析器:如
comma
分析器,用于处理逗号分隔的标签。 - 映射:定义索引中每个字段的类型和属性,如
videoId
不进行索引,videoName
使用 IK 分词器进行分词。
4. 检查文档是否存在
private Boolean docExist(String id) throws IOException {GetRequest getRequest = new GetRequest(appConfig.getEsIndexVideoName(), id);GetResponse response = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);return response.isExists();
}
该方法检查 Elasticsearch 中是否存在具有指定 id
的文档。
5. 保存文档
public void saveDoc(VideoInfo videoInfo) {try {if (docExist(videoInfo.getVideoId())) {updateDoc(videoInfo);} else {VideoInfoEsDto videoInfoEsDto = CopyTools.copy(videoInfo, VideoInfoEsDto.class);videoInfoEsDto.setCollectCount(0);videoInfoEsDto.setPlayCount(0);videoInfoEsDto.setDanmuCount(0);IndexRequest request = new IndexRequest(appConfig.getEsIndexVideoName());request.id(videoInfo.getVideoId()).source(JsonUtils.convertObj2Json(videoInfoEsDto), XContentType.JSON);restHighLevelClient.index(request, RequestOptions.DEFAULT);}} catch (Exception e) {log.error("新增视频到es失败", e);throw new BusinessException("保存失败");}
}
saveDoc()
方法用于将视频信息保存到 Elasticsearch。若文档已存在(通过 docExist()
判断),则调用 updateDoc()
进行更新;否则,创建新的索引文档。它还将一些字段(如播放数、弹幕数等)设置为默认值。
6. 更新文档
private void updateDoc(VideoInfo videoInfo) {try {videoInfo.setLastUpdateTime(null);videoInfo.setCreateTime(null);Map<String, Object> dataMap = new HashMap<>();Field[] fields = videoInfo.getClass().getDeclaredFields();for (Field field : fields) {String methodName = "get" + StringTools.upperCaseFirstLetter(field.getName());Method method = videoInfo.getClass().getMethod(methodName);Object object = method.invoke(videoInfo);if (object != null && object instanceof String && !StringTools.isEmpty(object.toString()) || object != null && !(object instanceof String)) {dataMap.put(field.getName(), object);}}if (dataMap.isEmpty()) {return;}UpdateRequest updateRequest = new UpdateRequest(appConfig.getEsIndexVideoName(), videoInfo.getVideoId());updateRequest.doc(dataMap);restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);} catch (Exception e) {log.error("新增视频到es失败", e);throw new BusinessException("保存失败");}
}
updateDoc()
方法用于更新 Elasticsearch 中的现有文档。它通过反射获取 videoInfo
对象的字段,并将非空字段值更新到 Elasticsearch 中。注意,lastUpdateTime
和 createTime
字段不会更新。
7. 更新计数字段
public void updateDocCount(String videoId, String fieldName, Integer count) {try {UpdateRequest updateRequest = new UpdateRequest(appConfig.getEsIndexVideoName(), videoId);Script script = new Script(ScriptType.INLINE, "painless", "ctx._source." + fieldName + " += params.count", Collections.singletonMap("count", count));updateRequest.script(script);restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);} catch (Exception e) {log.error("更新数量到es失败", e);throw new BusinessException("保存失败");}
}
updateDocCount()
方法更新文档中指定字段的计数值。例如,增加播放数、弹幕数等字段。它使用 Elasticsearch 的脚本语言 Painless
执行字段加法。
8. 删除文档
public void delDoc(String videoId) {try {DeleteRequest deleteRequest = new DeleteRequest(appConfig.getEsIndexVideoName(), videoId);restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);} catch (Exception e) {log.error("从es删除视频失败", e);throw new BusinessException("删除视频失败");}
}
delDoc()
方法用于删除 Elasticsearch 中的文档,通过指定 videoId
删除相应的视频文档。
9. 搜索文档
public PaginationResultVO<VideoInfo> search(Boolean highlight, String keyword, Integer orderType, Integer pageNo, Integer pageSize) {try {SearchOrderTypeEnum searchOrderTypeEnum = SearchOrderTypeEnum.getByType(orderType);SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.query(QueryBuilders.multiMatchQuery(keyword, "videoName", "tags"));if (highlight) {HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("videoName"); highlightBuilder.preTags("<span class='highlight'>");highlightBuilder.postTags("</span>");searchSourceBuilder.highlighter(highlightBuilder);}searchSourceBuilder.sort("_score", SortOrder.ASC); if (orderType != null) {searchSourceBuilder.sort(searchOrderTypeEnum.getField(), SortOrder.DESC);}pageNo = pageNo == null ? 1 : pageNo;pageSize = pageSize == null ? PageSize.SIZE20.getSize() : pageSize;searchSourceBuilder.size(pageSize);searchSourceBuilder.from((pageNo - 1) * pageSize);SearchRequest searchRequest = new SearchRequest(appConfig.getEsIndexVideoName());searchRequest.source(searchSourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);SearchHits hits = searchResponse.getHits();Integer totalCount = (int) hits.getTotalHits().value;List<VideoInfo> videoInfoList = new ArrayList<>();List<String> userIdList = new ArrayList<>();for (SearchHit hit : hits.getHits()) {VideoInfo videoInfo = JsonUtils.convertJson2Obj(hit.getSourceAsString(), VideoInfo.class);if (hit.getHighlightFields().get("videoName") != null) {videoInfo.setVideoName(hit.getHighlightFields().get("videoName").fragments()[0].string());}videoInfoList.add(videoInfo);userIdList.add(videoInfo.getUserId());}UserInfoQuery userInfoQuery = new UserInfoQuery();userInfoQuery.setUserIdList(userIdList);List<UserInfo> userInfoList = userInfoMapper.selectList(userInfoQuery);Map<String, UserInfo> userInfoMap = userInfoList.stream().collect(Collectors.toMap(item -> item.getUserId(), Function.identity(), (data1, data2) -> data2));videoInfoList.forEach(item -> {UserInfo userInfo = userInfoMap.get(item.getUserId());if (userInfo != null) {item.setNickName(userInfo.getNickName());}});SimplePage page = new SimplePage(pageNo, totalCount, pageSize);PaginationResultVO<VideoInfo> result = new PaginationResultVO(totalCount, page.getPageSize(), page.getPageNo(), page.getPageTotal(), videoInfoList);return result;} catch (BusinessException e) {throw e;} catch (Exception e) {log.error("查询视频到es失败", e);throw new BusinessException("查询失败");}
}
search()
方法用于在 Elasticsearch 中进行视频的搜索,支持关键词搜索、排序、分页以及高亮显示。它通过 SearchSourceBuilder
构建查询,使用 multiMatchQuery()
查询 videoName
和 tags
字段,并在需要时加上高亮。查询结果通过分页封装在 PaginationResultVO
对象中。
总结:
- 索引管理:包括创建索引和检查索引是否存在。
- 文档操作:保存、更新、删除文档。
- 搜索功能:支持关键词搜索、排序、分页和高亮显示。
这个组件是一个集成 Elasticsearch 的基础工具类,便于管理视频数据,并且提供了常用的搜索、更新和删除操作。