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 的基础工具类,便于管理视频数据,并且提供了常用的搜索、更新和删除操作。

相关推荐
一只叫煤球的猫8 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9659 小时前
tcp/ip 中的多路复用
后端
bobz9659 小时前
tls ingress 简单记录
后端
皮皮林55110 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友10 小时前
什么是OpenSSL
后端·安全·程序员
bobz96510 小时前
mcp 直接操作浏览器
后端
前端小张同学12 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook13 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康13 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在14 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net