From 13355828274107655f2a450243cd7938ad520728 Mon Sep 17 00:00:00 2001 From: Hanserwei <2628273921@qq.com> Date: Sun, 2 Nov 2025 14:13:10 +0800 Subject: [PATCH] =?UTF-8?q?fix(search):=E4=BF=AE=E5=A4=8D=E7=94=A8?= =?UTF-8?q?=E6=88=B7=E6=90=9C=E7=B4=A2=E6=9C=8D=E5=8A=A1=E4=B8=AD=E7=9A=84?= =?UTF-8?q?=E7=A9=BA=E6=8C=87=E9=92=88=E5=BC=82=E5=B8=B8=E5=92=8C=E9=AB=98?= =?UTF-8?q?=E4=BA=AE=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复了likeTotal字段为null时的空指针异常- 重构用户搜索逻辑,优化查询构建和响应处理 - 移除了过时的Guava Lists依赖,使用ArrayList替代 - 改进了高亮字段处理逻辑,确保正确提取高亮内容 - 更新异常处理类型从Exception到具体的IOException-优化代码结构,添加注释分段标识提高可读性- 调整粉丝总数格式化逻辑,增强空值处理能力 --- .../search/service/impl/NoteServiceImpl.java | 2 +- .../search/service/impl/UserServiceImpl.java | 165 +++++++----------- 2 files changed, 63 insertions(+), 104 deletions(-) diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/NoteServiceImpl.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/NoteServiceImpl.java index 877c561..f8836cc 100644 --- a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/NoteServiceImpl.java +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/NoteServiceImpl.java @@ -249,7 +249,7 @@ public class NoteServiceImpl implements NoteService { .nickname(nickname) .updateTime(updateTime) .highlightTitle(highlightedTitle) - .likeTotal(NumberUtils.formatNumberString(Long.parseLong(likeTotal))) + .likeTotal(likeTotal == null ? "0" : NumberUtils.formatNumberString(Long.parseLong(likeTotal))) .build()); } diff --git a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java index 7b2f796..6b19069 100644 --- a/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java +++ b/han-note-search/src/main/java/com/hanserwei/hannote/search/service/impl/UserServiceImpl.java @@ -1,14 +1,16 @@ package com.hanserwei.hannote.search.service.impl; import co.elastic.clients.elasticsearch.ElasticsearchClient; +import co.elastic.clients.elasticsearch._types.SortOptions; import co.elastic.clients.elasticsearch._types.SortOrder; +import co.elastic.clients.elasticsearch._types.query_dsl.Query; import co.elastic.clients.elasticsearch._types.query_dsl.TextQueryType; import co.elastic.clients.elasticsearch.core.SearchRequest; import co.elastic.clients.elasticsearch.core.SearchResponse; +import co.elastic.clients.elasticsearch.core.search.Highlight; import co.elastic.clients.elasticsearch.core.search.HighlightField; import co.elastic.clients.elasticsearch.core.search.Hit; import co.elastic.clients.util.NamedValue; -import com.google.common.collect.Lists; import com.hanserwei.framework.common.response.PageResponse; import com.hanserwei.framework.common.utils.NumberUtils; import com.hanserwei.hannote.search.index.UserIndex; @@ -19,6 +21,8 @@ import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.io.IOException; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -31,128 +35,83 @@ public class UserServiceImpl implements UserService { @Override public PageResponse searchUser(SearchUserReqVO searchUserReqVO) { - // 查询关键词 + // --- 2. 获取请求参数 --- String keyword = searchUserReqVO.getKeyword(); - // 当前页码 Integer pageNo = searchUserReqVO.getPageNo(); - int pageSize = 10; // 每页展示数据量 - int from = (pageNo - 1) * pageSize; // 偏移量 - - HighlightField nicknameHighlight = HighlightField.of(hf -> hf + // --- 3. 设置分页 --- + int pageSize = 10; // 假设每页大小为10 + int from = (pageNo - 1) * pageSize; + // --- 4. 构建 multi_match 查询 --- + Query multiMatchQuery = Query.of(q -> q + .multiMatch(m -> m + .query(keyword) + .type(TextQueryType.PhrasePrefix) + .fields(UserIndex.FIELD_USER_NICKNAME, UserIndex.FIELD_USER_HAN_NOTE_ID) + ) + ); + // --- 5. 构建排序 --- + SortOptions sortOptions = SortOptions.of(so -> so + .field(f -> f + .field(UserIndex.FIELD_USER_FANS_TOTAL) + .order(SortOrder.Desc))); + // --- 6. 构建高亮 --- + HighlightField nikeNameHighlight = HighlightField.of(hf -> hf .preTags("") .postTags("") ); - - - SearchRequest searchRequest = SearchRequest.of(r -> r + Highlight highlight = Highlight.of(h -> h + .fields(NamedValue.of(UserIndex.FIELD_USER_NICKNAME, nikeNameHighlight))); + // --- 7. 构建 SearchRequest --- + SearchRequest searchRequest = SearchRequest.of(s -> s .index(UserIndex.NAME) - - // 1. 构建 Query: multiMatchQuery (RHL 风格的匹配) - .query(q -> q - .multiMatch(m -> m - .query(keyword) - .fields(UserIndex.FIELD_USER_NICKNAME, UserIndex.FIELD_USER_HAN_NOTE_ID) - // 默认使用 MatchQuery 行为,如果要模糊匹配,请添加 .fuzziness("AUTO") - .type(TextQueryType.PhrasePrefix) - ) - ) - - // 2. 构建 Sort - .sort(s -> s - .field(f -> f - .field(UserIndex.FIELD_USER_FANS_TOTAL) - .order(SortOrder.Desc) - ) - ) - - .highlight(h -> h.fields(NamedValue.of(UserIndex.FIELD_USER_NICKNAME, nicknameHighlight))) - - // 3. 分页 from 和 size + .query(multiMatchQuery) + .sort(sortOptions) + .highlight(highlight) .from(from) - .size(pageSize) - ); - - - // 返参 VO 集合 - List searchUserRspVOS = Lists.newArrayList(); - // 总文档数,默认为 0 + .size(pageSize)); + // --- 8. 执行查询和解析响应 --- + List searchUserRspVOS = new ArrayList<>(); long total = 0; - try { - log.info("==> SearchRequest: {}", searchRequest.toString()); - - // 执行查询请求 + log.info("==> searchRequest: {}", searchRequest); SearchResponse searchResponse = client.search(searchRequest, SearchUserRspVO.class); - - // 处理搜索结果 - List> hits = searchResponse.hits().hits(); + // 8.2. 处理响应 if (searchResponse.hits().total() != null) { total = searchResponse.hits().total().value(); } - - searchUserRspVOS = Lists.newArrayList(); - + log.info("==> 命中文档总数, hits: {}", total); + List> hits = searchResponse.hits().hits(); for (Hit hit : hits) { - // 1. 获取原始文档数据 (source) + // 获取source SearchUserRspVO source = hit.source(); - - // 2. 获取高亮数据 (highlight) - Map> highlights = hit.highlight(); - + // 8.3. 获取高亮字段 + String highlightNickname = null; + Map> highlightFiled = hit.highlight(); + if (highlightFiled.containsKey(UserIndex.FIELD_USER_NICKNAME)) { + highlightNickname = highlightFiled.get(UserIndex.FIELD_USER_NICKNAME).getFirst(); + } if (source != null) { - // 3. 调用辅助方法合并数据和高亮 - SearchUserRspVO searchUserRspVO = mergeHitToRspVO(source, highlights); - searchUserRspVOS.add(searchUserRspVO); + Long userId = source.getUserId(); + String nickname = source.getNickname(); + String avatar = source.getAvatar(); + String hanNoteId = source.getHanNoteId(); + Integer noteTotal = source.getNoteTotal(); + String fansTotal = source.getFansTotal(); + searchUserRspVOS.add(SearchUserRspVO.builder() + .userId(userId) + .nickname(nickname) + .highlightNickname(highlightNickname) + .avatar(avatar) + .hanNoteId(hanNoteId) + .noteTotal(noteTotal) + .fansTotal(fansTotal == null ? "0" : NumberUtils.formatNumberString(Long.parseLong(fansTotal))) + .build()); } } - - } catch (Exception e) { - log.error("==> 查询 Elasticsearch 异常: ", e); + } catch (IOException e) { + log.error("==> search error: {}", e.getMessage()); } - return PageResponse.success(searchUserRspVOS, pageNo, total); } - - /** - * 将原始文档和高亮数据合并到 SearchUserRspVO - * - * @param source 原始文档数据 (已自动反序列化) - * @param highlights 高亮数据 Map - * @return SearchUserRspVO - */ - private SearchUserRspVO mergeHitToRspVO(SearchUserRspVO source, Map> highlights) { - if (source == null) { - return null; - } - - // 1. 复制原始文档字段 (假设 SearchUserRspVO 使用 Lombok @Data 或 Builder) - SearchUserRspVO searchUserRspVO = SearchUserRspVO.builder() - .userId(source.getUserId()) - .nickname(source.getNickname()) - .avatar(source.getAvatar()) - .hanNoteId(source.getHanNoteId()) // 字段名应与您的 VO 保持一致 - .noteTotal(source.getNoteTotal()) - .build(); - if (source.getFansTotal() != null) { - searchUserRspVO.setFansTotal(NumberUtils.formatNumberString(Long.parseLong(source.getFansTotal()))); - } - - // 2. ⭐️ 核心逻辑:处理并设置高亮字段 - if (highlights != null) { - // 尝试从 highlights Map 中获取 nickname 字段的高亮结果 - List nicknameHighlights = highlights.get(UserIndex.FIELD_USER_NICKNAME); - - if (nicknameHighlights != null && !nicknameHighlights.isEmpty()) { - searchUserRspVO.setHighlightNickname(nicknameHighlights.getFirst()); - } - } - - // 3. 如果高亮字段为空,默认使用原始 nickname - if (searchUserRspVO.getHighlightNickname() == null) { - searchUserRspVO.setHighlightNickname(source.getNickname()); - } - - return searchUserRspVO; - } }