[엘라스틱서치] Elasticsearch in action 정리(3) - 데이터 분석

2020. 8. 3. 15:46 Big Data/빅데이터

엘라스틱서치에 데이터를 전송할때 실제 무슨일이 일어날까? 도큐먼트에서 텍스트를 엘라스틱서치로 전송하면 어떤 일이 발생할까? 엘라스틱서치는 대소문자가 변경될 때조차 문장 내에서 어떻게 특정 단어를 찾을 수 있을까? 엘라스틱서치의 분석이 더 유연한 방식으로 도큐먼트 집합을 검색하도록 하는지 알아보자.

 

분석은 엘라스틱서치가 도큐먼트를 역색인에 추가되도록 전송하기 전에 도큐먼트 본문에서 수행하는 과정이다. 엘라스틱서치는 도큐먼트가 색인에 추가되기 전에 모든 문석된 필드를 위해 수많은 단계를 거친다.

  • 문자 필터링 - 문자 필터를 이용해서 문자들을 변환한다.
  • 텍스트를 토큰으로 분해 - 텍스트를 한 개 이상의 토큰의 집합으로 분해한다.
  • 토큰 필터링 - 토큰 필터를 사용해서 개별 토큰을 변환한다.
  • 토큰 색인 - 토큰을 색인에 저장한다.

다음은 특정 텍스트가 분석된 토큰으로 변경되는 것을 나타낸다. 사용한 분석기는 문자 필터, 토크나이저, 토큰 필터를 사용해서 만든 사용자 지정 분석기라 할 수 있다.



 

(1) 문자 필터링

엘라스틱서치는 먼저 문자 필터를 실행하는데, 이들 필터는 연속된 특정 문자를 다른 문자로 변환하는데 사용된다. 이는 HTML에서 텍스트를 추출하거나 임의의 개수의 문자들을 다른 문자들로 변환하는데 사용할 수 있다. (동의어 딕셔너리 참조하여, 동의어는 하나의 토큰으로 처리?)

 

(2) 토큰으로 분해하기

텍스트에 문자 필터가 적용된 후 조각으로 분해할 필요가 있다. 루씬 자체는 데이터의 큰 문자열에 작용하지 않는 대신 토큰으로 알려져 있는것에 작동한다. 토큰은 몇개가 되든 텍스트 조각으로부터 생성한다. 예를 들어, 영문에서 사용 가능한 일반적인 토크나이저는 표준 토크나이저인데, 공백과 개행 같은 화이트스페이스뿐만 아니라 대시 같은 문자를 기반으로 텍스트를 토큰으로 분해한다.

 

(3) 토큰 필터링

텍스트 블록이 토큰으로 변환된 이후로, 엘라스틱서치는 토큰 필터로 불리는 것을 개별 토큰에 적용한다. 이들 토큰 필터는 입력으로 토큰을 가져와서 변경하거나 필요 시에 더 많은 토큰을 추가하고 삭제한다. 가장 유용한 것 중 하나이면서 토큰 필터링의 일반적인 예는 소문자 토큰 필터인데, 이 필터는 토큰을 가져와서 소문자로 만든다. 이는 "nosql" 텀으로 검색할 때 "NoSQL"을 가진 get-together를 찾을 수 있게 한다. 토큰은 각각 서로 다른 역할을 수행하는 하나 이상의 토큰 필터를 통과한 토큰으로 색인에 가장 적합한 형식의 데이터를 만든다.

 

위 그림 예에서 토큰 필터링 흐름을 살펴보면, 첫번째 토큰을 소문자로 만드는 것이고, 두번째는 불용어 "and"를 제거하는 것, 그리고 세번재는 동의어를 사용해서 "tools"를 "technologies"에 추가하는 것이다.

 

(4) 토큰 색인

0개 또는 그 이상의 토큰 필터를 거친 토큰은 도큐먼트 색인을 위해 루씬으로 전송된다. 이들 토큰은 역색인된다.

서로 다른 부분으로 구성한 모든 분석기는 0개 또는 그 이상의 문자 필터, 토크나이저, 0개 또는 그 이상의 토큰 필터처럼 정의할 수 있다. 직접 구성하지 않아도 바로 사용할 수 있는 사전구성된 분석기도 존재한다.

 

검색을 실행하는 동안 분석
어떤 종류의 쿼리를 사용하는가에 따라, 색인에 대해 검색을 수행하기 전에 분석은 검색 텍스트에 적용할 수도 있다. 특히, match와 match_phrase 쿼리는 검색하기 전에 분석을 수행하는 데 반해, term과 terms 쿼리는 그렇지 않다. 이는 왜 특정 검색만 도큐먼트가 일치하거나 그렇지 않은지를 디버깅할때 명심해야 할 중요한 사실인데, 이는 기대하는 바와 다르게 분석되었을 수도 있기 때문이다. 검색된 텍스트와 색인된 텍스트에 서로 다른 분석기를 설정하는 옵션조차 있다.

 

도큐먼트에 분석기 사용하기

서로 다른 유형의 분석기와 토큰 필터에 대해 아는 것이 좋지만, 실제 사용전 엘라스틱서치는 이를 어떻게 사용할지에 대한 정보가 필요하다. 엘라스틱 매핑에서 분석기를 위해 사용할 개별 토크나이저와 토큰 필터를 지정할 수 있고, 어떤 분석기가 어떤 필드를 사용할지 지정할 수 있다.

 

필드에 사용 가능한 분석기를 지정하는 두가지 방법이 있다.

  • 색인이 생성될때, 특정 색인을 위해 settings로
  • 엘라스틱서치의 설정 파일에서 전역 분석기로

 

일반적으로, 유연성을 높이려면, 매핑을 지정하기를 원하는 색인 생성 시점에 분석기를 지정하기가 더 쉽다. 이는 변경되거나 완전히 다른 분석기로 새로운 색인을 만들 수 있다. 반면에, 자주 변경하지 않는 색인에서 같은 분석기 집합을 사용하고 있다면, 설정 파일에 분석기를 넣어서 대역폭을 절약할 수도 있다. 모든 색인에서 사용되는 분석기를 설정 파일에 입력할 수 있으며, 색인을 생성할 때 안정성을 더하기 위해 추가적인 분석기를 지정할 수도 있다.

 

