Skip to content

Commit

Permalink
feat: 관련도 정렬을 제외한 리스트 검색 기능 구현 (#99)
Browse files Browse the repository at this point in the history
  • Loading branch information
kdkdhoho authored Feb 13, 2024
1 parent d7b53c0 commit 088bbda
Show file tree
Hide file tree
Showing 6 changed files with 197 additions and 0 deletions.
10 changes: 10 additions & 0 deletions src/main/java/com/listywave/common/util/StringUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.listywave.common.util;

import java.util.regex.Pattern;

public class StringUtils {

public static boolean match(String source, String keyword) {
return source.matches(".*" + Pattern.quote(keyword) + ".*");
}
}
28 changes: 28 additions & 0 deletions src/main/java/com/listywave/list/application/domain/Lists.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.listywave.list.application.domain;

import static com.listywave.common.util.StringUtils.match;

import com.listywave.list.application.dto.ListCreateCommand;
import com.listywave.list.application.vo.ItemComment;
import com.listywave.list.application.vo.ItemImageUrl;
Expand Down Expand Up @@ -163,6 +165,32 @@ public static Lists createList(
return lists;
}

public boolean isRelatedWith(String keyword) {
if (keyword.isBlank()) {
return true;
}
if (match(title.getValue(), keyword)) {
return true;
}
if (labels.stream().anyMatch(label -> match(label.getLabelName(), keyword))) {
return true;
}
if (items.stream().anyMatch(item -> match(item.getTitle(), keyword) || match(item.getComment(), keyword))) {
return true;
}
return false;
}

public boolean isIncluded(CategoryType category) {
if (category.equals(CategoryType.ENTIRE)) {
return true;
}
if (this.category.equals(category)) {
return true;
}
return false;
}

public void sortItems() {
this.getItems().sort(Comparator.comparing(Item::getRanking));
}
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/listywave/list/application/domain/SortType.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.listywave.list.application.domain;

public enum SortType {

NEW,
OLD,
RELATED,
COLLECTED,
;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package com.listywave.list.application.dto.response;

import com.listywave.list.application.domain.Item;
import com.listywave.list.application.domain.Lists;
import java.time.LocalDateTime;
import java.util.List;
import lombok.Builder;

@Builder
public record ListSearchResponse(
List<ListInfo> resultLists,
Long totalCount,
Long cursorId,
boolean hasNext
) {

public static ListSearchResponse of(java.util.List<Lists> lists, Long totalCount, Long cursorId, boolean hasNext) {
return ListSearchResponse.builder()
.resultLists(ListInfo.toList(lists))
.totalCount(totalCount)
.cursorId(cursorId)
.hasNext(hasNext)
.build();
}
}

@Builder
record ListInfo(
Long id,
String title,
java.util.List<ItemInfo> items,
boolean isPublic,
String backgroundColor,
LocalDateTime updatedDate,
Long ownerId,
String ownerNickname,
String ownerProfileImageUrl
) {

public static List<ListInfo> toList(java.util.List<Lists> lists) {
return lists.stream()
.map(ListInfo::of)
.toList();
}

private static ListInfo of(Lists lists) {
return ListInfo.builder()
.id(lists.getId())
.title(lists.getTitle())
.items(ItemInfo.toList(lists.getItems()))
.isPublic(lists.isPublic())
.backgroundColor(lists.getBackgroundColor())
.updatedDate(lists.getUpdatedDate())
.ownerId(lists.getUser().getId())
.ownerNickname(lists.getUser().getNickname())
.ownerProfileImageUrl(lists.getUser().getProfileImageUrl())
.build();
}
}

@Builder
record ItemInfo(
Long id,
String title
) {

public static java.util.List<ItemInfo> toList(java.util.List<Item> items) {
return items.stream()
.map(ItemInfo::of)
.toList();
}

private static ItemInfo of(Item item) {
return ItemInfo.builder()
.id(item.getId())
.title(item.getTitle())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,19 +1,25 @@
package com.listywave.list.application.service;

import static com.listywave.list.application.domain.SortType.COLLECTED;
import static com.listywave.list.application.domain.SortType.OLD;

import com.listywave.auth.application.domain.JwtManager;
import com.listywave.collaborator.application.domain.Collaborator;
import com.listywave.collaborator.repository.CollaboratorRepository;
import com.listywave.common.exception.CustomException;
import com.listywave.common.exception.ErrorCode;
import com.listywave.common.util.UserUtil;
import com.listywave.image.application.service.ImageService;
import com.listywave.list.application.domain.CategoryType;
import com.listywave.list.application.domain.Comment;
import com.listywave.list.application.domain.Item;
import com.listywave.list.application.domain.Lists;
import com.listywave.list.application.domain.SortType;
import com.listywave.list.application.dto.ListCreateCommand;
import com.listywave.list.application.dto.response.ListCreateResponse;
import com.listywave.list.application.dto.response.ListDetailResponse;
import com.listywave.list.application.dto.response.ListRecentResponse;
import com.listywave.list.application.dto.response.ListSearchResponse;
import com.listywave.list.application.dto.response.ListTrandingResponse;
import com.listywave.list.presentation.dto.request.ItemCreateRequest;
import com.listywave.list.repository.CommentRepository;
Expand Down Expand Up @@ -192,4 +198,52 @@ public ListRecentResponse getRecentLists(String accessToken) {
private boolean isSignedIn(String accessToken) {
return !accessToken.isBlank();
}

// TODO: 관련도 순 추가 (List 일급 컬렉션 만들어서 Scoring 하는 방식)
// TODO: 리팩터링
public ListSearchResponse search(String keyword, SortType sortType, CategoryType category, int size, Long cursorId) {
List<Lists> all = listRepository.findAll();

List<Lists> filtered = all.stream()
.filter(list -> list.isIncluded(category))
.filter(list -> list.isRelatedWith(keyword))
.sorted((list, other) -> {
if (sortType.equals(OLD)) {
return list.getUpdatedDate().compareTo(other.getUpdatedDate());
}
if (sortType.equals(COLLECTED)) {
return -(list.getCollectCount() - other.getCollectCount());
}
return -(list.getUpdatedDate().compareTo(other.getUpdatedDate()));
})
.toList();

List<Lists> result;
if (cursorId == 0L) {
if (filtered.size() >= size) {
return ListSearchResponse.of(filtered.subList(0, size), (long) filtered.size(), filtered.get(size - 1).getId(), true);
}
return ListSearchResponse.of(filtered, (long) filtered.size(), filtered.get(filtered.size() - 1).getId(), false);
} else {
Lists cursorList = listRepository.getById(cursorId);

int cursorIndex = filtered.indexOf(cursorList);
int startIndex = cursorIndex + 1;
int endIndex = cursorIndex + 1 + size;

if (endIndex >= filtered.size()) {
endIndex = filtered.size() - 1;
}

result = filtered.subList(startIndex, endIndex + 1);
}

int totalCount = filtered.size();
if (result.size() < size) {
return ListSearchResponse.of(result, (long) totalCount, result.get(result.size() - 1).getId(), false);
}
boolean hasNext = result.size() > size;
result = result.subList(0, size);
return ListSearchResponse.of(result, (long) totalCount, result.get(result.size() - 1).getId(), hasNext);
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.listywave.list.presentation.controller;

import com.listywave.list.application.domain.CategoryType;
import com.listywave.list.application.domain.SortType;
import com.listywave.list.application.dto.ListCreateCommand;
import com.listywave.list.application.dto.response.ListCreateResponse;
import com.listywave.list.application.dto.response.ListDetailResponse;
import com.listywave.list.application.dto.response.ListRecentResponse;
import com.listywave.list.application.dto.response.ListSearchResponse;
import com.listywave.list.application.dto.response.ListTrandingResponse;
import com.listywave.list.application.service.ListService;
import com.listywave.list.presentation.dto.request.ListCreateRequest;
Expand All @@ -18,6 +21,7 @@
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
Expand Down Expand Up @@ -67,4 +71,16 @@ ResponseEntity<ListRecentResponse> getRecentLists(
ListRecentResponse recentLists = listService.getRecentLists(accessToken);
return ResponseEntity.ok(recentLists);
}

@GetMapping("/search")
ResponseEntity<ListSearchResponse> search(
@RequestParam(value = "keyword", defaultValue = "") String keyword,
@RequestParam(value = "sort", defaultValue = "new") SortType sort,
@RequestParam(value = "category", defaultValue = "entire") CategoryType category,
@RequestParam(value = "size", defaultValue = "5") int size,
@RequestParam(value = "cursorId", defaultValue = "0") Long cursorId
) {
ListSearchResponse response = listService.search(keyword, sort, category, size, cursorId);
return ResponseEntity.ok(response);
}
}

0 comments on commit 088bbda

Please sign in to comment.