-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: 사용자는 도메인 검색 자동 완성을 사용할 수 있다.
- Loading branch information
Showing
20 changed files
with
622 additions
and
10 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
package com.seong.shoutlink.domain.common; | ||
|
||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import lombok.Getter; | ||
|
||
@Getter | ||
public class Trie { | ||
|
||
@Getter | ||
static class Node { | ||
|
||
private final char c; | ||
private final Map<Character, Node> children = new HashMap<>(); | ||
private boolean isWord; | ||
|
||
public Node(char c) { | ||
this.c = c; | ||
} | ||
|
||
public void setWord(boolean word) { | ||
isWord = word; | ||
} | ||
|
||
public boolean hasChildren(char c) { | ||
return children.containsKey(c); | ||
} | ||
|
||
public boolean isWord() { | ||
return isWord; | ||
} | ||
|
||
public void addSuggestions(String word, List<String> suggestions, int count) { | ||
children.forEach((character, childNode) -> { | ||
String suggestionsWord = word + character; | ||
if (childNode.isWord()) { | ||
suggestions.add(suggestionsWord); | ||
} | ||
if (suggestions.size() >= count) { | ||
return; | ||
} | ||
childNode.addSuggestions(suggestionsWord, suggestions, count); | ||
}); | ||
} | ||
} | ||
|
||
private static final int MAX_SUGGESTION = 100; | ||
private static final int MAX_WORD_LENGTH = 35; | ||
private static final int MAX_PREFIX_LENGTH = 30; | ||
public static final int ZERO = 0; | ||
|
||
private final Node root = new Node(' '); | ||
|
||
public synchronized void insert(String word) { | ||
if(word.length() > MAX_WORD_LENGTH) { | ||
return; | ||
} | ||
|
||
Node currentNode = root; | ||
for (char c : word.toCharArray()) { | ||
Map<Character, Node> children = currentNode.getChildren(); | ||
currentNode = children.getOrDefault(c, new Node(c)); | ||
children.put(c, currentNode); | ||
} | ||
currentNode.setWord(true); | ||
} | ||
|
||
public List<String> search(String prefix, int count) { | ||
if(prefix.length() > MAX_PREFIX_LENGTH) { | ||
prefix = prefix.substring(ZERO, MAX_PREFIX_LENGTH); | ||
} | ||
Node currentNode = root; | ||
for (char c : prefix.toCharArray()) { | ||
if (!currentNode.hasChildren(c)) { | ||
return List.of(); | ||
} | ||
Map<Character, Node> children = currentNode.getChildren(); | ||
currentNode = children.get(c); | ||
} | ||
return findWords(prefix, currentNode, Math.min(MAX_SUGGESTION, count)); | ||
} | ||
|
||
private List<String> findWords(String word, Node currentNode, int count) { | ||
List<String> suggestions = new ArrayList<>(); | ||
if (currentNode.isWord()) { | ||
suggestions.add(word); | ||
} | ||
currentNode.addSuggestions(word, suggestions, count); | ||
return suggestions; | ||
} | ||
} |
29 changes: 29 additions & 0 deletions
29
src/main/java/com/seong/shoutlink/domain/domain/controller/DomainController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
package com.seong.shoutlink.domain.domain.controller; | ||
|
||
import com.seong.shoutlink.domain.domain.controller.request.FindRootDomainsRequest; | ||
import com.seong.shoutlink.domain.domain.service.DomainService; | ||
import com.seong.shoutlink.domain.domain.service.request.FindRootDomainsCommand; | ||
import com.seong.shoutlink.domain.domain.service.response.FindRootDomainsResponse; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.ModelAttribute; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@RestController | ||
@RequiredArgsConstructor | ||
@RequestMapping("/api/domains") | ||
public class DomainController { | ||
|
||
private final DomainService domainService; | ||
|
||
@GetMapping("/search") | ||
public ResponseEntity<FindRootDomainsResponse> findRootDomains( | ||
@ModelAttribute @Valid FindRootDomainsRequest request) { | ||
FindRootDomainsResponse response = domainService.findRootDomains( | ||
new FindRootDomainsCommand(request.keyword(), request.size())); | ||
return ResponseEntity.ok(response); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
...ain/java/com/seong/shoutlink/domain/domain/controller/request/FindRootDomainsRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.seong.shoutlink.domain.domain.controller.request; | ||
|
||
import jakarta.validation.constraints.NotNull; | ||
import jakarta.validation.constraints.Size; | ||
import java.util.Objects; | ||
|
||
public record FindRootDomainsRequest( | ||
@NotNull(message = "검색어는 필수입니다.") | ||
@Size(min = 1, message = "검색어는 1자 이상이어야 합니다.") | ||
String keyword, | ||
Integer size) { | ||
|
||
public FindRootDomainsRequest(String keyword, Integer size) { | ||
this.keyword = keyword; | ||
this.size = Objects.isNull(size) ? 10 : size; | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
src/main/java/com/seong/shoutlink/domain/domain/repository/DomainCacheRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.seong.shoutlink.domain.domain.repository; | ||
|
||
import java.util.List; | ||
|
||
public interface DomainCacheRepository { | ||
|
||
List<String> findRootDomains(String prefix, int count); | ||
|
||
void insert(String rootDomain); | ||
} |
5 changes: 5 additions & 0 deletions
5
src/main/java/com/seong/shoutlink/domain/domain/repository/DomainJpaRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,9 +1,14 @@ | ||
package com.seong.shoutlink.domain.domain.repository; | ||
|
||
import java.util.List; | ||
import java.util.Optional; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
import org.springframework.data.jpa.repository.Query; | ||
|
||
public interface DomainJpaRepository extends JpaRepository<DomainEntity, Long> { | ||
|
||
Optional<DomainEntity> findByRootDomain(String rootDomain); | ||
|
||
@Query("select d.rootDomain from DomainEntity d") | ||
List<String> findRootDomains(); | ||
} |
21 changes: 21 additions & 0 deletions
21
src/main/java/com/seong/shoutlink/domain/domain/repository/DomainMemoryRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.seong.shoutlink.domain.domain.repository; | ||
|
||
import com.seong.shoutlink.domain.common.Trie; | ||
import java.util.List; | ||
import org.springframework.stereotype.Repository; | ||
|
||
@Repository | ||
public class DomainMemoryRepository implements DomainCacheRepository { | ||
|
||
private final Trie trie = new Trie(); | ||
|
||
@Override | ||
public List<String> findRootDomains(String prefix, int count) { | ||
return trie.search(prefix, count); | ||
} | ||
|
||
@Override | ||
public void insert(String rootDomain) { | ||
trie.insert(rootDomain); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
src/main/java/com/seong/shoutlink/domain/domain/service/DomainRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,16 @@ | ||
package com.seong.shoutlink.domain.domain.service; | ||
|
||
import com.seong.shoutlink.domain.domain.Domain; | ||
import java.util.List; | ||
import java.util.Optional; | ||
|
||
public interface DomainRepository { | ||
|
||
Optional<Domain> findByRootDomain(String rootDomain); | ||
|
||
Domain save(Domain domain); | ||
|
||
List<String> findRootDomains(String keyword, int size); | ||
|
||
void synchronizeRootDomains(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
5 changes: 5 additions & 0 deletions
5
src/main/java/com/seong/shoutlink/domain/domain/service/request/FindRootDomainsCommand.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
package com.seong.shoutlink.domain.domain.service.request; | ||
|
||
public record FindRootDomainsCommand(String keyword, int size) { | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
...main/java/com/seong/shoutlink/domain/domain/service/response/FindRootDomainsResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.seong.shoutlink.domain.domain.service.response; | ||
|
||
import java.util.List; | ||
|
||
public record FindRootDomainsResponse(List<String> rootDomains) { | ||
|
||
public static FindRootDomainsResponse from(List<String> rootDomains) { | ||
return new FindRootDomainsResponse(rootDomains); | ||
} | ||
} |
17 changes: 17 additions & 0 deletions
17
src/main/java/com/seong/shoutlink/global/config/SchedulerConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.seong.shoutlink.global.config; | ||
|
||
import com.seong.shoutlink.domain.domain.service.DomainRepository; | ||
import com.seong.shoutlink.global.scheduler.DomainScheduler; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
import org.springframework.scheduling.annotation.EnableScheduling; | ||
|
||
@Configuration | ||
@EnableScheduling | ||
public class SchedulerConfig { | ||
|
||
@Bean | ||
public DomainScheduler domainScheduler(DomainRepository domainRepository) { | ||
return new DomainScheduler(domainRepository); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/seong/shoutlink/global/scheduler/DomainScheduler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.seong.shoutlink.global.scheduler; | ||
|
||
import com.seong.shoutlink.domain.domain.service.DomainRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.scheduling.annotation.Scheduled; | ||
|
||
@RequiredArgsConstructor | ||
public class DomainScheduler { | ||
|
||
private final DomainRepository domainRepository; | ||
|
||
@Scheduled(cron = "0 0 * * * *") | ||
public void synchronizeRootDomains() { | ||
domainRepository.synchronizeRootDomains(); | ||
} | ||
} |
Oops, something went wrong.