From 40386557a73b044f30907d33ebeb25d48dfec1af Mon Sep 17 00:00:00 2001 From: John Niang Date: Thu, 8 Aug 2024 12:58:37 +0800 Subject: [PATCH] Support filtering search result by types, ownerNames, categoryNames and tagNames (#6442) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #### What type of PR is this? /kind improvement /area core #### What this PR does / why we need it: This PR allows users to filter search result by types, owner names, category names and tag names. #### Does this PR introduce a user-facing change? ```release-note 完善搜索引擎过滤功能 ``` --- .../run/halo/app/search/SearchOption.java | 8 ++-- .../app/search/lucene/LuceneSearchEngine.java | 40 +++++++++++++++++++ .../LuceneSearchEngineIntegrationTest.java | 6 +++ 3 files changed, 50 insertions(+), 4 deletions(-) diff --git a/api/src/main/java/run/halo/app/search/SearchOption.java b/api/src/main/java/run/halo/app/search/SearchOption.java index 2c0edf6ba6..1c84ce430c 100644 --- a/api/src/main/java/run/halo/app/search/SearchOption.java +++ b/api/src/main/java/run/halo/app/search/SearchOption.java @@ -53,22 +53,22 @@ public class SearchOption { private Boolean filterPublished; /** - * Types to include. If null, it will include all types. + * Types to include(or). If null, it will include all types. */ private List includeTypes; /** - * Owner names to include. If null, it will include all owners. + * Owner names to include(or). If null, it will include all owners. */ private List includeOwnerNames; /** - * Category names to include. If null, it will include all categories. + * Category names to include(and). If null, it will include all categories. */ private List includeCategoryNames; /** - * Tag names to include. If null, it will include all tags. + * Tag names to include(and). If null, it will include all tags. */ private List includeTagNames; diff --git a/application/src/main/java/run/halo/app/search/lucene/LuceneSearchEngine.java b/application/src/main/java/run/halo/app/search/lucene/LuceneSearchEngine.java index 42698cc421..b8e54fde78 100644 --- a/application/src/main/java/run/halo/app/search/lucene/LuceneSearchEngine.java +++ b/application/src/main/java/run/halo/app/search/lucene/LuceneSearchEngine.java @@ -17,6 +17,7 @@ import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Optional; import lombok.extern.slf4j.Slf4j; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.charfilter.HTMLStripCharFilterFactory; @@ -170,6 +171,45 @@ public SearchResult search(SearchOption option) { new TermQuery(new Term("published", filterPublished.toString())), FILTER ); } + + Optional.ofNullable(option.getIncludeTypes()) + .filter(types -> !types.isEmpty()) + .ifPresent(types -> { + var typeTerms = types.stream() + .distinct() + .map(BytesRef::new) + .toList(); + queryBuilder.add(new TermInSetQuery("type", typeTerms), FILTER); + }); + + Optional.ofNullable(option.getIncludeOwnerNames()) + .filter(ownerNames -> !ownerNames.isEmpty()) + .ifPresent(ownerNames -> { + var ownerTerms = ownerNames.stream() + .distinct() + .map(BytesRef::new) + .toList(); + queryBuilder.add(new TermInSetQuery("ownerName", ownerTerms), FILTER); + }); + + Optional.ofNullable(option.getIncludeTagNames()) + .filter(tagNames -> !tagNames.isEmpty()) + .ifPresent(tagNames -> tagNames + .stream() + .distinct() + .forEach(tagName -> + queryBuilder.add(new TermQuery(new Term("tag", tagName)), FILTER) + )); + + Optional.ofNullable(option.getIncludeCategoryNames()) + .filter(categoryNames -> !categoryNames.isEmpty()) + .ifPresent(categoryNames -> categoryNames + .stream() + .distinct() + .forEach(categoryName -> + queryBuilder.add(new TermQuery(new Term("category", categoryName)), FILTER) + )); + var finalQuery = queryBuilder.build(); var limit = option.getLimit(); diff --git a/application/src/test/java/run/halo/app/search/lucene/LuceneSearchEngineIntegrationTest.java b/application/src/test/java/run/halo/app/search/lucene/LuceneSearchEngineIntegrationTest.java index 91e4152358..9437af0fea 100644 --- a/application/src/test/java/run/halo/app/search/lucene/LuceneSearchEngineIntegrationTest.java +++ b/application/src/test/java/run/halo/app/search/lucene/LuceneSearchEngineIntegrationTest.java @@ -6,6 +6,7 @@ import static run.halo.app.core.extension.content.Post.VisibleEnum.PUBLIC; import java.time.Duration; +import java.util.List; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -122,6 +123,9 @@ void assertNoResult(int maxAttempts) { option.setKeyword("halo"); option.setHighlightPreTag(""); option.setHighlightPostTag(""); + option.setIncludeTagNames(List.of("search")); + option.setIncludeCategoryNames(List.of("halo")); + option.setIncludeOwnerNames(List.of("admin")); retryTemplate.execute(context -> { webClient.post().uri("/apis/api.halo.run/v1alpha1/indices/-/search") .bodyValue(option) @@ -218,6 +222,8 @@ void createPost(String postName) { spec.setPriority(0); spec.setSlug("/first-post"); spec.setDeleted(false); + spec.setTags(List.of("search")); + spec.setCategories(List.of("halo")); var excerpt = new Post.Excerpt(); excerpt.setRaw("first post description"); excerpt.setAutoGenerate(false);