From bfbc4ec70a50cfb6ac33c217213b0aa6efd4b5be Mon Sep 17 00:00:00 2001 From: guqing <38999863+guqing@users.noreply.github.com> Date: Thu, 22 Sep 2022 14:14:12 +0800 Subject: [PATCH] feat: add more query predicates for post list (#2436) 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 /milestone 2.0 #### What this PR does / why we need it: 文章列表新增更过的查询条件 - publishPhase 状态 - visible 可见性 - keyword 关键词 新增排序(sort)(可逆序 sortOrder) - 创建时间(默认创建时间逆序) - 发布时间 关键词过滤暂不管文章内容,否则需要查询所有文章内容判断是否包含字符串 排序暂无法支持评论数量和阅读量,这两个属性属于文章统计需要 #2430 的支撑 #### Which issue(s) this PR fixes: Fixes #2424 #### Special notes for your reviewer: /cc @halo-dev/sig-halo #### Does this PR introduce a user-facing change? ```release-note None ``` --- .../java/run/halo/app/content/PostQuery.java | 37 ++++++++- .../java/run/halo/app/content/PostSorter.java | 79 +++++++++++++++++++ .../app/content/impl/PostServiceImpl.java | 42 +++++++++- .../run/halo/app/core/extension/Post.java | 36 ++++++++- 4 files changed, 188 insertions(+), 6 deletions(-) create mode 100644 src/main/java/run/halo/app/content/PostSorter.java diff --git a/src/main/java/run/halo/app/content/PostQuery.java b/src/main/java/run/halo/app/content/PostQuery.java index 37d217688..f3c4f7c90 100644 --- a/src/main/java/run/halo/app/content/PostQuery.java +++ b/src/main/java/run/halo/app/content/PostQuery.java @@ -3,6 +3,7 @@ package run.halo.app.content; import io.swagger.v3.oas.annotations.media.Schema; import java.util.List; import java.util.Set; +import org.apache.commons.lang3.StringUtils; import org.springframework.lang.Nullable; import org.springframework.util.MultiValueMap; import run.halo.app.core.extension.Post; @@ -39,7 +40,41 @@ public class PostQuery extends IListRequest.QueryListRequest { } @Nullable - Set listToSet(List param) { + public Post.PostPhase getPublishPhase() { + String publishPhase = queryParams.getFirst("publishPhase"); + return Post.PostPhase.from(publishPhase); + } + + @Nullable + public Post.VisibleEnum getVisible() { + String visible = queryParams.getFirst("visible"); + return Post.VisibleEnum.from(visible); + } + + @Nullable + @Schema(description = "Posts filtered by keyword.") + public String getKeyword() { + return StringUtils.defaultIfBlank(queryParams.getFirst("keyword"), null); + } + + @Schema(description = "Post collation.") + public PostSorter getSort() { + String sort = queryParams.getFirst("sort"); + return PostSorter.convertFrom(sort); + } + + @Schema(description = "ascending order If it is true; otherwise, it is in descending order.") + public Boolean getSortOrder() { + String sortOrder = queryParams.getFirst("sortOrder"); + return convertBooleanOrNull(sortOrder); + } + + @Nullable + private Set listToSet(List param) { return param == null ? null : Set.copyOf(param); } + + private Boolean convertBooleanOrNull(String value) { + return StringUtils.isBlank(value) ? null : Boolean.parseBoolean(value); + } } diff --git a/src/main/java/run/halo/app/content/PostSorter.java b/src/main/java/run/halo/app/content/PostSorter.java new file mode 100644 index 000000000..957ae0d82 --- /dev/null +++ b/src/main/java/run/halo/app/content/PostSorter.java @@ -0,0 +1,79 @@ +package run.halo.app.content; + +import java.time.Instant; +import java.util.Comparator; +import java.util.Objects; +import java.util.function.Function; +import org.springframework.util.comparator.Comparators; +import run.halo.app.core.extension.Post; + +/** + * A sorter for {@link Post}. + * + * @author guqing + * @since 2.0.0 + */ +public enum PostSorter { + PUBLISH_TIME, + CREATE_TIME; + + static final Function name = post -> post.getMetadata().getName(); + + /** + * Converts {@link Comparator} from {@link PostSorter} and ascending. + * + * @param sorter a {@link PostSorter} + * @param ascending ascending if true, otherwise descending + * @return a {@link Comparator} of {@link Post} + */ + public static Comparator from(PostSorter sorter, Boolean ascending) { + if (Objects.equals(true, ascending)) { + return from(sorter); + } + return from(sorter).reversed(); + } + + /** + * Converts {@link Comparator} from {@link PostSorter}. + * + * @param sorter a {@link PostSorter} + * @return a {@link Comparator} of {@link Post} + */ + public static Comparator from(PostSorter sorter) { + if (sorter == null) { + return defaultComparator(); + } + if (CREATE_TIME.equals(sorter)) { + Function comparatorFunc = + post -> post.getMetadata().getCreationTimestamp(); + return Comparator.comparing(comparatorFunc) + .thenComparing(name); + } + + if (PUBLISH_TIME.equals(sorter)) { + Function comparatorFunc = + post -> post.getSpec().getPublishTime(); + return Comparator.comparing(comparatorFunc, Comparators.nullsLow()) + .thenComparing(name); + } + + throw new IllegalArgumentException("Unsupported sort value: " + sorter); + } + + static PostSorter convertFrom(String sort) { + for (PostSorter sorter : values()) { + if (sorter.name().equalsIgnoreCase(sort)) { + return sorter; + } + } + return null; + } + + static Comparator defaultComparator() { + Function createTime = + post -> post.getMetadata().getCreationTimestamp(); + return Comparator.comparing(createTime) + .thenComparing(name) + .reversed(); + } +} diff --git a/src/main/java/run/halo/app/content/impl/PostServiceImpl.java b/src/main/java/run/halo/app/content/impl/PostServiceImpl.java index 5ecad6930..10c759d98 100644 --- a/src/main/java/run/halo/app/content/impl/PostServiceImpl.java +++ b/src/main/java/run/halo/app/content/impl/PostServiceImpl.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Objects; import java.util.function.Function; import java.util.function.Predicate; +import org.apache.commons.lang3.StringUtils; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.security.core.context.SecurityContext; import org.springframework.stereotype.Component; @@ -22,6 +23,7 @@ import run.halo.app.content.ListedPost; import run.halo.app.content.PostQuery; import run.halo.app.content.PostRequest; import run.halo.app.content.PostService; +import run.halo.app.content.PostSorter; import run.halo.app.core.extension.Category; import run.halo.app.core.extension.Post; import run.halo.app.core.extension.Snapshot; @@ -40,8 +42,6 @@ import run.halo.app.infra.ConditionStatus; */ @Component public class PostServiceImpl implements PostService { - private static final Comparator DEFAULT_POST_COMPARATOR = - Comparator.comparing(post -> post.getMetadata().getCreationTimestamp()); private final ContentService contentService; private final ReactiveExtensionClient client; @@ -52,8 +52,10 @@ public class PostServiceImpl implements PostService { @Override public Mono> listPost(PostQuery query) { + Comparator comparator = + PostSorter.from(query.getSort(), query.getSortOrder()); return client.list(Post.class, postListPredicate(query), - DEFAULT_POST_COMPARATOR.reversed(), query.getPage(), query.getSize()) + comparator, query.getPage(), query.getSize()) .flatMap(listResult -> Flux.fromStream( listResult.get().map(this::getListedPost) ) @@ -70,6 +72,40 @@ public class PostServiceImpl implements PostService { contains(query.getCategories(), post.getSpec().getCategories()) && contains(query.getTags(), post.getSpec().getTags()) && contains(query.getContributors(), post.getStatus().getContributors()); + + String keyword = query.getKeyword(); + if (keyword != null) { + paramPredicate = paramPredicate.and(post -> { + String excerpt = post.getStatusOrDefault().getExcerpt(); + return StringUtils.containsIgnoreCase(excerpt, keyword) + || StringUtils.containsIgnoreCase(post.getSpec().getSlug(), keyword) + || StringUtils.containsIgnoreCase(post.getSpec().getTitle(), keyword); + }); + } + + Post.PostPhase publishPhase = query.getPublishPhase(); + if (publishPhase != null) { + paramPredicate = paramPredicate.and(post -> { + if (Post.PostPhase.PENDING_APPROVAL.equals(publishPhase)) { + return !post.isPublished() + && Post.PostPhase.PENDING_APPROVAL.name() + .equalsIgnoreCase(post.getStatusOrDefault().getPhase()); + } + // published + if (Post.PostPhase.PUBLISHED.equals(publishPhase)) { + return post.isPublished(); + } + // draft + return !post.isPublished(); + }); + } + + Post.VisibleEnum visible = query.getVisible(); + if (visible != null) { + paramPredicate = + paramPredicate.and(post -> visible.equals(post.getSpec().getVisible())); + } + Predicate predicate = labelAndFieldSelectorToPredicate(query.getLabelSelector(), query.getFieldSelector()); return predicate.and(paramPredicate); diff --git a/src/main/java/run/halo/app/core/extension/Post.java b/src/main/java/run/halo/app/core/extension/Post.java index 5214eaf0d..9d9b7862f 100644 --- a/src/main/java/run/halo/app/core/extension/Post.java +++ b/src/main/java/run/halo/app/core/extension/Post.java @@ -130,6 +130,8 @@ public class Post extends AbstractExtension { private Boolean inProgress; + private Integer commentsCount; + private List contributors; @JsonIgnore @@ -153,13 +155,43 @@ public class Post extends AbstractExtension { public enum PostPhase { DRAFT, PENDING_APPROVAL, - PUBLISHED + PUBLISHED; + + /** + * Convert string value to {@link PostPhase}. + * + * @param value enum value string + * @return {@link PostPhase} if found, otherwise null + */ + public static PostPhase from(String value) { + for (PostPhase phase : PostPhase.values()) { + if (phase.name().equalsIgnoreCase(value)) { + return phase; + } + } + return null; + } } public enum VisibleEnum { PUBLIC, INTERNAL, - PRIVATE + PRIVATE; + + /** + * Convert value string to {@link VisibleEnum}. + * + * @param value enum value string + * @return {@link VisibleEnum} if found, otherwise null + */ + public static VisibleEnum from(String value) { + for (VisibleEnum visible : VisibleEnum.values()) { + if (visible.name().equalsIgnoreCase(value)) { + return visible; + } + } + return null; + } } @Data