mirror of
https://github.com/nagisa77/OpenIsle.git
synced 2026-02-24 23:20:49 +08:00
feat: include categories and tags in global search
This commit is contained in:
@@ -74,7 +74,9 @@ export default {
|
|||||||
const iconMap = {
|
const iconMap = {
|
||||||
user: 'fas fa-user',
|
user: 'fas fa-user',
|
||||||
post: 'fas fa-file-alt',
|
post: 'fas fa-file-alt',
|
||||||
comment: 'fas fa-comment'
|
comment: 'fas fa-comment',
|
||||||
|
category: 'fas fa-folder',
|
||||||
|
tag: 'fas fa-hashtag'
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(selected, val => {
|
watch(selected, val => {
|
||||||
@@ -89,6 +91,10 @@ export default {
|
|||||||
if (opt.postId) {
|
if (opt.postId) {
|
||||||
router.push(`/posts/${opt.postId}#comment-${opt.id}`)
|
router.push(`/posts/${opt.postId}#comment-${opt.id}`)
|
||||||
}
|
}
|
||||||
|
} else if (opt.type === 'category') {
|
||||||
|
router.push({ path: '/', query: { category: opt.id } })
|
||||||
|
} else if (opt.type === 'tag') {
|
||||||
|
router.push({ path: '/', query: { tags: opt.id } })
|
||||||
}
|
}
|
||||||
selected.value = null
|
selected.value = null
|
||||||
keyword.value = ''
|
keyword.value = ''
|
||||||
|
|||||||
@@ -3,5 +3,8 @@ package com.openisle.repository;
|
|||||||
import com.openisle.model.Category;
|
import com.openisle.model.Category;
|
||||||
import org.springframework.data.jpa.repository.JpaRepository;
|
import org.springframework.data.jpa.repository.JpaRepository;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
public interface CategoryRepository extends JpaRepository<Category, Long> {
|
||||||
|
List<Category> findByNameContainingIgnoreCase(String keyword);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,9 +4,13 @@ import com.openisle.model.Post;
|
|||||||
import com.openisle.model.PostStatus;
|
import com.openisle.model.PostStatus;
|
||||||
import com.openisle.model.Comment;
|
import com.openisle.model.Comment;
|
||||||
import com.openisle.model.User;
|
import com.openisle.model.User;
|
||||||
|
import com.openisle.model.Category;
|
||||||
|
import com.openisle.model.Tag;
|
||||||
import com.openisle.repository.PostRepository;
|
import com.openisle.repository.PostRepository;
|
||||||
import com.openisle.repository.CommentRepository;
|
import com.openisle.repository.CommentRepository;
|
||||||
import com.openisle.repository.UserRepository;
|
import com.openisle.repository.UserRepository;
|
||||||
|
import com.openisle.repository.CategoryRepository;
|
||||||
|
import com.openisle.repository.TagRepository;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -21,6 +25,8 @@ public class SearchService {
|
|||||||
private final UserRepository userRepository;
|
private final UserRepository userRepository;
|
||||||
private final PostRepository postRepository;
|
private final PostRepository postRepository;
|
||||||
private final CommentRepository commentRepository;
|
private final CommentRepository commentRepository;
|
||||||
|
private final CategoryRepository categoryRepository;
|
||||||
|
private final TagRepository tagRepository;
|
||||||
|
|
||||||
@org.springframework.beans.factory.annotation.Value("${app.snippet-length:50}")
|
@org.springframework.beans.factory.annotation.Value("${app.snippet-length:50}")
|
||||||
private int snippetLength;
|
private int snippetLength;
|
||||||
@@ -48,6 +54,14 @@ public class SearchService {
|
|||||||
return commentRepository.findByContentContainingIgnoreCase(keyword);
|
return commentRepository.findByContentContainingIgnoreCase(keyword);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Category> searchCategories(String keyword) {
|
||||||
|
return categoryRepository.findByNameContainingIgnoreCase(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Tag> searchTags(String keyword) {
|
||||||
|
return tagRepository.findByNameContainingIgnoreCaseAndApprovedTrue(keyword);
|
||||||
|
}
|
||||||
|
|
||||||
public List<SearchResult> globalSearch(String keyword) {
|
public List<SearchResult> globalSearch(String keyword) {
|
||||||
Stream<SearchResult> users = searchUsers(keyword).stream()
|
Stream<SearchResult> users = searchUsers(keyword).stream()
|
||||||
.map(u -> new SearchResult(
|
.map(u -> new SearchResult(
|
||||||
@@ -59,6 +73,26 @@ public class SearchService {
|
|||||||
null
|
null
|
||||||
));
|
));
|
||||||
|
|
||||||
|
Stream<SearchResult> categories = searchCategories(keyword).stream()
|
||||||
|
.map(c -> new SearchResult(
|
||||||
|
"category",
|
||||||
|
c.getId(),
|
||||||
|
c.getName(),
|
||||||
|
null,
|
||||||
|
c.getDescription(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
|
Stream<SearchResult> tags = searchTags(keyword).stream()
|
||||||
|
.map(t -> new SearchResult(
|
||||||
|
"tag",
|
||||||
|
t.getId(),
|
||||||
|
t.getName(),
|
||||||
|
null,
|
||||||
|
t.getDescription(),
|
||||||
|
null
|
||||||
|
));
|
||||||
|
|
||||||
// Merge post results while removing duplicates between search by content
|
// Merge post results while removing duplicates between search by content
|
||||||
// and search by title
|
// and search by title
|
||||||
List<SearchResult> mergedPosts = Stream.concat(
|
List<SearchResult> mergedPosts = Stream.concat(
|
||||||
@@ -101,7 +135,8 @@ public class SearchService {
|
|||||||
c.getPost().getId()
|
c.getPost().getId()
|
||||||
));
|
));
|
||||||
|
|
||||||
return Stream.concat(Stream.concat(users, mergedPosts.stream()), comments)
|
return Stream.of(users, categories, tags, mergedPosts.stream(), comments)
|
||||||
|
.flatMap(s -> s)
|
||||||
.toList();
|
.toList();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,10 +61,10 @@ class SearchIntegrationTest {
|
|||||||
String admin = registerAndLoginAsAdmin("admin1", "a@a.com");
|
String admin = registerAndLoginAsAdmin("admin1", "a@a.com");
|
||||||
String user = registerAndLogin("bob_nice", "b@b.com");
|
String user = registerAndLogin("bob_nice", "b@b.com");
|
||||||
|
|
||||||
ResponseEntity<Map> catResp = postJson("/api/categories", Map.of("name", "misc", "description", "d", "icon", "i"), admin);
|
ResponseEntity<Map> catResp = postJson("/api/categories", Map.of("name", "nic-cat", "description", "d", "icon", "i"), admin);
|
||||||
Long catId = ((Number)catResp.getBody().get("id")).longValue();
|
Long catId = ((Number)catResp.getBody().get("id")).longValue();
|
||||||
|
|
||||||
ResponseEntity<Map> tagResp = postJson("/api/tags", Map.of("name", "misc", "description", "d", "icon", "i"), admin);
|
ResponseEntity<Map> tagResp = postJson("/api/tags", Map.of("name", "nic-tag", "description", "d", "icon", "i"), admin);
|
||||||
Long tagId = ((Number)tagResp.getBody().get("id")).longValue();
|
Long tagId = ((Number)tagResp.getBody().get("id")).longValue();
|
||||||
|
|
||||||
ResponseEntity<Map> postResp = postJson("/api/posts",
|
ResponseEntity<Map> postResp = postJson("/api/posts",
|
||||||
@@ -76,9 +76,11 @@ class SearchIntegrationTest {
|
|||||||
Map.of("content", "Nice article"), admin);
|
Map.of("content", "Nice article"), admin);
|
||||||
|
|
||||||
List<Map<String, Object>> results = rest.getForObject("/api/search/global?keyword=nic", List.class);
|
List<Map<String, Object>> results = rest.getForObject("/api/search/global?keyword=nic", List.class);
|
||||||
assertEquals(3, results.size());
|
assertEquals(5, results.size());
|
||||||
assertTrue(results.stream().anyMatch(m -> "user".equals(m.get("type"))));
|
assertTrue(results.stream().anyMatch(m -> "user".equals(m.get("type"))));
|
||||||
assertTrue(results.stream().anyMatch(m -> "post".equals(m.get("type"))));
|
assertTrue(results.stream().anyMatch(m -> "post".equals(m.get("type"))));
|
||||||
assertTrue(results.stream().anyMatch(m -> "comment".equals(m.get("type"))));
|
assertTrue(results.stream().anyMatch(m -> "comment".equals(m.get("type"))));
|
||||||
|
assertTrue(results.stream().anyMatch(m -> "category".equals(m.get("type"))));
|
||||||
|
assertTrue(results.stream().anyMatch(m -> "tag".equals(m.get("type"))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user