텍스트 분석
“엘라스틱서치는 루씬을 기반으로 구축된 텍스트 기반 검색엔진이다.”
일반적으로 특정 단어가 포함된 문서를 찾으려면 검색어로 찾을 단어를 입력하면 될 것이라 생각하겠지만
엘라스틱 서치의 기본 분석기는 내가 생각한대로 동작하지 않았다.
별도의 설정 없이 “엘라스틱서치”나 “텍스트”라고 입력해도 위 문장이 검색되지 않는다는 것이다.
엘라스틱서치는 문서를 색인하기 전에 해당 문서의 필드 타입이 무엇인지 확인하고
텍스트 타입이면 분석기 이용해 분석한다.
텍스트가 분석되면 텀(term)으로 나눠서 형태소 형태로 분석된다.
해당 형태소는 특정 원칙에 의해 필터링되어 단어가 삭제되거나
추가, 수정되고 최종적으로 역색인된다.
1) 역색인 구조
역색인 구조는 다음과 같이 요약할 수 있다.
- 모든 문서가 가지는 단어의 고유 단어 목록
- 해당 단어가 어떤 문서에 속해 있는지에 대한 정보
- 전체 문서에 각 단어가 몇 개 들어있는지에 대한 정보
- 하나의 문서에 단어가 몇 번씩 출현했는지에 대한 빈도
색인 파일들에 들어간 토큰만 변경되어 저장되고 실제 문서의 내용은 변함없이 저장된다.
색인할 때 특정한 규칙과 흐름에 의해 텍스트를 변경하는 과정을 분석(Analyze)이라고 하고 해당 처리는 분석기(Analyzer)라는 모듈을 조합해서 이루어진다.
2) 분석기 구조
분석기는 기본적으로 다음과 같은 프로세스로 동작한다.
- 문장을 특정한 규칙에 의해 수정한다.
- 수정한 문자를 개별 토큰으로 분리한다.
- 개별 토큰을 특정한 규칙에 의해 변경한다.
- CHARACTER FILTER
문장을 분석하기 전에 입력 텍스트에 대해 특정한 단어를 변경하거나 HTML과 같은 태그를 제거하는 역할을 하는 필터
해당 내용은 텍스트를 개별 토큰화하기 전의 전처리 과정이며,
ReplaceAll()
함수처럼 패턴으로 텍스트를 변경하거나 사용자가 정의한 필터를 적용할 수 있습니다.
- TOKENIZER FILTER
TOKENIZER FILTER는 분석기를 구성할 때 하나만 사용할 수 있으며 텍스트를 어떻게 나눌 것인지를 정의한다.
한글을 분해할 때는 한글 형태소 분석기의 TOKENIZER를 사용하고,
영문을 분석할 때는 영문 형태소 분석기의 TOKENIZER를 사용하는 등 상황에 맞게 적절한 TOKENIZER를 사용하면 된다.
- TOKEN FILTER
토큰화된 단어를 하나씩 필터링해서 사용자가 원하는 토큰으로 변환한다.
Token Filter는 여러 단계가 순차적으로 이뤄지며 순서를 어떻게 지정하느냐에 따라 검색의 질이 달라질 수 있습니다.
전체적인 프로세스는 다음과 같다.
Character Filter → Tokenizer Filter → Token Filter → Index
↕️
사전
부분 검색 기능을 위한 설정
1) PostIndex
@Getter
@Setting(settingPath = "/tokenizer/setting.json")
@ToString
@Builder
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@Document(indexName = "post")
public class PostIndex {
@Id
private String id;
@Field(type = FieldType.Text, analyzer = "word_analyzer")
private String content;
@Field(type = FieldType.Date)
private ZonedDateTime createdAt;
@Field(type = FieldType.Date)
private ZonedDateTime updatedAt;
}
@Setting(settingPath = "/tokenizer/setting.json")
이 부분은 프로젝트 resource/**
경로의 파일을 찾는다.
2) /resources/tokenizer/setting.json
{
"index": {
"max_ngram_diff": "10",
"analysis": {
"analyzer": {
"word_analyzer": {
"tokenizer": "text_tokenizer",
"filter": [
"lowercase"
]
}
},
"tokenizer": {
"text_tokenizer": {
"type": "ngram",
"min_gram": 1,
"max_gram": 10,
"token_chars": [
"letter",
"digit"
]
}
}
}
}
}
해당 파일에 사용할 분석기, 필터 등을 설정하면 인덱스 생성시에 적용된다.
- letter tokenizer: 글자가 아닌 문자를 만날 때마다 텍스트를 용어로 나눈다.
- token_chars: 토큰에 포함되어야 할 문자 종류. Elasticsearch는 지정된 종류에 속하지 않은 문자를 분할한다. 기본값은 []
-
ngram: 지정된 문자 목록 중 하나를 만날 때마다 텍스트를 먼저 단어로 분리
- 예) cat → cat, ca, at, c, a, t
- filter lowercase: 소문자로 변환
3) PostIndexRepository
@Repository
public interface PostIndexRepository extends ElasticsearchRepository<PostIndex, Long> {
List<PostIndex> findByContent(String content);
@Query("{\"match\": {\"content\": \"?0\"}}")
List<PostIndex> searchByContent(String content, Pageable pageable);
}
위와 같이 jpql도 사용이 가능하다.
참고
- Elastic 가이드 북: https://esbook.kimjmin.net/06-text-analysis