사내에서 제안 검색어를 사용하는 방법에 대해 작성해 보겠습니다.
우선 플러그인으로는 analysis-icu와 QueryDsl의 Suggest를 사용합니다.
analysis-icu 분석 플러그인은 ICU(Interational Components for Unicode) 라이브러리를 사용하여 아시아 언어에 대한 향상된 분석, 유니코드 정규화, 유니코드 인식 대소문자 접기, 대조 지원 및 음역을 포함하여 확장된 유니코드 지원을 추가합니다.
analysis-icu 분석 플러그인은 문자 필터, 토크나이저, 토큰 필터를 제공하는데 그중 문자 필터인 icu_normalizer를 사용하겠습니다.
플러그인 설정
name
- 정규화의 종류를 지정.
- nfc(normalization Form C)는 문자열을 정규화할 때 모든 조합 문자를 조합 문자로 변환하여 표준 형식을 만듦.
mode
- Unicode 정규화 모드 지정.
- compose: 하나의 문자로 합치는 방식
- decompose: 문자열을 구성하는 하위 문자로 분해하는 방식
type
- 문자 필터의 종류를 지정.
- icu_normalizer는 Unicode 문자열을 정규화하는 데 사용.
Suggest 쿼리는 제공된 텍스트를 기반으로 유사해 보이는 용어를 제한하는 Query Dsl 입니다.
POST _search
{
"suggest": {
"text" : "tring out Elasticsearch",
"my-suggest-1" : {
"term" : {
"field" : "message"
}
},
"my-suggest-2" : {
"term" : {
"field" : "user"
}
}
}
}
text: 제안 텍스트.
suggest_mode
- missing: 가장 유사한 검색 제안을 제공. 입력된 검색어와 가장 일치점을 가지는 검색어를 추천함. (default)
- popular: 가장 인기 있는 검색어나 검색어 구성요소를 추천함.
- always: 항상 검색어를 제안함. (사용자가 입력한 키워드도 포함됨)
sort: 제안 텍스트 용어별로 제안을 정렬하는 방법을 정의.
- score: 먼저 점수를 기준으로 정렬한 다음 문서 빈도, 용어 자체를 기준으로 정렬.
- frequency: 문서 빈도를 기준으로 정렬한 다음 유사성 점수, 용어 자체를 기준으로 정렬.
string_distance: 제안된 용어가 얼마나 유사한지 비교하기 위해 사용된 문자열 거리 구현 알고리즘.
- jaro_winkler: Jaro-Winkler 알고리즘을 기반으로 한 문자열 거리 알고리즘.
실습
Elasticsearch 엔진에 analysis-icu 플러그인을 설치 방법
sudo bin/elasticsearch-plugin install analysis-icu
엘라스틱서치 클러스터 docker-compose.yml
플러그인 설치 확인
GET _cat/plugins
es1 analysis-icu 7.8.1
es1 analysis-nori 7.8.1
es2 analysis-icu 7.8.1
es2 analysis-nori 7.8.1
제안 검색어 인덱스 생성
위에서 설명했던 icu_normalizer 문자 필터를 가지고 icu_analyzer를 만들어 keyword.icu 필드에 분석기로 넣었습니다.
PUT propose-keyword
{
"mappings": {
"properties": {
"keyword": {
"type": "text",
"fields": {
"icu": {
"type": "text",
"analyzer": "icu_analyzer"
}
}
}
}
},
"settings": {
"index": {
"analysis": {
"analyzer": {
"icu_analyzer": {
"filter": [
"lowercase"
],
"char_filter": [
"icu_normalizer"
],
"tokenizer": "standard"
}
},
"char_filter": {
"icu_normalizer": {
"mode": "decompose",
"name": "nfc",
"type": "icu_normalizer"
}
}
}
}
}
}
Suggest 쿼리 사용
my-suggestion이라는 제안을 만들고 노트븍 키워드를 jaro_winkler 알고리즘 방식으로 유사한 키워드를 찾겠습니다.
POST propose-keyword/_search
{
"suggest" : {
"my-suggestion" : {
"text" : "노트븍",
"term" : {
"field" : "keyword.icu",
"string_distance" : "jaro_winkler"
}
}
}
}
{
"took" : 4,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 0,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"suggest" : {
"my-suggestion" : [
{
"text" : "노트븍",
"offset" : 0,
"length" : 3,
"options" : [
{
"text" : "노트북",
"score" : 0.952381,
"freq" : 1
}
]
}
]
}
}
Suggest 호출 클라이언트와 결과 값입니다.
public class SuggestExample {
public static void main(String[] args) throws IOException {
// Elasticsearch 클라이언트 초기화
RestClientBuilder restClientBuilder = RestClient.builder(new HttpHost("localhost", 9200, "http"))
.setHttpClientConfigCallback(httpClientBuilder -> {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY,
new UsernamePasswordCredentials("elastic", "changeme"));
return httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
});
try (RestHighLevelClient client = new RestHighLevelClient(restClientBuilder)) {
// SearchRequest 초기화
SearchRequest searchRequest = new SearchRequest("propose-keyword");
// Suggest 쿼리 설정
SuggestBuilder suggestBuilder = new SuggestBuilder();
// SuggestionBuilder를 사용하여 Suggest 쿼리 작성
SuggestionBuilder<?> completionSuggestionBuilder = SuggestBuilders.termSuggestion("keyword.icu")
.text("노투븍")
.size(5); // 추천 결과 개수 설정
// SuggestBuilder에 SuggestionBuilder 추가
suggestBuilder.addSuggestion("my-suggestion", completionSuggestionBuilder);
// SearchRequest에 SuggestBuilder 설정
searchRequest.source(new SearchSourceBuilder().suggest(suggestBuilder));
// Elasticsearch에 요청 보내기
SearchResponse searchResponse = client.search(searchRequest, RequestOptions.DEFAULT);
// 결과 출력
Suggest suggest = searchResponse.getSuggest();
List<? extends Option> options = suggest.getSuggestion("my-suggestion").getEntries().get(0).getOptions();
for (Option option : options) {
String text = option.getText().string();
System.out.println("Suggested Text: " + text);
}
}
}
}
// 결과값
Suggested Text: 노트북
참고
- https://www.elastic.co/guide/en/elasticsearch/client/net-api/7.17/writing-analyzers.htm
- https://www.elastic.co/guide/en/elasticsearch/reference/current/search-suggesters.html
- https://www.elastic.co/guide/en/elasticsearch/plugins/current/analysis-icu-normalization-charfilter.html
'ELK' 카테고리의 다른 글
Elasticsearch 커스텀 분석기 만들기 (이슈) (0) | 2023.09.20 |
---|---|
Elasticsearch 커스텀 분석기 만들기 (0) | 2023.09.19 |
es_rejected_execution_exception 처리하기 (1) | 2023.09.12 |
Elasticsearch 롤링 배포 (0) | 2023.06.23 |
Elasticsearch Reindex 성능 개선 (0) | 2023.06.12 |