티스토리 뷰

DataBase/Redis

[Redis] Redis를 이용한 검색 자동완성

IT공부블로그 2019. 5. 28. 11:30
728x90
반응형

https://github.com/okihouse/spring-boot-redis-auto-complete

위 링크의 자료를 이용


AutocompleteConfig 



@Configuration

public class AutocompleteConfig {


@Autowired

private StringRedisTemplate stringRedisTemplate;


@Bean(name = {"autocompleteKeyRepository", "keyRepository"})

public AutocompleteKeyRepository keyRepository() {

// auto-complete custom key implements for test

AutocompleteKeyRepository keyRepository = new AutocompleteKeyServiceImpl(stringRedisTemplate);

return keyRepository;

}


@Bean(name = {"autocompleteRepository"})

public AutocompleteRepository autocompleteRepository(AutocompleteKeyRepository autocompleteKeyRepository) {

AutocompleteRepository autocompleteRepository = new AutocompleteServiceImpl(stringRedisTemplate, autocompleteKeyRepository);

return autocompleteRepository;

}

}


AutocompleteKeyRepository



@Repository

public interface AutocompleteKeyRepository {


String create(String word, String identifier);


double incr(String word, String identifier);


String getKey(String firstLetter);

}


AutocompleteData (VO)



public class AutocompleteData implements Comparable<AutocompleteData> {


private String value;

private int score;


public AutocompleteData(String value, int score) {

this.value = value;

this.score = score;

}


@Override

public int compareTo(AutocompleteData autocompleteData) {

return ((Integer)autocompleteData.getScore()).compareTo(this.score);

}


public String getValue() {

return value;

}


public void setValue(String value) {

this.value = value;

}


public int getScore() {

return score;

}


public void setScore(int score) {

this.score = score;

}


}


AutocompleteRepository 



@Repository

public interface AutocompleteRepository {


String DEFAULT_DELIMITER = "§";


List<AutocompleteData> complete(final String word);


List<AutocompleteData> complete(final String word, final double min, final double max, final int offset);


void add(final String word);


double incr(final String word);


void clear(final String key);


}


AutocompleteKeyServiceImpl 



@Service

public class AutocompleteKeyServiceImpl implements AutocompleteKeyRepository {


protected static final String DELIMITER = ":";

protected static final String PREFIX = "autocomplete" + DELIMITER;


private StringRedisTemplate stringRedisTemplate;

public AutocompleteKeyServiceImpl(StringRedisTemplate stringRedisTemplate) {

this.stringRedisTemplate = stringRedisTemplate;

}

@Override

public String create(final String word, final String identifier) {

Assert.hasLength(word, "Word cannot be empty or null");


String trimedWord =  word.trim();

String firstLetter = getPrefix(trimedWord);


String generatedKey = generateKey(firstLetter, trimedWord.length());

System.out.println("generatedKey : " + generatedKey);

if(!hasKey(generatedKey, trimedWord, identifier)) {

stringRedisTemplate.opsForZSet().add(generatedKey, trimedWord + identifier, 1);

stringRedisTemplate.opsForZSet().add(generatedKey, firstLetter, 0);

for (int index = 1; index < trimedWord.length() + 1; index++) {

stringRedisTemplate.opsForZSet().add(generatedKey, trimedWord.substring(0, index), 0);

}

return generatedKey;

}


@Override

public String getKey(String word) {

String firstLetter = getPrefix(word);

return generateKeyWithoutLength(firstLetter);

}


@Override

public double incr(final String word, final String identifier) {

Assert.hasLength(word, "Word cannot be empty or null");

String trimedWord =  word.trim();

String firstLetter = getPrefix(trimedWord);


String generatedKey = generateKey(firstLetter, trimedWord.length());

System.out.println("generatedKey : " + generatedKey);

if(!hasKey(generatedKey, trimedWord, identifier)) return 0;

return stringRedisTemplate.opsForZSet().incrementScore(generatedKey, trimedWord + identifier, 1);

}


private String generateKey(final String firstLetter, int length){

return generateKeyWithoutLength(firstLetter) + length;

}


private String generateKeyWithoutLength(final String firstLetter){

return PREFIX + firstLetter + DELIMITER;

}


private boolean hasKey(final String key, final String word, final String identifier){

Double exist = stringRedisTemplate.opsForZSet().score(key, word.trim() + identifier);

return exist != null;

}


private String getPrefix(final String word){

return word.substring(0, 1);

}

}


AutocompleteServiceImpl 



@Service

public class AutocompleteServiceImpl implements AutocompleteRepository {


private final double min = 0;

private final double max = 5;

private final int offset = 30;


private StringRedisTemplate stringRedisTemplate;

private AutocompleteKeyRepository keyRepository;



public AutocompleteServiceImpl(StringRedisTemplate stringRedisTemplate, AutocompleteKeyRepository keyRepository) { 

this.stringRedisTemplate = stringRedisTemplate; 

this.keyRepository = keyRepository; 

}

 


@Override

public List<AutocompleteData> complete(@RequestParam String word) {

return complete(word, min, max, offset);

}


@Override

public List<AutocompleteData> complete(String word, double min, double max, int offset) {

Assert.hasLength(word, "Word cannot be empty or null");


String trimedWord = word.trim();

int trimedWordLength = trimedWord.length();


String key = keyRepository.getKey(trimedWord);

add(trimedWord);

incr(trimedWord);

List<AutocompleteData> autocompletes = new ArrayList<>();

for (int i = trimedWordLength; i < offset; i++) {

if (autocompletes.size() == offset) break;


Set<TypedTuple<String>> rangeResultsWithScore = stringRedisTemplate

.opsForZSet()

.reverseRangeByScoreWithScores(key + i, min, max, 0, offset);

if (rangeResultsWithScore.isEmpty()) continue;


for (TypedTuple<String> typedTuple : rangeResultsWithScore) {

if (autocompletes.size() == offset) break;


String value = typedTuple.getValue();

int minLength = Math.min(value.length(), trimedWordLength);

if (!value.endsWith(DEFAULT_DELIMITER) || !value.startsWith(trimedWord.substring(0, minLength))) continue;

autocompletes.add(new AutocompleteData(value.replace(DEFAULT_DELIMITER, ""), typedTuple.getScore().intValue()));

}

}

Collections.sort(autocompletes);

return autocompletes;

}


@Override

public void add(String word) {

keyRepository.create(word, DEFAULT_DELIMITER);

}


@Override

public double incr(String word) {

return keyRepository.incr(word, DEFAULT_DELIMITER);

}


@Override

public void clear(String key) {

stringRedisTemplate.delete(key);

}

}


Controller



@Autowired

AutocompleteKeyRepository autocompleteKeyRepository;

@Autowired

AutocompleteRepository autocompleteRepository;


 @ResponseBody

    @RequestMapping("/redisComplete")

    public JSONResult redisSearch(@RequestParam String word) {

    return JSONResult.success(autocompleteRepository.complete(word));

   

    }


위 내용들을 각 service, repository, controller에 넣고 확인

728x90
반응형

'DataBase > Redis' 카테고리의 다른 글

[Redis] Redis-cli 한글 깨질때 방법  (0) 2019.05.29
[Redis] Redis 명령어  (0) 2019.05.28
[Redis] Jedis 설치 및 사용법  (0) 2019.05.28
[Redis] Spring & Redis 연동  (0) 2019.05.28
[Redis] Redis-Server 시작  (0) 2019.05.28
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함