사용자 지정 분석기를 지정한 방식에 상관없이, 색인을 생성할때 매핑을 지정하거나 나중에 이를 지정하는 "put mapping API"를 사용해서 색인의 매핑에서 어떤 필드가 어떤 분석기를 사용할지 지정할 필요가 있다.

 

 

색인 생성시 분석기 추가하기

사용자 지정 분석기를 추가하는 것은 index 키 아래에 있는 settings 설정에 별도의 맵으로 지정한다. 이 키에 사용하려는 사용자 지정 분석기를 지정하고, 여기에 색인이 사용하는 사용자 지정 토크나이저, 토큰 필터, 문자 필터를 포함할 수 있다. 

curl -XPOST 'localhost:9200/myindex' -d '{
	"settings": {
		"number_of_shards": 2,
		"number_of_replicas": 1,
		"index": {	// 색인 레벨 설정
			"analysis": {	// 이 색인을 위한 분석 설정
				
				/* 사용자 지정 분석기 */
				"analyzer": {	// 분석기 개체에서 사용자 지정 분석기 지정
					"myCustomAnalyzer": {	// myCustomAnalyzer 사용자 지정 분석기
						"type": "custom",
						"tokenizer": "myCustomTokenizer",	// 텍스트를 토큰화하는 myCustom 토크나이저 사용
						"filter": ["myCustomFilter1", "myCustomFilter2"],	// 텍스트에 실행되는 myCustomFilter1와 myCustomFilter2 두 필터 설정
						"char_filter": ["myCustomCharFilter"]	// 다른 분석 전에 실행될 사용자 지정 문자 필터로 myCustomCharFilter를 지정
					}
				},

				/* 토크나이저 */
				"tokenizer": {
					"myCustomTokenizer": {
						"type": "letter"	// letter 타입의 사용자 지정 토크나이저 지정
					}
				},

				/* 사용자 지정 필터 */
				"filter": {
					"myCustomFilter1": {
						"type": "letter"
					},
					"myCustomFilter2": {
						"type": "kstem"
					}
				},

				/* 문자 필터 */
				"char_filter": {
					// 문자를 다른 것으로 매핑하는 사용자 문자 필터
					"myCustomCharFilter": {
						"type": "mapping",
						"mappings": ["ph=>f", "u=>you"]
					}
				}
			}
		}
	},
	"mappings": {
		... // 색인을 생성하기 위한 매핑
	}
}'

 

위 예제 코드에 필드의 분석기를 어떻게 지정하는지 알아보기 위한 매핑이 있다. 이 예제에서는 myCustomAnalyzer로 부르는 사용자 지정 분석기를 만드는데, 이 분석기는 myCustomTokenizer 사용자 지정 토크나이저와 myCutomFilter1, myCustomFilter2 두 개의 사용자 지정 필터를, 그리고 myCustomCharFilter 사용자 지정 문자 필터를 지정한다. 이들 개별 분석기 부품은 각각 JSON 서브 맵에 주어진다. 색인과 검색시 유연한 분석 옵션을 주기 위해 서로 다른 이름을 가진 다중 분석기를 지정하고 사용자 지정 분석기로 그들을 결합할 수 있다.

 

 

엘라스틱서치 설정으로 분석기 추가하기

색인을 생성하는 동안 settings로 분석기를 지정하는 것외에도 엘라스틱서치 설정 파일에 분석기를 추가하는 것은 사용자 지정 분석기를 지정하는 또 다른 방식이다. 그러나 이 방법에는 트레이드오프가 있다. 색인을 생성시 분석기를 추가하는 방법은 언제든 엘라스틱서치를 재시작하지 않아도 분석기를 변경할 수 있다. 그러나 엘라스틱 설정에 분석기를 지정하게 되면, 분석기의 어떠한 변경 사항이라도 엘라스틱서치를 재시작할 필요가 생긴다. 반면 색인을 생성할때 전송하는 데이터는 적을 것이다. 이 사실이 비록 일반적으로 유연성 측면에서 색인 생성할때 지정하는 것이 더 편리해도, 결코 분석기를 변경하지 않을 계획이라면 설정 파일에 넣는 것이 더 나은 선택이다.

 

 

매핑에서 필드를 위한 분석기 지정하기

사용자 지정 분석기 중 하나를 사용해서 분석하는 매핑을 가진 특정 필드를 어떻게 지정해야할까? 이는 단순히 매핑에서 분석 필드를 설정해서 필드에 대한 분석기를 지정하면 된다. 예를 들어, description으로 불리는 필드 매핑이 있다면, 다음과 같이 분석기를 설정한다.

{
	"mappings": {
		"document": {
			"properties": {
				"description": {
					"type": "string",
					// description 필드에 myCustomAnalyzer 분석기 지정
					"analyzer": "myCustomAnalyzer"
				}
			}
		}
	}
}

 

특정 필드가 모두 분석되는 것을 원치 않는다면, index 필드에 not_analyzed 설정을 지정할 필요가 있다. 이는 텍스트를 어떠한 종류의 변경도 없이 단일 토큰처럼 유지한다.

{
	"mappings": {
		"document": {
			"properties": {
				"name": {
					"type": "string",
					// name 필드는 분석하지 않도록 지정
					"index": "not_analyzed"
				}
			}
		}
	}
}

 

분석된 테스트를 다르게 저장하는 다중 필드 유형 사용하기

만약, 분석되거나 원본 텍스트 필드 둘 다에서 검색하려면 그 둘을 다중 필드에 지정하면 된다. 가끔 분석된 버전 필드와 더불어 분석되지 않은 원본 텍스트 검색이 필요할 때가 있다. 특히 문자열 필드에서 집계나 정렬 같은 작업을 할 경우에 그렇다. 다음 예제처럼 두가지 역할을 다할 수 있도록 필드를 지정할 수 있다.

