ES기초(1) NORI 알아보기: 데이터 색인과 텍스트 분석
엘라스틱 서치, 형태소 분석기인 NORI에 대해 알아보기전에 ES와 조금더 친해지기 위한 포스팅을 하려한다.
이번장에서는 Elasticsearch 가 검색을 위해 텍스트 데이터를 어떻게 처리하고 데이터를 색인 할 때 Elasticsearch에서 어떤 과정이 이루어지는지 알아본다.
0. ES 데이터 가공 작업
Elasticsearch 가 풀텍스트 검색을 하기 위해 데이터를 검색에 맞게 가공하는 작업이 필요하다. Elasticsearch는 데이터를 __저장하는 과정__에서 이 작업을 처리한다.
1. 역 인덱스 Inverted Index
일반적인 RDB 들은, “한승우는 밥을 먹었다.”, “한승우의 블로그” 라는 문장들을 저장할때, 문장들 각각 그대로 테이블에 저장을 한다. 만약 위 문장들에서 “한승우”가 포함된 문장을 찾고 싶다면, DB는 모든 로우들을 한줄씩 찾아 내려가면서 “한승우”가 있으면 가져오고 없으면 넘어가는 식의로 검색을 할것이다.
일반적인 RDBMS는 데이터가 늘어날수록 검색해야 할 대상이 늘어나 시간이 오래걸리고, row안의 모든 내용을 읽어야 하기 때문에(like) 기본적으로 속도가 굉장히 느리다. 하지만 Elasticsearch는 데이터를 저장할때 역 인덱스 구조를 사용하여 저장하여 위와 같은 검색에서 굉장히 유리하다.
역 인덱스는 책의 맨 뒤에 있는 주요 키워드에 대한 내용이 몇 페이지에 있는지 알수 있는 찾아보기 페이지 로 볼수 있다. Elasticsearch에서는 각 키워드를 __term__이라고 부르고, 이렇게 역 인덱스가 있다면, 해당 키워드가 있는 도큐면트 id를 바로 얻는것이 가능해진다.
Elasticsearch는 데이터가 늘어나도 찾아가야 할 행이 늘어나는 것이 아닌 역 인덱스가 가리키는 id의 값만 추가되기 때문에 속도의 저하가 없다.
이러한 저장과정으로 Elasticsearch가 데이터를 입력할 때는 __저장이 아닌 색인__이라고 한다.
2. 텍스트 분석 - Text Analysis
Elasticsearch는 문자열 필드가 저장될 때 데이터에서 검색어 토큰을 저장하기 위해 여러 단계의 처리 과정을 거친다.
- 이 전체 과정을 텍스트 분석
- 이 과정을 처리하는 기능을 analyzer 라고 한다.
Elasticsearch의 Analyzer는
- 0~3개의 캐릭터 필터
- 1개의 토크나이저
-
0~n개의 토큰 필터 로 구성된다.
- 캐릭터 필터 : 텍스트 데이터가 입력되면, 전체 문장에서 특정 문자를 대치하거나 제거하는 작업
- 토크나이저 : 문장에 속한 단어들을 텀 단위로 하나씩 분리 처리하는 과정, __반드시 1개__만 적용 default tokenizer는 whitespace 토크나이저
- 토큰 필터 : 분리된 텀들을 하나씩 가공하는 과정,
토큰 필터 종류 는 굉장히 많다.
- 이러한 토큰 필터로 텀들의 key값이 같아지면, 서로 id들이 병합이 되어 관리가 된다.
- 동의어 필터 동작 원리 synonym 토큰 필터를 사용하여 “한숭우” 텀에 동의어로 “한승유” 를 지정하면 한승유를 검색하였을때 같은 의미인 한승우 를 포함하는 도큐먼트가 검색되기도 한다. (그런데 이런 오타에 대한 동의어 생성은 생각해보고 해야될수 있음) AWS 와 Amazon 을 동의어로 놓아 amazon 을 검색해도 aws를 검색할수 있게 된다.
3. Analyzer
Elasticsearch 에서는 분석된 문장을 ___analyze__API 를 이용하여 확인이 가능하다. 토크나이저는 tokenizer 에 하나만 적용하므로 바로 입력, 토큰 필터는 filter 항목의 값으로 여러개를 적용할수 있기에 [] 배열 형식으로 입력하면 된다.
GET _analyze
{
"text": "The quick brown fox jumps over the lazy dog",
"tokenizer": "whitespace",
"filter": [
"lowercase",
"stop",
"snowball"
]
}
필터 적용에는 순서또한 중요하다. 위의 결과에서는 lowercase filter가 먼저 적용되어 있기 때문에, the 가 불용어로 간주되어 텀으로 색인되지 않는다.
하지만 위의 순서를 변경한다면, the는 stop 토큰필터 처리시에는 “The” 로 불용어로 간주 되지 않아 그대로 남게 된다.
애널라이저는 _analyzer API analyzer 항목으로 적용해서 사용이 가능하다. 이 애널라이저는 사용자 정의 애널라이저와 Elasticsearch에서 사전에 정의되어 있는 애널라이저를 선택하여 사용하는것이 가능하다.
위의 예제는 Elasticsearch가 제공하는 snowball 애널라이저이다. 저걸 이 snowball 애널라이저를 사용하여 쓰는방법은 아래와 같다. GET _analyze { “text”: “The quick brown fox jumps over the lazy dog”, “analyzer”: “snowball” }
인덱스의 매핑설정에 snowball 애널라이저를 적용하고 “The quick brown fox jumps over the lazy dog” 색인(저장) 하면 위의 예제의 결과의 단어들이 텀으로 저장이 된다. match 쿼리로 검색을 수행하면 입력한 검색어도 앞에서 적용한 snowball 애널라이저를 똑같이 거치한다. 즉 Jump , jumps 로 검색을 하여도 elasticsearch가 jump로 변경하여 검색을 해준다는 것이다.
인덱스에 애널라이저는 아래와 같이 설정할 수 있다.
PUT my_index2
{
"mappings": {
"properties": {
"message": {
"type": "text",
"analyzer": "snowball"
}
}
}
}
Term 쿼리
Elasticsearch에서 제공하는 쿼리중 term 이라는 쿼리가 있다. match 쿼리와 문법은 유사하지만 term 쿼리는 입력한 검색어는 애널라이저를 적용하지 않고 검색어 그대로 일치하는 텀을 찾는 쿼리이다.
사용자 정의 애널라이저
_analyze API 로 애널라이저, 토크나이저, 토큰필터 테스트가 가능하지만 실제 인덱스에 저장 되는 데이터의 처리 설정은 __애널라이저__만 적용할 수 있다.
인덱스 매핑에 애널라이저를 적용 할 때 보통은 이미 정의되어 제공하는 애널라이저 보다는 토크나이저, 토큰 필터 등을 조합하여 만든 사용자 정의 애널라이저를 주로 사용하기 때문에 중요하다
사용자 정의 애널라이저는 인덱스 settings의
"index" : { "analysis" :
부분에 정의한다.
생성한 다음에는 해당 인덱스에서 GET또는 POST <인덱스명>/_analyze 명령으로 사용이 가능하다.인덱스명>
아래는 my_custom_index 안에 whitespace 토크나이저, 그리고 토큰필터들을 추가하여 사용하는 my_custom_analyzer 라는 애널라이저를 추가하는 예제이다.
PUT my_custom_index
{
"settings" : {
"index" : {
"analysis" : {
"analyzer" : {
"my_custom_analyzer" : {
"type" : "custom",
"tokenizer" : "whitespace",
"filter" : [
"lowercase",
"stop",
"snowball"
]
}
}
}
}
}
}
이제 새로 생성한 my_custom_index 내에서 my_custom_analyzer를 사용이 가능해졌다.
_analyzer API로 my_custom_analyzer 사용
GET my_custom_index/_analyze
{
"analyzer" : "my_custom_analyzer",
"text" : [
"The quick brown fox jumps over the lazy dog"
]
}
사용자 정의 토큰 필터
토크나이저, 토큰필터의 경우에도 옵션을 지정하는 경우, 사용자 정의 토크나이저, 토큰필터를 만들어 추가해야 한다.
매핑에 사용자 정의 애널라이저 적용
애널라이저를 실제 인덱스에 입력할 데이터에 적용하려면 settings 부분에서 만든 애널라이저를 mappings 의 text 필드 별로 지정해야 한다.
앞에서 만든 my_custom_analyzer를 message필드에 적용하는 방법은 아래와 같다..
PUT my_custom_index
{
"settings" : {
"index" : {
"analysis" : {
"analyzer" : {
"my_custom_analyzer" : {
"type" : "custom",
"tokenizer" : "whitespace",
"filter" : [
"lowercase",
"stop",
"snowball"
]
}
}
}
},
"mappings" : {
"properties" : {
"message" : {
"type" : "text",
"analyzer" : "my_custom_analyzer"
}
}
}
}
}
이제 index에 message 필드에 입력되는 값은 위 지정한 애널라이저가 적용이 된다.
index의 message 필드에 값을 입력하고 검색해 보면 filter 조건대로 대소문자가 필터링 되고 불용어들이 처리됨을 확인할 수 있다.
4. 텀 벡터
색인된 도큐먼트의 역 인덱스의 내용을 확인할 때는 도큐먼트 별로 _termvectors API를 이용해서 확인한다
GET <인덱스>/<도큐먼트 타입="">/<도큐먼트id>/_termvectors?fields=<필드명> 형식으로 사용,필드명>도큐먼트id>도큐먼트>인덱스>
앞에 입력한 my_custom_index/_doc/1 도큐먼트의 message필드를 확인하는 예제이다.
GET my_custom_index/_termvectors/1?fields=message