diff --git a/src/main/java/com/openisle/service/SearchService.java b/src/main/java/com/openisle/service/SearchService.java index 517467058..14a692f8b 100644 --- a/src/main/java/com/openisle/service/SearchService.java +++ b/src/main/java/com/openisle/service/SearchService.java @@ -11,7 +11,9 @@ import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import java.util.List; +import java.util.LinkedHashMap; import java.util.stream.Stream; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -46,13 +48,29 @@ public class SearchService { public List globalSearch(String keyword) { Stream users = searchUsers(keyword).stream() .map(u -> new SearchResult("user", u.getId(), u.getUsername())); - Stream posts = searchPosts(keyword).stream() - .map(p -> new SearchResult("post", p.getId(), p.getTitle())); - Stream titles = searchPostsByTitle(keyword).stream() - .map(p -> new SearchResult("post_title", p.getId(), p.getTitle())); + + // Merge post results while removing duplicates between search by content + // and search by title + List mergedPosts = Stream.concat( + searchPosts(keyword).stream() + .map(p -> new SearchResult("post", p.getId(), p.getTitle())), + searchPostsByTitle(keyword).stream() + .map(p -> new SearchResult("post_title", p.getId(), p.getTitle())) + ) + .collect(java.util.stream.Collectors.toMap( + SearchResult::id, + sr -> sr, + (a, b) -> a, + java.util.LinkedHashMap::new + )) + .values() + .stream() + .toList(); + Stream comments = searchComments(keyword).stream() .map(c -> new SearchResult("comment", c.getId(), c.getContent())); - return Stream.concat(Stream.concat(Stream.concat(users, posts), titles), comments) + + return Stream.concat(Stream.concat(users, mergedPosts.stream()), comments) .toList(); } diff --git a/src/test/java/com/openisle/service/SearchServiceTest.java b/src/test/java/com/openisle/service/SearchServiceTest.java new file mode 100644 index 000000000..19379cc82 --- /dev/null +++ b/src/test/java/com/openisle/service/SearchServiceTest.java @@ -0,0 +1,47 @@ +package com.openisle.service; + +import com.openisle.model.Post; +import com.openisle.model.PostStatus; +import com.openisle.repository.CommentRepository; +import com.openisle.repository.PostRepository; +import com.openisle.repository.UserRepository; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.*; + +class SearchServiceTest { + + @Test + void globalSearchDeduplicatesPosts() { + UserRepository userRepo = Mockito.mock(UserRepository.class); + PostRepository postRepo = Mockito.mock(PostRepository.class); + CommentRepository commentRepo = Mockito.mock(CommentRepository.class); + SearchService service = new SearchService(userRepo, postRepo, commentRepo); + + Post post1 = new Post(); + post1.setId(1L); + post1.setTitle("hello"); + Post post2 = new Post(); + post2.setId(2L); + post2.setTitle("world"); + + Mockito.when(postRepo.findByTitleContainingIgnoreCaseOrContentContainingIgnoreCaseAndStatus( + Mockito.anyString(), Mockito.anyString(), Mockito.eq(PostStatus.PUBLISHED))) + .thenReturn(List.of(post1)); + Mockito.when(postRepo.findByTitleContainingIgnoreCaseAndStatus(Mockito.anyString(), Mockito.eq(PostStatus.PUBLISHED))) + .thenReturn(List.of(post1, post2)); + Mockito.when(commentRepo.findByContentContainingIgnoreCase(Mockito.anyString())) + .thenReturn(List.of()); + Mockito.when(userRepo.findByUsernameContainingIgnoreCase(Mockito.anyString())) + .thenReturn(List.of()); + + List results = service.globalSearch("h"); + + assertEquals(2, results.size()); + assertEquals(1L, results.get(0).id()); + assertEquals(2L, results.get(1).id()); + } +}