curl -XPOST 'localhost:9200/get-together' -d '{
	"mappings": {
		"group": {
			"properties": {
				"name": {
					"type": "string",
					"analyzer": "standard", // 표준 분석기는 기본값이며 생략가능하다.
					"fields": {
						"raw": {
							"index": "not_analyzed", // 필드의 raw 버전이며 분석되지 않는다.
							"type": "string"
						}
					}
				}
			}
		}
	}
}'

 

분석 API로 텍스트 분석하기

분석 절차를 테스트하는 분석 API 사용은 엘라스틱서치 색인에 어떤 정보가 저장돼 있는지 추적하는 데 상당한 도움이 될 수 있다. 이 API는 분석기, 토크나이저, 토큰 필터를 지정해서 엘라스틱서치에 어떠한 텍스트라도 보내면 분석된 토큰을 돌려받게 한다. 

curl -X POST 'localhost:9200/_analyze?analyzer=standard' -d 'share your experience with NoSql & big data technologies'

{
	"tokens": [{
	 "token": "share",
	 "start_offset": 0,
	 "end_offset": 5,
	 "type": "<ALPHANUM>",
	 "position": 1
	}, {
	 "token": "your",
	 "start_offset": 6,
	 "end_offset": 10,
	 "type": "<ALPHANUM>".
	 "position": 2
	}
	...
	]
}

분석 API로부터 가장 중요한 출력은 토큰 키다. 출력은 보는 것처럼 처리된 토큰(실제 이들은 색인에 기록될 것이다)이 있는지 나타내는 이들 맵의 목록이다. 예를 들어, "share your experience with NoSql & big data technologies" 텍스트에 대해 여덟 개의 토큰인 share, your, experience, with, nosql, big, data, technologies를 반환받을 것이다. 이 경우 표준 분석기로 각 토큰은 소문자로 되고 문장 끝의 구두점은 제거된다는 것에 주목한다. 이는 엘라스틱서치가 어떻게 분석하는지 도큐먼트를 테스트하면서 볼 수 있어서 좋은데, 텍스트에서 수행되는 분석을 사용자 지정하는 몇가지 방법을 갖고 있다.

 

 

분석기 선택하기

생각해둔 분석기가 있어서 어떻게 텍스트를 다루는지 확인하고 싶다면, 분석기의 이름을 analyzer 파라미터로 설정하면 된다. elasticsearch.yml 파일에 분석기를 설정한다면, analyzer 파라미터에 있는 name으로 이를 지정할 수 있다. 추가적으로 사용자 지정 분석기로 색인을 만들었다면, 이 또한 name으로 분석기를 지정할 수 있다. 그러나, /_search HTTP 종단점 앞에 색인을 우선 명시해야 할 것이다. get-together 색인 이름을 사용하고 myCustomAnalyzer로 불리는 분석기를 사용한다면 다음과 같이 한다.

 

curl -XPOST 'localhost:9200/get-together/_analyze?analyzer=myCustomAnalyzer' -d 'share your experience with NoSql & big data technologies'

 

가끔은 내장 분석기만 사용하고 싶지 않을때도 있다. 그 대신 토크나이저와 토큰 필터의 조합으로 시도를 해보고 싶기도 하다. 예를 들어, 다른 분석기 없이 특정 토크나이저가 어떻게 문장을 분해하는지 보고 싶을 경우가 그렇다. 분석 API로 텍스트를 분석하기 위해 사용하는 토크나이저와 토큰 필터 목록을 지정할 수 있다. 예를 들어, 공백으로 텍스트를 분석하기 위해 화이트스페이스 토크나이저와 그 후에 소문자화 및 역토큰 필터를 사용하고 싶다면 다음과 같이 하면 된다.

 

curl -XPOST 'localhost:9200/_analyze?tokenizer=whitespace&filters=lowercase,revers' -d 'share your experience with NoSql & big data technologies'

 

그러면 토큰으로 "erahs, ruoy, ecneirepxe, htiw, lqson, &, gib, atad, seigolonhcet" 받을 것이다. 이 토크나이저는 데이터 파라미터로 넘겨받은 문장을 토큰화한 뒤에, 소문자 토큰으로 만들고 마지막으로 제공하는 텀을 가져와서 각 토큰을 반대로 뒤집는다.

 

 

필드 매핑에 기반한 분석

분석 API에 있어서 또 하나의 유용한 점은 색인을 위한 매핑 생성을 시작하면, 엘라스틱서치는 이미 생성된 매핑이 있는 필드를 기반으로 분석하게 한다. Description 필드로 매핑한다면 다음과 같이 보일 것이다.

... 다른 매핑 ...
"description": {
	"type": "string",
	"analyzer": "myCustomAnalyzer"
}

 

Field 파라미터를 명시해서 다음과 같은 요청으로 그 field와 연관된 분석기를 사용할 수 있다.

curl -XPOST 'localhost:9200/get-together/_analyze?field=description' -d 'share your experience with NoSql & big data technologies'

 

Description 필드와 연관된 분석기이므로 그 사용자 지정 분석기가 자동으로 사용될 것이다. 이를 사용하려면 우선 색인을 명시해야 한다는 것을 잊지 말자. 엘라스틱서치가 색인으로부터 특정 필드의 매핑을 가져올 수 있어야 하기 때문이다.

 

어떻게 서로 다른 분석기를 cURL로 테스트하는지 이미 다뤄봤으므로 엘라스틱서치가 바로 사용할 수 있도록 제공하는 모든 서로 다른 분석기를 알아보겠다. 그러나 언제든 서로 다른 컴포넌트(토크나이저와 토큰 필터)와 결합하는 자기만의 분석기를 만들 수 있다.

 

 

텀 백터 API를 사용하여 색인된 텀에 대해 알아보기

