当前位置: 首页 > article >正文

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

这段代码定义了一个 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。它通过调用 RestHighLevelClientindices().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 中。注意,lastUpdateTimecreateTime 字段不会更新。

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


总结:

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

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


http://www.kler.cn/a/445014.html

相关文章:

  • LLaMA-Factory 单卡3080*2 deepspeed zero3 微调Qwen2.5-7B-Instruct
  • 使用xjar 对Spring-Boot JAR 包加密运行
  • uni-app商品搜索页面
  • Linux文件:动静态库制作 动态库链接原理解析
  • 深度学习推理速度优化指南
  • MFC 应用程序语言切换
  • 搭建私有链
  • C++ 引用的基本用法
  • 403 Forbidden HTTP 响应状态码
  • 大模型在研发提效方面的实践(附最佳实践资料)
  • uniapp自定义树型结构数据弹窗,给默认选中的节点,禁用所有子节点
  • electron 顶部的元素点不中,点击事件不生效
  • 模组 RG500Q入网问题分析
  • 用python写一个接口
  • 【中间件介绍及案例分析】
  • 回归预测 | MATLAB实现CNN-BiLSTM卷积神经网络结合双向长短期记忆神经网络多输入单输出回归预测
  • 探索 AnythingLLM:借助开源 AI 打造私有化智能知识库
  • 计算机工作流程
  • Linux dnf 包管理工具使用教程
  • 在linux系统的docker中安装GitLab
  • 三维引擎cesium学习经验
  • 增强现实(AR)和虚拟现实(VR)的应用
  • 使用 esrally race 测试 Elasticsearch 性能:实践指南
  • halcon单相机+机器人*眼在手外标定心得
  • Maven 插件详解
  • 将3D模型转换为Babylon格式