libilibi项目总结(17)Elasticsearch 的使用

这段代码定义了一个 EsSearchComponent 类,主要用于与 Elasticsearch 进行交互,执行一些基本的操作,如创建索引、保存、更新和删除文档,及搜索操作。以下是对每部分代码的详细解释:

1. 类的依赖注入

java 复制代码
@Resource
private AppConfig appConfig;

@Resource
private RestHighLevelClient restHighLevelClient;

@Resource
private UserInfoMapper userInfoMapper;
  • AppConfig:存储应用程序的配置信息,如 Elasticsearch 的索引名称。
  • RestHighLevelClient:Elasticsearch 官方提供的客户端,用于执行与 Elasticsearch 的交互(如查询、索引等)。
  • UserInfoMapper:用于操作用户信息数据库的 MyBatis 映射器,用于获取与视频相关的用户信息。

2. 检查索引是否存在

java 复制代码
private Boolean isExistIndex() throws IOException {
    GetIndexRequest getIndexRequest = new GetIndexRequest(appConfig.getEsIndexVideoName());
    return restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
}

此方法检查是否已经存在指定名称的索引。如果存在,返回 true,否则返回 false。它通过调用 RestHighLevelClientindices().exists() 方法来执行查询。

3. 创建索引

java 复制代码
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. 检查文档是否存在

java 复制代码
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. 保存文档

java 复制代码
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. 更新文档

java 复制代码
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 中。注意,lastUpdateTimecreateTime 字段不会更新。

7. 更新计数字段

java 复制代码
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. 删除文档

java 复制代码
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. 搜索文档

java 复制代码
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() 查询 videoNametags 字段,并在需要时加上高亮。查询结果通过分页封装在 PaginationResultVO 对象中。


总结:

  • 索引管理:包括创建索引和检查索引是否存在。
  • 文档操作:保存、更新、删除文档。
  • 搜索功能:支持关键词搜索、排序、分页和高亮显示。

这个组件是一个集成 Elasticsearch 的基础工具类,便于管理视频数据,并且提供了常用的搜索、更新和删除操作。

相关推荐
yang_shengy1 小时前
【JavaEE】Spring(1)
java·后端·spring·java-ee
m0_748254882 小时前
芋道源码(无遮羞布版)Spring Boot 全景指南
java·spring boot·后端
千里马学框架4 小时前
安卓java端service如何在native进程进行访问-跨进程通讯高端知识
android·java·开发语言·安卓framework开发·车机·跨进程·安卓窗口系统
程序研4 小时前
适配器模式
java·设计模式
NULL->NEXT4 小时前
Java(面向对象进阶——接口)
android·java·开发语言
步、步、为营4 小时前
解锁新技能:Windows Forms与ASP.NET API的梦幻联动
windows·后端·asp.net
雨 子4 小时前
Spring Boot 日志
java·spring boot·后端·log4j
技术的探险家5 小时前
R语言的文件操作
开发语言·后端·golang
violin-wang5 小时前
SpringBoot的Bean-高级-第三方Bean以及Bean管理
java·spring boot·后端·bean
梅羽落5 小时前
ideal jdk报错如何解决
java·经验分享·jdk·intellij-idea