적절한 분석기에 대해 생각해본다면, _analyze endpoint는 유용하다. 그러나 특정 도큐먼트에 있는 텀에 관해 더 알아보려 한다면, 모든 구분 필드를 확인하는 것보다 더 효과적인 방법이 있다. 모든 텀에서 더 많은 정보를 얻기 위해 _termvector endpoint를 사용하면 된다. 이 종단점을 사용하면, 텀이 도큐먼트와 색인에 얼마나 있는지, 그리고 도큐먼트 내에 어디에 있는지도 알수 있다.

 

curl 'localhost:9200/get-together/group/1/_termvector?pretty=true'

{
	"_index": "get-together",
	"_type": "group",
	"_id": "1",
	"_version": 1,
	"found": true,
	"term_vectors": {
		"description": {
			"field_statistics": {
				"sum_doc_freq": 197,
				"doc_count": 12,
				"sum_ttf": 209
			},
...

엘라스틱서치는 소문자화, 스태밍, language-specific, 동의어 등 상당히 많은 것을 제공한다. 그리고 필요한 토큰을 얻기 위해 서로 다른 방식을 결합할 수 있는 유연성을 제공한다.

 

엘라스틱서치 분석기는 단일 토크나이저, 0 또는 그 이상의 토큰 필터와 함께 선택적인 문자 필터로 구성된다. 개별 분석기와 함께 분석기 같은 것을 사용하여 간단한 분석도 가능하다.

 

표준 분석기

표준 분석기는 분석기를 명시하지 않을때, 텍스트를 위한 기본 분석기다. 표준 토크나이저, 표준 토큰 필터, 소문자화 토큰 필터, 불용어 필터를 조합함으로써 유럽권 언어에 기본적인 것을 갖추고 있다. 필터를 위한 분석기를 명시하지 않으면 표준 분석기가 사용된다.

 

단순 분석기

단순 분석기는 소문자 토크나이저를 사용하는데, 토큰은 비-문자로 분해되고 자동으로 소문자로 변형된 것이다. 화이트스페이스로 분해하지 않는 아시아권 언어에서는 잘 동작하지 않는다.

 

화이트스페이스

화이트스페이스 분석기는 아주 단순하게 텍스트를 화이트스페이스로 분해해서 토큰으로 만든다.

 

불용어

불용어 분석기는 토큰 스트림으로부터 불용어를 거르는 추가적인 기능을 제외하고는 단순 분석기와 유사하다.

 

키워드

키워드 분석기는 필드 전체를 단일 토큰으로 만든다. 매핑에서 키워드 토크나이저를 사용하는 것보다 색인 설정에 not_analyzed로 설정하는 것이 더 낫다.

 

패턴

패턴 분석기는 분해된 부분이 될 토큰의 패턴을 명시하도록 한다. 그러나 패턴을 지정해야 하므로, 사용자 지정 분석기를 이용해서 필요한 토큰 필터와 기존 패턴 토크나이저를 결합하는 것이 보통 더 낫다.

 

언어 및 다국어

엘라스틱서치는 특정 언어에 특화된 분석기를 지원한다. arabic, armenian, basque, brazilian, bulgarian, catalan, chinese, cjk, czech, danish, dutch, english, finnish, french, galician, german, greek, irish, hindi, hungarian, indonesian, italian, norwegian, persian, portuguese, romanian, russian, sorani, spanish, swedish, turkish, thai를 위한 분석기다. 이들 이름 중 하나를 사용해서 특정 언어에 특화된 분석기를 명시할 수 있는데, 이 이름은 소문자로 입력해야 한다. 위 목록에 없는 언어는 플러그인으로 제공할 수 있다.

 

스노우볼

스노우볼 분석기는 표준 분석기와 마찬가지로 표준 토크나이저, 토큰 필터를 소문자화 토큰 필터, 불용어 필터와 함께 사용하는데, 여기에다 스노우볼 스태머(snowball stemmer)를 사용해서 텍스트로부터 어간(stem)을 추출한다. 이들 분석기에 대해 완전하게 이해하기 전에 분석기를 구성하는 부품에 대한 이해가 필요하다.

 

토큰화는 텍스트의 문자열을 가져와서 토큰이라 불리는 더 작은 청크로 분해하는 것이다. 엘라스틱서치는 바로 사용 가능한 내장 분석기뿐만 아니라 수많은 내장 토크나이저를 갖고 있다.

 

 

표준 토크나이저

표준 토크나이저는 대부분 유럽권 언어에 적합한 문법 기반 토크나이저인데 기본적으로 최대 255 길이의 토큰을 가지는 Segmenting Unicode Text를 다룬다. 이 토크나이저는 쉼표, 마침표 같은 구두점은 제거한다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=standard' -d 'I have, potatoes.'

위 예제의 결과 토큰은 I, have, potatoes다.

 

키워드

키워드 토크나이저는 전체 텍스트를 가져와서 단일 토큰처럼 토큰 필터에 제공하는 단순한 토크나이저이다. 어떠한 종류의 토큰화도 하지 않고 오직 토큰 필터만 적용하고자 할때 유용하다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=lowercase' -d 'Hi, there.'

토큰은 Hi와 there이다.

 

문자

문자 토크나이저는 텍스트를 가져와서 이를 문자가 아닌 것으로 토큰을 나눈다. 'Hi, there.' 문장에 적용하면, 쉼표, 공백, 마침표가 모두 비-문자이므로 토큰은 Hi와 there가 될 것이다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=letter' -d 'Hi, there.'

 

소문자화

소문자화(lowercase) 토크나이저는 정규(regular) 문자 토크나이저의 역할과 더불어 소문자화 토큰 필터(전체 토큰을 소문자로 만듦)를 결합한다. 단일 토크나이저(single tokenizer)를 사용하는 주요 이유는 한번에 두 역할을 모두 수행하므로 더 나은 성능을 얻기 때문이다. 결과 토큰은 hi와 there이다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=letter' -d 'Hi, there.'

 

화이트스페이스

화이트스페이스 토크나이저는 공백, 탭, 개행 등의 화이트스페이스로 토큰을 분해한다. 이 토크나이저는 어떠한 종류의 구두점도 제거하지 않아서 "Hi, there." 텍스트를 토큰화한 결과는 Hi와 there와 같이 두 개의 토큰이 된다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=whitespace' -d 'Hi, there.'

 

패턴

패턴 토크나이저는 임의의 패턴을 명시해서 텍스트를 토큰으로 분해한다. 이 패턴은 분해시 기준이 되는 간격 문자를 일치하도록 명시해야 한다. 예를 들어, 다음과 같은 텍스트를 분해하기 위해 텍스트를 .-.로 분해하는 사용자 정의 분석기를 만들 수도 있다.

curl -XPOST 'localhost:9200/pattern' -d '{
    "settings": {
        "index": {
            "analysis": {
                "tokenizer": {
                    "pattern1": {
                        "type": "pattern",
                        "pattern": "\\.-\\."
                    }
                }
            }
        }
    }
}'

curl -XPOST 'localhoat:9200/pattern/_analyze?tokenizer=pattern1' -d 'breaking.-.some.-.text'

토큰은 breaking, some, text이다.

 

UAX URL EMAIL

표준 토크나이저는 영어 단어를 처리하는데 상당한 쓸모가 있다. 그렇지만 최근 텍스트에는 웹사이트 주소나 이메일 주소로 끝을 맺는 것들이 많다. 표준 분석기는 의도하지 않았던 위치를 기준으로 분해하는 문제가 있고, 아래 분석 요청의 토큰 결과는 john.smith와 example.com으로 분해된다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=standard' -d 'john.smith@example.com'

 

다음 요청의 토큰 결과는 http, example, com, q, foo이다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=standard' -d 'http://example.com?q=foo'

 

UAX URL email 토크나이저는 이메일과 URL 모두 단일 토큰으로 남긴다. 이 토크나이저는 텍스트 필드에서 완전한 URL이나 이메일 주소를 검색할 때 도움이 된다. 

curl -XPOST 'localhost:9200/_analyze?tokenizer=uax_url_email' -d 'john.smith@example.com http://example.com?q=bar'

{
  "tokens": [
    {
      "token": "john.smith@example.com",
      "start_offset": 1,
      "end_offset": 23,
      "type": "<EMAIL>",    // email 필드 타입
      "position": 1
    },
    {
      "token": "http://example.com?q=bar",
      "start_offset": 24,
      "end_offset": 48,
      "type": "<URL>",    // url 필드 타입
      "position": 2
    }
  ]
}

이 토크나이저는 텍스트 필드에서 완전한 URL이나 이메일 주소를 검색할 때 큰 도움이 된다. 이 경우 응답에 email과 url로 설정된 필드 타입을 포함한다.

 

경로 계층

경로 계층 토크나이저는 같은 경로로 분할하는 파일을 검색할 시스템 경로를 색인하게 한다. 예를 들어, 파일명을 갖고 있는 /usr/local/var/log/elasticsearch.log를 색인한다고 가정하면, 경로 계층 토크나이저는 이를 다음과 같이 토큰화한다.

curl -XPOST 'localhost:9200/_analyze?tokenizer=path_hierarchy' -d '/usr/local/var/log/elasticsearch.log'

토큰은 /usr, /usr/local, /usr/local/var, /usr/local/var/log, /usr/local/var/log/elasticsearch.log 이다. 이는 파일이 공유하는 같은 경로 계층을 검색한다는 의미다. 즉, "/usr/local/var/log/es.log"를 쿼리해도 "/usr/local/var/log/elasticsearch.log"와 같은 토큰을 공유하므로 역시 결과를 반환할 수 있다.

토큰 필터는 색인을 위해 토크나이저와 준비한 데이터로부터 토큰을 받아들인다. 엘라스틱서치는 수많은 토큰 필터를 갖고 있는데, 이 중 대표적인 토큰 필터 몇가지를 알아본다.

 

표준 토큰 필터

표준 토큰 필터가 완벽하게 결과를 산출한다는 것을 보장할 순 없다. 예전 버전의 루씬에서는 단어 마지막에 붙어있는 's 문자나 관련없는 마침표 문자를 제거하는데 사용되었는데, 이제는 다른 토큰 필터와 토크나이저의 일부가 이미 이를 처리하고 있다.



 

소문자화

소문자화(lowercase) 토큰 필터는 통과하는 토큰을 소문자로 만드는 일만 한다. 다음 토큰 결과는 hi there!이다.

curl 'localhost:9200/_analyze?tokenizer=keyword&filters=lowercase' -d 'HI THERE!'

 

길이

길이 토큰 필터는 토큰의 min과 max 길이의 경계 밖으로 떨어진 단어를 제거한다. 예를 들어, min을 2로 max를 8로 설정하면 어떤 토큰이라도 2 문자보다 짧거나 8 문자보다 긴 토큰은 제거될 것이다. 다음 분석 요청의 토큰 결과는 small, word, and이다.

curl -XPUT 'localhost:9200/length' -d '{
  "settings": {
    "index": {
      "analysis": {
        "filter": {
          "my-length-filter": {
            "type": "length",
            "max": 8,
            "min": 2
          }
        }
      }
    }
  }
}'

curl 'localhost:9200/length/_analyze?tokenizer=standard&filters=my-length-filter&pretty=true' -d 'a small word and a longerword'

 

불용어

불용어 토큰 필터는 토큰 스트림으로부터 불용어를 제거한다. 다음은 영어를 위한 불용어 목록이다.

a, an, and, are, as, at, be, but, by, for, if, in, into, is, it, no, not, of, on, or, such, that, the, their, then, there, these, they, this, to, was, will, with

 

불용어 목록을 명시하기 위해, 다음과 같이 단어 목록으로 사용자 지정 토큰 필터를 만들 수도 있다.

curl -XPOST 'localhost:9200/stopwords' -d {
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "stop1": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": ["my-stop-filter"]
          }
        },
        "filter": {
          "my-stop-filter": {
            "type": "stop",
            "stopwords": ["the", "a", "an"]
          }
        }
      }
    }
  }
}

 

