From db0d86ea08436abf8997ac06eed5075773fb697f Mon Sep 17 00:00:00 2001 From: vsxd Date: Thu, 12 Mar 2026 17:47:18 +0800 Subject: [PATCH] feat(search): update search documents and query services --- .../search/SearchVisibilityScope.java | 2 +- .../skillhub/search/SkillSearchDocument.java | 2 +- .../PostgresFullTextQueryService.java | 40 +++++++++---- .../PostgresSearchRebuildService.java | 57 ++++++++++--------- 4 files changed, 61 insertions(+), 40 deletions(-) diff --git a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SearchVisibilityScope.java b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SearchVisibilityScope.java index e66d9411..cb35d2c6 100644 --- a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SearchVisibilityScope.java +++ b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SearchVisibilityScope.java @@ -3,7 +3,7 @@ package com.iflytek.skillhub.search; import java.util.Set; public record SearchVisibilityScope( - Long userId, + String userId, Set memberNamespaceIds, Set adminNamespaceIds ) { diff --git a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SkillSearchDocument.java b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SkillSearchDocument.java index d00a0064..d2eaec3c 100644 --- a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SkillSearchDocument.java +++ b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/SkillSearchDocument.java @@ -4,7 +4,7 @@ public record SkillSearchDocument( Long skillId, Long namespaceId, String namespaceSlug, - Long ownerId, + String ownerId, String title, String summary, String keywords, diff --git a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresFullTextQueryService.java b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresFullTextQueryService.java index 69379b61..12727810 100644 --- a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresFullTextQueryService.java +++ b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresFullTextQueryService.java @@ -8,6 +8,7 @@ import jakarta.persistence.Query; import org.springframework.stereotype.Service; import java.util.List; +import java.util.Set; @Service public class PostgresFullTextQueryService implements SearchQueryService { @@ -20,14 +21,21 @@ public class PostgresFullTextQueryService implements SearchQueryService { @Override public SearchResult search(SearchQuery query) { + Set memberNamespaceIds = query.visibilityScope().memberNamespaceIds().isEmpty() + ? Set.of(-1L) + : query.visibilityScope().memberNamespaceIds(); + Set adminNamespaceIds = query.visibilityScope().adminNamespaceIds().isEmpty() + ? Set.of(-1L) + : query.visibilityScope().adminNamespaceIds(); + StringBuilder sql = new StringBuilder(); sql.append("SELECT skill_id FROM skill_search_document WHERE 1=1 "); // Visibility filtering sql.append("AND (visibility = 'PUBLIC' "); if (query.visibilityScope().userId() != null) { - sql.append("OR (visibility = 'NAMESPACE' AND namespace_id IN :memberNamespaceIds) "); - sql.append("OR (visibility = 'PRIVATE' AND namespace_id IN :adminNamespaceIds) "); + sql.append("OR (visibility = 'NAMESPACE_ONLY' AND namespace_id IN :memberNamespaceIds) "); + sql.append("OR (visibility = 'PRIVATE' AND (namespace_id IN :adminNamespaceIds OR owner_id = :userId)) "); } sql.append(") "); @@ -41,16 +49,18 @@ public class PostgresFullTextQueryService implements SearchQueryService { // Full-text search if (query.keyword() != null && !query.keyword().isBlank()) { - sql.append("AND to_tsvector('english', search_text) @@ plainto_tsquery('english', :keyword) "); + sql.append("AND search_vector @@ plainto_tsquery('simple', :keyword) "); } // Sorting if ("downloads".equals(query.sortBy())) { sql.append("ORDER BY (SELECT download_count FROM skill WHERE id = skill_id) DESC "); + } else if ("rating".equals(query.sortBy())) { + sql.append("ORDER BY (SELECT rating_avg FROM skill WHERE id = skill_id) DESC "); } else if ("newest".equals(query.sortBy())) { - sql.append("ORDER BY (SELECT created_at FROM skill WHERE id = skill_id) DESC "); + sql.append("ORDER BY (SELECT updated_at FROM skill WHERE id = skill_id) DESC "); } else if ("relevance".equals(query.sortBy()) && query.keyword() != null && !query.keyword().isBlank()) { - sql.append("ORDER BY ts_rank(to_tsvector('english', search_text), plainto_tsquery('english', :keyword)) DESC "); + sql.append("ORDER BY ts_rank(search_vector, plainto_tsquery('simple', :keyword)) DESC "); } else { sql.append("ORDER BY updated_at DESC "); } @@ -61,8 +71,9 @@ public class PostgresFullTextQueryService implements SearchQueryService { Query nativeQuery = entityManager.createNativeQuery(sql.toString()); if (query.visibilityScope().userId() != null) { - nativeQuery.setParameter("memberNamespaceIds", query.visibilityScope().memberNamespaceIds()); - nativeQuery.setParameter("adminNamespaceIds", query.visibilityScope().adminNamespaceIds()); + nativeQuery.setParameter("memberNamespaceIds", memberNamespaceIds); + nativeQuery.setParameter("adminNamespaceIds", adminNamespaceIds); + nativeQuery.setParameter("userId", query.visibilityScope().userId()); } if (query.namespaceId() != null) { @@ -83,14 +94,21 @@ public class PostgresFullTextQueryService implements SearchQueryService { // Count total String countSql = sql.toString().replaceFirst("SELECT skill_id", "SELECT COUNT(*)"); - countSql = countSql.substring(0, countSql.indexOf("ORDER BY")); - countSql = countSql.substring(0, countSql.indexOf("LIMIT")); + int orderByIndex = countSql.indexOf("ORDER BY"); + if (orderByIndex >= 0) { + countSql = countSql.substring(0, orderByIndex); + } + int limitIndex = countSql.indexOf("LIMIT"); + if (limitIndex >= 0) { + countSql = countSql.substring(0, limitIndex); + } Query countQuery = entityManager.createNativeQuery(countSql); if (query.visibilityScope().userId() != null) { - countQuery.setParameter("memberNamespaceIds", query.visibilityScope().memberNamespaceIds()); - countQuery.setParameter("adminNamespaceIds", query.visibilityScope().adminNamespaceIds()); + countQuery.setParameter("memberNamespaceIds", memberNamespaceIds); + countQuery.setParameter("adminNamespaceIds", adminNamespaceIds); + countQuery.setParameter("userId", query.visibilityScope().userId()); } if (query.namespaceId() != null) { diff --git a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresSearchRebuildService.java b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresSearchRebuildService.java index 9f39c01f..7d1a0b2e 100644 --- a/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresSearchRebuildService.java +++ b/server/skillhub-search/src/main/java/com/iflytek/skillhub/search/postgres/PostgresSearchRebuildService.java @@ -31,9 +31,12 @@ public class PostgresSearchRebuildService implements SearchRebuildService { @Override public void rebuildAll() { - // This would need a findAll method in SkillRepository - // For now, we'll leave it as a placeholder - throw new UnsupportedOperationException("rebuildAll not yet implemented"); + List documents = skillRepository.findAll().stream() + .filter(skill -> skill.getStatus() == SkillStatus.ACTIVE) + .map(this::toDocument) + .flatMap(Optional::stream) + .toList(); + searchIndexService.batchIndex(documents); } @Override @@ -52,30 +55,7 @@ public class PostgresSearchRebuildService implements SearchRebuildService { return; } - Skill skill = skillOpt.get(); - Optional namespaceOpt = namespaceRepository.findById(skill.getNamespaceId()); - if (namespaceOpt.isEmpty()) { - return; - } - - Namespace namespace = namespaceOpt.get(); - - String searchText = buildSearchText(skill); - - SkillSearchDocument document = new SkillSearchDocument( - skill.getId(), - skill.getNamespaceId(), - namespace.getSlug(), - skill.getOwnerId(), - skill.getDisplayName() != null ? skill.getDisplayName() : skill.getSlug(), - skill.getSummary(), - "", // keywords - could be extracted from metadata - searchText, - skill.getVisibility().name(), - skill.getStatus().name() - ); - - searchIndexService.index(document); + toDocument(skillOpt.get()).ifPresent(searchIndexService::index); } private String buildSearchText(Skill skill) { @@ -89,4 +69,27 @@ public class PostgresSearchRebuildService implements SearchRebuildService { } return sb.toString().trim(); } + + private Optional toDocument(Skill skill) { + Optional namespaceOpt = namespaceRepository.findById(skill.getNamespaceId()); + if (namespaceOpt.isEmpty()) { + return Optional.empty(); + } + + Namespace namespace = namespaceOpt.get(); + String searchText = buildSearchText(skill); + + return Optional.of(new SkillSearchDocument( + skill.getId(), + skill.getNamespaceId(), + namespace.getSlug(), + skill.getOwnerId(), + skill.getDisplayName() != null ? skill.getDisplayName() : skill.getSlug(), + skill.getSummary(), + "", + searchText, + skill.getVisibility().name(), + skill.getStatus().name() + )); + } }