설정 위치로부터의 상대 경로나 절대 경로로 지정한 파일로부터 불용어 목록을 읽어오는데 개별 단어는 개행으로 구분하고 파일은 UTF-8로 인코드해서 저장한 파일이여야 한다. 파일로부터 불용 단어 필터를 읽어오도록 다음과 같이 설정할 수 있다.

curl -XPOST 'localhost:9200/stopwords' -d '{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "stop1": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [ "my-stop-filter" ]
          }
        },
        "filter": {
          "my-stop-filter": {
            "type": "stop",
            "stopwords_path": "config/stopwords.txt"
          }
        }
      }
    }
  }
}'

 

사전 정의된 불용어 언어 목록을 사용할 수도 있다. 이 경우 불용어 값은 "_dutch_"와 같은 사전 정의된 언어가 올 수 있는데, "stopwords":"_dutch_" 처럼 지정한다.

 

 

TRUNCATE, TRIM, LIMIT TOKEN COUNT

다음의 필터는 토큰 스트림을 제한하는데 사용한다고 할 수 있다.

truncate 토큰 필터는 사용자 지정 설정에서 length 파라미터로 특정 길이가 넘어가는 토큰을 잘라낸다. 기본값으로 10문자까지 잘라낸다.
trim 토큰 필터는 토큰을 둘러싼 모든 화이트스페이스를 제거한다. 예를 들어 " foo "는 "foo" 토큰으로 변형될 것이다.
limit token count 토큰 필터는 특정 필드가 담을 수 있는 토큰의 최대 갯수를 제한한다. 

 

반전

반전 토큰 색인은 토큰 스트림으로부터 개별 토큰을 정반대로 뒤집는다. Edge ngram 필터를 사용하거나 와일드카드로 시작하는 검색을 해야 할때 특히 유용하다. "*bar"처럼 매우 느린 와일드카드로 시작하는 검색 대신 반전된 필드에서 "rab*"로 검색하는 것이 루씬에서 더 빠르기 때문이다. 개별 토큰이 반전됐음을 확인할 수 있다. 그러나 토큰 사이의 순서는 바뀌지는 않았다.

curl 'localhost:9200/_analyze?tokenizer=standard&filters=reverse' -d 'Reverse token filter'

{
  "tokens": [
    {
      "token": "esreveR",
      "start_offset": 0,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "nekot",
      "start_offset": 8,
      "end_offset": 13,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "retlif",
      "start_offset": 14,
      "end_offset": 20,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

 

유일성

유일성 토큰 필터는 오직 유일한 토큰만 유지한다. 일치하는 첫 토큰의 메타 데이터를 유지하면서 이후에 나타나는 것들은 모두 제거한다.

curl 'localhost:9200/_analyze?tokenizer=standard&filters=unique' -d 'foo bar foo bar baz'

{
  "tokens": [
    {
      "token": "foo",
      "start_offset": 0,
      "end_offset": 3,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "bar",
      "start_offset": 4,
      "end_offset": 7,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "baz",
      "start_offset": 16,
      "end_offset": 19,
      "type": "<ALPHANUM>",
      "position": 3
    }
  ]
}

 

ASCII FOLDING

ASCII FOLDING 토큰 필터는 일반 ASCII 문자셋에 해당하지 않는 Unicode 문자가 있다면, 이에 대응하는 ASCII로 변환한다. 

 

동의어

동의어 토큰 필터는 토큰 스트림의 단어를 원래 토큰과 동일한 오프셋 내에서 동의어로 교체한다.

curl -XPOST 'localhost:9200/_analyze?analyzer=standard' -d'I own that automobile'

{
  "tokens": [
    {
      "token": "i",
      "start_offset": 0,
      "end_offset": 1,
      "type": "<ALPHANUM>",
      "position": 1
    },
    {
      "token": "own",
      "start_offset": 2,
      "end_offset": 5,
      "type": "<ALPHANUM>",
      "position": 2
    },
    {
      "token": "that",
      "start_offset": 6,
      "end_offset": 10,
      "type": "<ALPHANUM>",
      "position": 3
    },
    {
      "token": "automobile",
      "start_offset": 11,
      "end_offset": 21,
      "type": "<ALPHANUM>",
      "position": 4
    }
  ]
}

 

"automobile"에 대한 동의어를 다음과 같이 지정하는 사용자 지정 분석기를 정의할 수 있다.

curl -XPOST 'localhost:9200/syn-test' -d '{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "synonyms": {
            "type": "custom",
            "tokenizer": "standard",
            "filter": [ "my-synonym-filter" ]
          }
        },
        "filter": {
          "my-synonym-filter": {
            "type": "synonym",
            "expand": true,
            "synonym": [ "automobile=>car" ]
          }
        }
      }
    }
  }
}'

위 예제에서는 토큰을 교체하는 동의 필터를 설정했다. 그런데, 교체하는 대신 동의어 토큰에 정의해둔 토큰들을 토큰에 추가할 수도 있다.(automobile, car로 동의어 필터를 만들면, automobile이나 car 토큰이 들어왔을때 automobile과 car를 토큰으로 함께 출력) 이 경우 automobile=>car 대신 automobile, car를 사용한다.

 

Ngram과 edge ngram은 엘라스틱서치에서 텍스트를 토큰화하는 독특한 방식이다. Ngram은 토큰의 각 단어 부분을 다중 서브 토큰으로 분해하는 방식이다. Ngram은 토큰의 각 단어 부분을 다중 서브 토큰으로 분해하는 방식이다. Ngram과 edge ngram 필터 둘 다 min_gram과 max_gram 설정이 필요하다. 이들 설정은 단어로부터 분해된 토큰의 크기를 제어한다. Ngram 분석기로 "spaghetti" 단어를 분석한다고, 가정해보겠다.

 

1-gram

"spaghetti"의 1-gram은 s,p,a,g,h,e,t,t,i가 된다. ngram의 크기에 따라 문자열은 작은 토큰으로 분해한다. 이 경우, 개별항목은 단일 문자가 된다.

 

Bigram

문자열을 bigrams로 분해한다면, sp,pa,ag,gh,he,et,tt,ti와 같은 작은 토큰을 얻을 것이다.

 

Trigram

크기가 3이라면, spa,pag,agh,ghe,het,ett,tti 토큰을 얻을 것이다.

 

min_gram과 max_gram 설정

이 분석기를 이용할때 두개의 크기값을 설정해야 한다. 하나는 생성하려는 가장 작은 ngram(min_gram 설정), 다른 하나는 가장 긴 ngram(max_gram 설정)을 지정한다. 이런 방식으로 텍스트를 분석하는 것에 흥미로운 점은, 텍스트를 쿼리할때 쿼리는 텍스트를 동일한 방식으로 분해할텐데, 그래서 철자가 틀린 "spaghety" 단어도 찾을 수 있다는 것이다. 이를 검색하는 한 가지 방법은 일치를 확인하기 위해 단어의 편집 거리를 지정하는 fuzzy 쿼리를 실행하는 것이다. 그러나 ngram을 사용해서 그런 유사한 기능을 얻을 수 있다. 

"spaghetti"를 bigram으로 만든 토큰: sp, pa, ag, gh, he, et, tt, ti
"spaghety"를 bigram으로 만든 토큰: sp, pa, ag, gh, he, et, ty

6개의 토큰이 서로 겹치는 것을 볼 수 있는데, "spaghetti" 단어는 "spaghety"로 쿼리할때 여전히 일치할 수 있는 것이다. 그러나 더 많은 단어가 포함된 쿼리라면, 원본 "spaghetti" 단어와 일치하지 않을 수도 있다. 이런 이유로 쿼리 적합성 테스트가 매우 중요하다.

 

Ngram의 또 다른 유용성은 사전에 어떤 언어로된 텍스트인지 모르거나 다른 유럽권 언어가 서로 다른 방식으로 결합된 단어로된 언어를 갖고 있을 때에도 텍스트를 분석할 수 있다는 것이다. 이는 또한 서로 다른 언어에서 서로 다른 분석기를 정의하거나 도큐먼트의 서로 다른 필드를 사용하는 방식이 아니라 단일 분석기로 다중 언어를 다룰 수 있는 장점이 있다.

 

Edge ngram

Edge ngram으로 불리는 ngram 분해의 변종은 오직 앞부분부터 ngram을 만든다. "spaghetti" 예제에서 min_gram 설정을 2로 max_gram 설정을 6으로 지정하면 sp, spa, spag, spagh, spaghe 토큰을 얻게될 것이다.

 

개별 토큰이 앞부분부터 만들어지는 것을 볼 수 있다. 실제로 프리픽스 쿼리를 실행하지 않고 같은 프리픽스를 공유하는 단어를 검색하는데 도움이 된다. 단어의 뒷부분부터 ngram을 만들려면 side 속성을 기본값이 front 대신 back으로 변경하면 된다.

 

Ngram 설정

Ngram은 단어 사이의 공백이 없어도 언어를 분석할 수 있기 때문에 어떤 언어인지 모를때 텍스트를 분석하는데 좋은 방법일 수 있다. Min_gram과 Max_gram 설정으로 edge ngram 분석기를 설정하는 예제이다.

curl -XPOST 'localhost:9200/ng' -d '{
  "settings": {
    "number_of_shards": 1,
    "number_of_replicas": 0,
    "index": {
      "analysis": {
        "analyzer": {
          "ng1": {
            "type": "custom",
            "tokenizer": "standard".
            "filter": ["reverse", "ngf1", "reverse"]   // reverse, edge ngram, reverse 순서로 분석기 지정
          }
        },
        "filter": {
          "ngf1": {
            "type": "edgeNgram",
            "min_gram": 2,
            "max_gram": 6
          }
        }
      }
    }
  }
}'
curl -XPOST 'localhost:9200/ng/_analyze?analyzer=ng1' -d'spaghetti'

{
  "tokens": [
    {
      "token": "ti",
      "start_offset": 0,
      "end_offset": 9,
      "type": "word",
      "position": 1
    },
    {
      "token": "tti",
      "start_offset": 0,
      "end_offset": 9,
      "type": "word",
      "position": 1
    },
    {
      "token": "etti",
      "start_offset": 0,
      "end_offset": 9,
      "type": "word",
      "position": 1
    },
    {
      "token": "hetti",
      "start_offset": 0,
      "end_offset": 9,
      "type": "word",
      "position": 1
    },
    {
      "token": "ghetti",
      "start_offset": 0,
      "end_offset": 9,
      "type": "word",
      "position": 1
    }
  ]
}

 

Shingle

Ngram과 edge ngram과 같은 선상에서 shingle 필터로 알려져 있다. Shingle 토큰 필터는 기본적으로 문자 수준이 아닌 토큰 수준에서의 ngram이다. Shingle 필터는 대신 토큰 수준에서 이를 결정하는데, "foo bar baz" 텍스트를 min_shingle_size는 2로 max_shingle_size는 3으로 적용하면 foo, foo bar, foo bar baz, bar, bar baz, baz가 결과토큰이 된다.

 

단일 토큰이 결과에 포함되는 이유는, shingle 필터는 기본적으로 원래 토큰을 포함한다. 그래서 원본 토크나이저가 만ㄷ를어내는 토큰 foo, bar, baz가 shingle 토큰필터를 거치게되어 단일 토큰도 결과에 포함되게 되는 것이다. 단일 토큰을 포함한 이들 모든 토큰은 최종 토큰 스트림을 형성하도록 결합한다.

 

 

스태밍은 단어를 단어의 원형이나 어근으로 줄이는 역할을 한다. 이는 검색할 때 아주 편리하다. 단어의 복수형뿐만 아니라 단어의 어근이나 어간을 공유하는 단어 같은 것도 일치할 수 있기 때문이다. 스태밍은 엄격하고 정확한 일치를 필요로 하는 검색을 좀 더 유연하게 바꾸는 강력한 방식이다.

 

(1) 스태밍 알고리즘

스태밍 알고리즘은 공식을 사용하거나 어간으로 만들기 위해 개별 토큰 규칙의 집합을 적용한다. 엘라스틱서치는 현재 세개의 각각 다른 스태머 알고리즘을 제공하는데 snowball 필터, porter_stem 필터, kstem 필터가 이에 해당한다. 이들은 거의 유사항 방식으로 동작하는데, 스태밍에 관해서 어느 정도로 공격적으로 처리하는지가 약간 다르다. 더 공격적인 스태머는 그렇지 않은 스태머에 비해 단어를 더 많이 자른다는 것을 의미한다.

 

어떻게 스태머가 단어를 어간으로 만드는지 보기위해 분석 API에 토큰 필터처럼 명시한다.

curl -X POST 'localhost:9200/_analyze?tokenizer=standard&filters=kstem' -d 'administrators'

 

테스트할 필터로 snowball, porter_stem, kstem 중 하나를 사용해보자. 알고리즘 스태머의 대안으로 원래 단어와 그의 어간으로 일대일 매핑된 사전을 이용해서 어간을 만들 수도 있다.

 

 

(2) 사전으로 스태밍

바탕이 되는 언어가 뭔지 알 수 없으므로 가끔 알고리즘 스태머가 이상한 방식으로 단어를 어간으로 만든다. 이런 이유로, 단어를 어간으로 만드는 더 정확한 방식은 단어 사전을 사용하는 것이다. 엘라스틱서치에서 스태밍을 취급하는 사전과 결합하는 hunspell 토큰 필터를 사용할 수 있다. 이 때문에 스태밍의 품질을 사용하는 사전의 품질과 직접 연관이 있다. 스태머는 오직 사전에 있는 단어만 어간으로 만들 수 있기 때문이다.

 

Hunspell 분석기를 생성할때, 사전 파일은 elasticsearch.yml 파일이 있는 같은 디렉터리에 hunspell이라는 디렉터리에 있어야 한다. hunspell 디렉터리 내부에 개별 언어 사전은 locale명으로 폴더를 만들어야 한다.

 

 

(3) 토큰 필터로부터 스태밍 오버라이드

가끔 스태머가 올바르지 않게 처리하거나 특정 단어가 정화하게 일치하기를 원한다면, 어간화된 단어는 원치 않을 것이다. 토큰 필터 체인에서 스태밍 필터 전에 키워드마커 토큰 필터를 배치하면 이를 해결할 수 있다. 이 키워드 마커 토큰 필터에서 단어 목록이나 단어 목록을 가진 파일을 명시해서 어간으로 만들지 않도록 할 수 있다.

 

어간화된 것으로부터 단어를 막는 것 외에 수동으로 스태밍 단어로 사용되는 규칙 목록을 명시하는 것이 유용할 수도 있다. cats => cat 규칙 같은 것도 명시하도록 하는 스태머 오버라이드 토큰 필터로 이를 해결할 수 있다.

 

스태머 오버라이드가 규칙을 발견하고 단어로 이를 적용하면 다어는 어떠한 다른 스태머도 어간화하지 않는다.

 

이들 토큰 필터 둘 다 다른 어떠한 다른 스태밍 필터보다 앞에 위치해야 한다. 체인에서 이후 다른 토큰 필터에 의해 적용된 스태밍으로부터 텀을 보호하려면 말이다.

 

엘라스틱서치가 색인이나 쿼리 전에 필드 텍스트를 어떻게 조각으로 분해하는지 살펴보았다. 텍스트는 서로 다른 토큰으로 분해되고 필터는 이들 토큰을 만들고 삭제하며 변경하는데 사용된다.

 

분석은 도큐먼트의 필드에 있는 텍스트를 토큰으로 만드는 과정이다. 같은 과정이 match 쿼리 같은 쿼리의 검색 문자열에도 적용된다. 오큐먼트의 토큰이 검색 문자열로부터 토큰과 일치할때 그 도큐먼트는 일치한다.

 

매핑을 통해 개별 필드에 분석기를 할당한다. 그 분석기는 엘라스틱서치 설정이나 색인 설정에 의해, 또는 기본 분석기 설정으로 정의한다.

 

분석기는 하나 이상의 문자 필터가 하나 이상의 토큰 필터가 되는 토크나이저에 의해 만들어지는 체인을 처리한다.

 

문자 필터는 토크나이저로 보내기 전에 문자열을 처리하는데 사용된다. 예를 들어 "&"을 "and"로 변환하는 과정

 

토크나이저는 문자열을 다수개 토큰으로 분해하는데 사용할 수 있다. 화이트스페이스 토크나이저는 공백으로 개별 단어를 구분해서 토큰을 만드는데 사용한다.

 

토큰 필터는 토크나이저로부터 오는 토큰을 처리하는데 사용한다. 어근으로 단어를 줄이고 단어의 복수와 단수 버전 둘다 동작하는 검색을 만드는데 스태밍을 사용할 수 있다.

 

Ngram 토큰 필터는 단어 일부로 토큰을 만든다. 이는 검색 문자열이 오자를 포함하더라도 동작하는 검색이 필요하다면 유용하다.

 

Edge ngram은 ngram과 비슷하지만 오직 단어의 시작 또는 마지막으로부터 처리한다는 것이 다르다.

 

Shingle은 ngram과 phrase 수준에서는 닮았다. 구에서 매번 연속하는 두 개의 단어로 텀을 생성할 수 있다. 이는 다중-단어 일치에 대해 적합성을 높이려 할 때 유용하다.



출처: https://12bme.tistory.com/478?category=737765 [길은 가면, 뒤에 있다.]