데이터 모델링
서론
엘라스틱서치에서는 색인할 때 문서의 데이터 유형에 따라 필드에 적절한 데이터 타입을 지정해야 하는데
이런 과정을 매핑 이라고 한다.
사전 매핑을 하지 않으면 엘라스틱서치가 자동으로 필드 생성 및 타입까지 결정하는데 이를 스키마리스 기능 이라고 한다.
스키마리스 기능은 성능과 직접적인 관련이 있기 때문에 사용하지 않을 것을 권장한다.
매핑 API 이해하기
매핑은 데이터베이스의 스키마에 대응하는 개념이다.
필드의 속성에는 데이터 타입과 메타 데이터가 포함 된다. 이를 통해 어떻게 역색인으로 변환되는지 상세하게 정의할 수 있다.
매핑 정보 설정시 고려해야 할 사항은 세가지가 있으며 다음과 같다.
- 문자열을 분석할 것인가 ?
- _source에 어떤 필드를 정의할 것인가?
- 날짜 필드를 가지는 필드는 무엇인가?
매핑 생성하기
도큐먼트에 입력될 데이터의 매핑 형태를 정의할 때는 properties 필드에 필드 명과 타입 등 옵션을 입력해서 설정한다.
PUT <호스트>/<인덱스> -d '
{
"mappings": {
"properties": {
... <매핑내용>
}
}
}
'
예제 인덱스를 생성해보자
curl -XPUT -H 'Content-Type: application/json;' http://localhost:9200/tests -d '
{
"mappings": {
"properties": {
"movieCd": { "type": "keyword" },
"movieNm": { "type": "text", "analyzer": "standard" },
"movieNmEn": { "type": "text", "analyzer": "standard" },
"prdtYear": { "type": "integer" },
"openDt": { "type": "integer" },
"typeNm": { "type": "keyword" },
"prdtStatNm": { "type": "keyword" },
"nationAlt": { "type": "keyword" },
"genreAlt": { "type": "keyword" },
"repNationNm": { "type": "keyword" },
"repGenreNm": { "type": "keyword" },
"compaines": {
"properties": {
"companyCd": { "type": "keyword" },
"companyNm": { "type": "keyword" }
}
},
"directors": {
"properties": {
"peopleNm": { "type": "keyword" }
}
}
}
}
}
'
엘라스틱서치 최신버전 에서는 Index 하나에 Type 하나만 매핑이 가능해 짐에 따라 매핑 정보에 Type을 설정할 수 없다.
인덱스 생성시 _doc 이라는 타입이 자동 생성된다.
www.elastic.co/kr/blog/moving-from-types-to-typeless-apis-in-elasticsearch-7-0
매핑 확인하기
_mapping API 를 사용하면 인덱스/타입 의 매핑 확인이 가능하다.
GET <호스트>/<인덱스>/_mapping
GET <호스트>/<인덱스>/<타입>/_mapping
이전에 생성했던 매핑 정보를 확인 해 보자.
curl -XGET http://localhost:9200/tests/_mapping?pretty
{
"tests" : {
"mappings" : {
"properties" : {
"compaines" : {
"properties" : {
"companyCd" : {
"type" : "keyword"
},
"companyNm" : {
"type" : "keyword"
}
}
},
"directors" : {
"properties" : {
"peopleNm" : {
"type" : "keyword"
}
}
},
"genreAlt" : {
"type" : "keyword"
},
"movieCd" : {
"type" : "keyword"
},
"movieNm" : {
"type" : "text",
"analyzer" : "standard"
},
"movieNmEn" : {
"type" : "text",
"analyzer" : "standard"
},
"nationAlt" : {
"type" : "keyword"
},
"openDt" : {
"type" : "integer"
},
"prdtStatNm" : {
"type" : "keyword"
},
"prdtYear" : {
"type" : "integer"
},
"repGenreNm" : {
"type" : "keyword"
},
"repNationNm" : {
"type" : "keyword"
},
"typeNm" : {
"type" : "keyword"
}
}
}
}
}
매핑 파라미터
매핑 파라미터는 색인할 필드의 데이터 저장에 대한 다양한 옵션을 제공한다.
analyzer
해당 필드는 형태소 분석을 하겠다는 의미이다.
색인 및 검색 시 지정한 분석기 로 형태소 분석을 수행한다.
text 데이터 타입은 analyzer 파라미터를 사용 해야 하며, 기본 값 은 Standard Analyzer 이다.
normalizer
term query에 분석기를 사용하기 위해 사용된다.
keyword 데이터 타입은 원문을 기준으로 색인 된다.
따라서 cafe, Cafe 는 서로 다른 문서로 인식되지만, normalizer를 통해 필터를 사용하면 같은 데이터로 인식한다.
boost
필드에 가중치를 부여한다. 가중치에 따라 _score 점수가 달라 지게 되는데, 이 점수에 따라 검색 결과의 노출 순서에 영향을 주게 된다.
색인 시점에 boost 설정을 한다면, 재색인 하기 전 까지는 가중치 변경이 불가능하다.
검색 시점에만 사용하는 것을 권장한다. 7.0 버전 이후부터는 Deprecate 되었다.
coerce
색인 시 자동 변환을 허용할 지 여부를 결정한다.
ex) "10" → 10 의 자동 형 변환
copy_to
해당 필드의 값을 지정한 필드로 복사한다.
keyword 타입의 필드에 copy_to 파라미터를 사용해서 text 타입을 지정하여 형태소 분석이 가능하다.
이를 이용해 _all 칼럼과 동일한 기능 구현이 가능하다.
fielddata
Heap 공간에 생성하는 메모리 캐시
메모리 이슈와 잦은 GC로 인해 사용 되지 않으며, 최신 버전에서는 doc_values 라는 새로운 캐시를 제공한다.
doc_values
엘라스틱서치 최신 버전 에서 제공하는 기본 캐시 이다.
Text 타입을 제외한 모든 타입에서 기본 값으로 활성화 되어있다.
이는 루씬 기반 캐시 이며 운영체제 파일 시스템 캐시를 통해 빠른 액세스가 가능하다.
dynamic
매핑에 필드 추가 시 동적 생성 여부를 결정한다.
인자 | 설명 |
true | 새로 추가는 필드를 매핑에 추가한다. |
false | 새로 추가되는 필드를 무시한다. 색인 대상에서 제외되지만 _source 에는 표시된다. |
strict | 새로운 필드가 감지되면 예외가 발생하고 문서 자체가 색인되지 않는다. |
enabled
false 설정시 _source에서는 검색이 되지만 색인은 하지 않는다.
Text, Keyword 타입에는 매핑 되지 않으며 Object 타입에만 적용된다.
format
엘라스틱서치는 날짜/시간 을 문자열로 표현한다. 문자열로 변경 시 미리 구성된 포맷을 사용할 수 있다.
포맷 | 날짜 형식 | 비고 |
basic_date | yyyyMMdd | 년도/월/일 |
basic_date_time | yyyyMMdd'T'HHmmss.SSZ | 년도/월/일/T/시/분/초/밀리초/Z |
basic_time | HHmmss.SSS | 시/분/초/밀리초/Z |
date/strict_date | yyyy-MM-dd | 년도/시/분 |
date_hour_minute_second/strict_date_hour_minute_second | yyyy-MM-dd'T'HH:mm:ss | 년도/시/분/T/시/분/초/밀리초 |
date_time/strict_date_time | yyyy-MM-dd'T'HH:mm:ss.SSSZZ | 년도/시/분/T/시/분/초/밀리초/ZZ |
ignore_above
지정한 크기를 넘어설 경우 빈 값으로 색인 한다. (빈 값으로 저장 되므로 주의)
ignore_malformed
잘못된 데이터 타입 색인 시 해당 필드를 무시하고 색인 한다.
index
필드 값 색인 여부를 결정한다. 기본 값은 true
fields
다중 필드를 설정할 수 있다.
필드 내 또 다른 필드 정보를 매핑 할 수 있어 각각 다른 분석기 로 처리하도록 설정이 가능하다.
norms
_score 값 계산 시 정규화 인수를 사용할지 설정한다. 기본 값은 true
null_value
엘라스틱서치는 필드의 값이 null 이면 색인 시 필드를 생성하지 않는데, null_value를 설정하면 이를 무시하고 색인 시 필드를 생성한다.
position_increment_gap
배열 데이터 색인 시 정확도를 높이기 위한 옵션이다.
필드 데이터 중 단어와 단어 사이의 간격을 허용할지 설정한다.
properties
Object 혹은 Nested 타입 스키마 정의 시 사용된다.
search_analyzer
색인과 검색 시 보통 같은 분석기를 사용하지만, 다른 분석기를 사용하고 싶을 경우 별도로 지정할 수 있다.
similarity
유사도 측정 알고리즘을 지정한다. 기본 값은 BM25
알고리즘 | 설명 |
BM25 | Okapi BM25 알고리즘이다. 엘라스틱서치의 기본 유사도 측정 알고리즘 |
classic | TF/IDF 알고리즘이다. 문서 내 용어의 갯수와 전체 용어의 갯수를 이용해 유사도를 계산한다. |
boolean | 단순히 boolean 연산으로 복잡도를 측정한다. |
store
필드 값을 검색 결과에 포함하기 위한 옵션이다.
해당 필드를 자체적으로 저장하여 검색에 사용한다.
_source 값이 크다면 store 옵션을 사용해 특정 필드만 로드 해서 사용하는 것이 효율적이다.
term_vector
루씬에서 분석된 용어 정보 포함 여부를 결정한다.
인자 | 설명 |
no | 텀 벡터를 저장하지 않는다. |
yes | 필드와 용어만 사용한다. |
with_poisitions | 용어의 시작과 끝 위치를 저장한다. |
with_offsets | 문자 오프셋을 저장한다. |
with_positions_offsets | 용어의 시작과 끝 위치 문자 오프셋을 모두 저장한다. |
메타 필드 (내장 필드)
엘라스틱서치 매핑시 매핑 의 메타 정보를 설정하고 데이터 처리 방법 설정이 가능하다.
최초 인덱스 생성시 매핑에 설정하거나 _mapping API 를 사용하여 기존 생성된 인덱스에 메타 필드 설정이 가능하다.
인덱스 생성시 메타 필드 설정
PUT <호스트>/<인덱스> -d '
{
"mappings": {
"<내장필드명>": {
...<필드 내용>
}
}
}
'
인덱스 생성 후 메타 필드 설정
PUT <호스트>/<인덱스>/_mapping/<타입> -d '
{
"mappings": {
"<내장필드명>": {
...<필드 내용>
}
}
}
'
_index 필드
해당 문서가 속한 인덱스 명을 가지고 있다.
집계 API를 이용해 해당 인덱스에 존재하는 문서 확인 등이 가능하다.
집계 API를 활용한 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/teams/_search?pretty -d '
{
"size": 0,
"aggs": {
"indices": {
"terms": {
"field": "_index",
"size": 10
}
}
}
}
'
요청 결과
{
"took" : 11,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 3,
"relation" : "eq"
},
"max_score" : null,
"hits" : [ ]
},
"aggregations" : {
"indices" : {
"doc_count_error_upper_bound" : 0,
"sum_other_doc_count" : 0,
"buckets" : [
{
"key" : "teams",
"doc_count" : 3
}
]
}
}
}
_type 필드
해당 문서가 속한 매핑의 타입 정보를 가지고 있다.
인덱스 내부에 타입별 문서를 확인 가능하다.
_id 필드
문서를 식별하는 유일한 키이다.
index 옵션과 store 옵션을 사용해서 색인 여부 및 저장 여부 설정이 가능하다.
_uid 필드
특수 목적의 식별 키 이며, '#' 태그를 사용해 _type, _id 값을 조합할 수 있다.
검색 시 조회되지 않는다.
_source 필드
문서의 원본 데이터를 제공한다.
_source의 enabled 옵션을 false로 설정하면 색인시 원본 데이터는 저장하지 않는다.
_reindex API 나 스크립트를 사용해 해당 값 계산시 해당 메타 필드 활용이 가능하다.
_all 필드
색인에 사용된 모든 필드의 정보를 가진 메타 필드이다.
모든 필드의 내용이 하나의 텍스트로 합쳐서 제공된다.
- 각 필드의 include_in_all 옵션을 false 로 주면 해당 필드를 제외 할 수 있다.
6.0 이후부터는 deprecated, 필드 복사 필요시 copy_to 파라메터를 사용하자.
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/testbooks?pretty -d '
{
"mappings": {
"_all": { "enabled": true },
"properties": {
"title": {
"include_in_all": true,
"type": "keyword"
},
"subtitle": {
"include_in_all": false,
"type": "keyword"
}
}
}
}'
필드 데이터 타입
필드 데이터 타입은 크게 문자열, 정수, 실수, 불린 과 같은 기본 타입과 객체 와 같은 확장 타입이 있다.
Keyword 타입
키워드 형태로 사용할 데이터에 적합한 타입이다.
분석기를 거치지 않고, 원문 그대로 색인 하며 보통 검색 시 필터링 되는 항목, 정렬이 필요한 항목, 집계 항목에 많이 사용된다.
주요 파라미터는 boost, doc_values, index, null_value, store 가 있다.
Text 타입
색인 시 지정된 분석기 가 문자열 데이터로 분석한다.
별도 분석기를 정의하지 않는다면 기본 값 인 Standard Analyzer 를 사용한다.
전체 텍스트가 토큰 화 되며, 전문 검색 및 특정 단어 검색이 가능하다.
주요 파라미터는 analyzer, boost, fielddata, index, norms, store, search_analyzer, similarity, term_vector 가 있다.
검색 뿐 아니라 정렬이나 집계가 필요한 경우 멀티 필드로 Keyword 타입을 지정해서 사용하자.
Array 타입
2차원의 데이터를 표현하고 싶다면 Array 데이터 타입을 사용해야 한다.
기본 데이터 타입으로 지정할 수도 있지만, 객체 형태로 도 정의 가 가능하다.
Array타입은 매핑 시 타입을 명시적으로 지정하지 않는다. 첫 번째 값이 배열의 데이터 타입을 결정한다.
Numeric 타입
엘라스틱서치는 숫자 타입을 여러 종류로 제공하는데, 데이터 크기에 알맞은 타입을 제공함으로써 색인과 검색을 효율적으로 처리한다.
타입 명 | 설명 |
long | 64비트 정수. 범위는 -2의 63승 ~ 2의 63승 -1 |
integer | 32비트 정수. 범위는 -2의 31승 ~ 2의 31승 -1 |
short | 16비트 정수. 범위는 -32,768 ~ 32,767 |
double | 64비트 부동 소수점을 갖는 수 |
float | 32비트 부동 소수점을 갖는 수 |
half_float | 16비트 부동 소수점을 갖는 수 |
Date 타입
Date 타입은 문자열로 처리 되며 포맷 설정이 가능하다.
기본 값은 yyyy-MM-ddTHH:mm:ssZ 이다.
Date 타입은 크게 3가지 형태를 제공하며 내부적으로 UTC 밀리 초 단위로 변환해 저장한다.
-
문자열이 포함된 형식
- 2018-04-20
- 2018/04.20
- 2018-04-20 10:55:00
- 2018/04/20 10:55:00
-
ISO_INSTANT 포맷의 날짜 형식
- 2018-04-10T10:50:00Z
-
밀리초
- 1524449145579
Range 타입
범위가 있는 데이터를 저장할 때 사용한다.
ex) 10 ~ 20 사이의 정수 라면 시작과 끝만 정의한다.
타입 | 설명 |
integer_range | 최솟값/최대값을 갖는 부호 있는 32비트 정수 범위 |
float_range | 부동 소수점을 갖는 32비트 실수 범위 |
long_range | 최솟값/최대값을 갖는 부호있는 64비트 정수의 범위 |
double_range | 부동 소수점 값을 갖는 64비트 실수 범위 |
date_range | 64비트 정수 형태의 밀리초로 표시되는 날짜값의 범위 |
ip_range | IPv4, IPv6 주소를 지원하는 IP 값 |
Boolean 타입
참과 거짓 논리 값을 가지는 데이터 타입이다.
해당 값을 문자열로 표현도 가능하다.
Geo-Point 타입
위도, 경로 등 위치 정보를 담은 데이터 저장 시 사용한다.
반경 내 쿼리, 위치 기반 집계, 정렬 등 에 사용이 가능하다.
IP 타입
IP 주소 데이터를 저장할 때 사용한다.
IPv4, IPv6 모두 지정이 가능하다.
Object 타입
내부에 데이터를 계층적으로 표현이 가능하다.
특정 키워드나 타입 없이 필드 값으로 문서의 구조를 입력하면 된다.
Nested 타입
Object 객체 배열을 독립적으로 색인하고 질의하는 데이터 타입이다.
객체를 배열 형태로 저장할 수 있다.
Array 데이터 타입 내부에 존재하는 검색은 모든 데이터를 기준으로 OR 검색이 된다. 다수의 조건을 모두 만족하는 검색이 되지 않는 문제를 해결하기 위해 Nested 타입이 고안되었다.
엘라스틱서치 분석기
엘라스틱서치는 루씬을 기반으로 구축된 텍스트 기반 검색엔진이며, 루씬이 제공하는 다양한 분석기를 그대로 사용하고 있다. (기본적으로 Standard Analyzer)
분석기를 이용해 텍스트를 분석하면 개별 텀으로 나뉘어 형태소 형태로 분석된다.
엘라스틱서치는 각각 다른 언어의 형태소를 분석할 수 있도록 언어별로 분석기를 제공한다.
역색인 구조
색인할 때 특정한 규칙과 흐름에 의 텍스트를 변경하는 과정을 분석(Analyze) 이라고 하고 해당 처리는 분석기(Analyzer)라는 모듈을 조합해서 이루어 진다.
https://github.com/ces518/TIL/blob/master/elasticsearch/역색인 구조.md
분석기의 구조
분석기의 기본적인 동작 프로세스는 다음과 같다.
- 문장을 특정 규칙에 의해 수정한다. (Character Filter)
- 수정한 문장을 개별 토큰으로 분리한다. (Tokenizer Filter)
- 개별 토큰을 특정한 규칙에 의해 변경한다. (Token Filter)
Character Filter
문장을 분석하기 전 특정 단어를 변경하거나, HTML 태그를 제거하는 등 전처리 역할을 한다.
Tokenizer FIlter
분석기 구성 시 하나만 사용이 가능하며, 텍스트를 어떻게 나눌 것인지 정의한다.
각 언어에 맞게 적절한 Tokenizer 를 사용 하면 된다.
Token Filter
불필요한 단어를 제거하거나, 영문 단어를 소문자로 변환하는 등 작업을 수행한다.
Token Filter는 여러 단계가 순차적으로 이루어지며 지정된 순서에 따라 검색의 질이 달라질 수 있다.
전체 분석 프로세스 과정
불필요한 단어를 제거하거나, 영문 단어를 소문자로 변환하는 등 작업을 수행한다.
Token Filter는 여러 단계가 순차적으로 이루어지며 지정된 순서에 따라 검색의 질이 달라질 수 있다.
_analyze API
분석기를 사용하기 위해 엘라스틱서치에서 _analyze API 를 제공한다.
분석기를 활용한 분석
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/_analyze -d '
{ "analyzer": "standard", "text": "채널개발파트" }
'
{"tokens":[{"token":"채널개발파트","start_offset":0,"end_offset":6,"type":"<HANGUL>","position":0}]}
필드를 활용한 분석
인덱스 매핑 시 필드에 분석기를 지정할 수 있는데, 해당 분석기를 사용해서 분석을 하고 싶다면 다음과 같이 사용이 가능하다.
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/movie_search/_analyze -d '
{ "field": "movieNm", "text": "채널개발파트" }
'
{"tokens":[{"token":"채널개발파트","start_offset":0,"end_offset":6,"type":"<HANGUL>","position":0}]}
색인과 검색시 분석기를 각각 설정
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer?pretty -d '
{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"analysis": {
"analyzer": {
"dev_lower_test_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [ "lowercase" ]
},
"dev_stop_test_analyzer": {
"type": "custom",
"tokenizer": "standard",
"fitler": [ "lowercase", "english_stop" ]
}
},
"filter": {
"english_stop": {
"type": "stop",
"stopwords": "_english"
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "text",
"analyzer": "dev_stop_test_analyzer",
"search_analyzer": "dev_lower_test_analyzer"
}
}
}
}
'
생성한 인덱스에 도큐먼트를 색인
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer/_doc/1 -d '
{
"title": "Harry Poter and the Chamber of Secrets"
}
'
색인 시 불용어 처리가 되어 [harry], [poter], [chamber], [secrets] 가 토큰화 된다.
검색 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer/_search?pretty -d '
{
"query": {
"query_string": {
"default_operator": "AND",
"query": "Chamber of Secrets"
}
}
}
'
검색어는 [chamber], [of], [secrets] 로 토큰화 된다.
대표적인 분석기
엘라스틱서치는 루씬에 존재하는 대부분의 분석기를 제공하는데, 그 중 대표적으로 많이 사용 되는 분석기를 살펴보자.
Standard Analyer
공백 혹은 특수 기호를 기준으로 토큰을 분리하고, 모든 문자를 소문자로 변경하는 필터를 사용한다.
Text 타입을 사용하면 기본 분석기는 Standard Analyzer
파라미터 명 | 설명 |
max_token_length | 최대 토큰 길이를 초과 할 경우 해당 length 간격으로 분할, default: 255 |
stopwords | 사전 정의된 불영어 사전을 사용, default: 사용하지 않음 |
stopwords_path | 불용어가 포함된 파일을 사용할 경우의 파일 경로 |
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer/_analyze?pretty -d '
{
"analyzer": "standard",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "<ALPHANUM>",
"position" : 4
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "<ALPHANUM>",
"position" : 5
},
{
"token" : "secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 6
}
]
}
Whitespace 분석기
공백 문자열을 기준으로 토큰을 분리하는 분석기
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer/_analyze?pretty -d '
{
"analyzer": "whitespace",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "word",
"position" : 0
},
{
"token" : "Poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "word",
"position" : 1
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "word",
"position" : 2
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "word",
"position" : 3
},
{
"token" : "Chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "word",
"position" : 4
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "word",
"position" : 5
},
{
"token" : "Secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "word",
"position" : 6
}
]
}
Keyword 분석기
전체 입력 문자열을 하나의 키워드 처럼 처리하여 토큰화 작업을 하지 않는다.
전처리 필터
엘라스틱서치의 분석기는 전처리 필터를 이용해 전처리 작업후 본격적인 토큰 작업을 수행한다.
전처리 필터는 활용도가 높지 않아, 기본 제공 종류도 많지 않다.
Html strip char 필터
문장에서 HTML을 제거하는 전처리 필터이다.
파라미터는 escaped_tags 가 있는데 특정 태그만 삭제하도록 설정이 가능하며, 기본 값 은 모든 태그를 제거한다.
테스트용 인덱스 생성
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_html_analyzer?pretty -d '
{
"settings": {
"analysis": {
"analyzer": {
"html_strip_analyzer": {
"tokenizer": "keyword",
"char_filter": [ "html_strip_char_filter" ]
}
},
"char_filter": {
"html_strip_char_filter": {
"type": "html_strip",
"escape_tags": [ "b" ]
}
}
}
}
}
'
분석 결과
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_html_analyzer/_analyze?pretty -d '
{
"analyzer": "html_strip_analyzer",
"text": "<span>Harry Poter</span> and the <b>Chamber</b> of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry Poter and the Chamber of Secrets",
"start_offset" : 6,
"end_offset" : 58,
"type" : "word",
"position" : 0
}
]
}
토크나이저 필터
토크나이저 필터는 분석기를 구성하는 가장 핵심 요소이다.
어떤 토크나이저를 사용하냐에 따라 분석기의 전체적인 성격이 결정된다.
Standard 토크나이저
일반적으로 사용하는 토크나이저이며, 대부분의 기회를 만나면 토큰화 한다.
옵션 파라미터는 max_token_length 가 있으며 최대 토큰 길이를 초과하는 경우 해당 간격으로 토큰을 분할한다 (기본값 255)
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_html_analyzer/_analyze?pretty -d '
{
"tokenizer": "standard",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "Poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "Chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "<ALPHANUM>",
"position" : 4
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "<ALPHANUM>",
"position" : 5
},
{
"token" : "Secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 6
}
]
}
Whitespace 토크나이저
공백을 만나면 토큰화 한다.
옵션 파라미터는 max_token_length 가 있으며 최대 토큰 길이를 초과하는 경우 해당 간격으로 토큰을 분할한다 (기본값 255)
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_html_analyzer/_analyze?pretty -d '
{
"tokenizer": "whitespace",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "word",
"position" : 0
},
{
"token" : "Poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "word",
"position" : 1
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "word",
"position" : 2
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "word",
"position" : 3
},
{
"token" : "Chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "word",
"position" : 4
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "word",
"position" : 5
},
{
"token" : "Secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "word",
"position" : 6
}
]
}
Ngram 토크나이저
Ngram은 기본적으로 한 글자씩 토큰화 하며 특정 문자를 지정할 수 있다.
특정 문자를 지정하면 해당 문자를 기준으로 토큰화 하며, 자동 완성을 만들 때 유용하게 쓰인다.
파라미터 명 | 설명 |
min_gram | Ngram 적용할 문자의 최소 길이를 나타냄 기본값 1 |
max_gram | Ngram 적용할 문자의 최대 길이를 나타냄 기본값 2 |
token_chars | 토큰에 포함할 문자열을 지정한다. letter (문자), digit(숫자), whitespace(공백), punctuation(구두점), symbol(특수기호) |
테스트용 인덱스 생성
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_ngram_analyzer?pretty -d '
{
"settings": {
"analysis": {
"analyzer": {
"ngram_analyzer": {
"tokenizer": "ngram_tokenizer"
}
},
"tokenizer": {
"ngram_tokenizer": {
"type": "ngram",
"min_gram": 3,
"max_gram": 3,
"token_chars": [ "letter" ]
}
}
}
}
}
'
3글자씩 짤라서 토큰을 생성하도록 매핑
분석 결과
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_ngram_analyzer/_analyze?pretty -d '
{
"tokenizer": "ngram_tokenizer",
"text": "Harry Poter and the Chamber of Secrets"
}
'
{
"tokens" : [
{
"token" : "Har",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 0
},
{
"token" : "arr",
"start_offset" : 1,
"end_offset" : 4,
"type" : "word",
"position" : 1
},
{
"token" : "rry",
"start_offset" : 2,
"end_offset" : 5,
"type" : "word",
"position" : 2
},
{
"token" : "Pot",
"start_offset" : 6,
"end_offset" : 9,
"type" : "word",
"position" : 3
},
{
"token" : "ote",
"start_offset" : 7,
"end_offset" : 10,
"type" : "word",
"position" : 4
},
{
"token" : "ter",
"start_offset" : 8,
"end_offset" : 11,
"type" : "word",
"position" : 5
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "word",
"position" : 6
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "word",
"position" : 7
},
{
"token" : "Cha",
"start_offset" : 20,
"end_offset" : 23,
"type" : "word",
"position" : 8
},
{
"token" : "ham",
"start_offset" : 21,
"end_offset" : 24,
"type" : "word",
"position" : 9
},
{
"token" : "amb",
"start_offset" : 22,
"end_offset" : 25,
"type" : "word",
"position" : 10
},
{
"token" : "mbe",
"start_offset" : 23,
"end_offset" : 26,
"type" : "word",
"position" : 11
},
{
"token" : "ber",
"start_offset" : 24,
"end_offset" : 27,
"type" : "word",
"position" : 12
},
{
"token" : "Sec",
"start_offset" : 31,
"end_offset" : 34,
"type" : "word",
"position" : 13
},
{
"token" : "ecr",
"start_offset" : 32,
"end_offset" : 35,
"type" : "word",
"position" : 14
},
{
"token" : "cre",
"start_offset" : 33,
"end_offset" : 36,
"type" : "word",
"position" : 15
},
{
"token" : "ret",
"start_offset" : 34,
"end_offset" : 37,
"type" : "word",
"position" : 16
},
{
"token" : "ets",
"start_offset" : 35,
"end_offset" : 38,
"type" : "word",
"position" : 17
}
]
}
Edge Ngram 토크나이저
문자의 목록을 지정하여 하나를 만날 때 마다 시작 부분을 고정시켜 단어를 자르는 토크나이저 이다.
자동완성 구현시 유용하다. 옵션은 Ngram 토크나이저와 동일하다.
테스트용 인덱스 생성
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_engram_analyzer?pretty -d '
{
"settings": {
"analysis": {
"analyzer": {
"edge_ngram_analyzer": {
"tokenizer": "edge_ngram_tokenizer"
}
},
"tokenizer": {
"edge_ngram_tokenizer": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10,
"token_chars": [ "letter" ]
}
}
}
}
}
'
분석 결과
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_engram_analyzer/_analyze?pretty -d '
{
"tokenizer": "edge_ngram_tokenizer",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Ha",
"start_offset" : 0,
"end_offset" : 2,
"type" : "word",
"position" : 0
},
{
"token" : "Har",
"start_offset" : 0,
"end_offset" : 3,
"type" : "word",
"position" : 1
},
{
"token" : "Harr",
"start_offset" : 0,
"end_offset" : 4,
"type" : "word",
"position" : 2
},
{
"token" : "Harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "word",
"position" : 3
},
{
"token" : "Po",
"start_offset" : 6,
"end_offset" : 8,
"type" : "word",
"position" : 4
},
{
"token" : "Pot",
"start_offset" : 6,
"end_offset" : 9,
"type" : "word",
"position" : 5
},
{
"token" : "Pote",
"start_offset" : 6,
"end_offset" : 10,
"type" : "word",
"position" : 6
},
{
"token" : "Poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "word",
"position" : 7
},
{
"token" : "an",
"start_offset" : 12,
"end_offset" : 14,
"type" : "word",
"position" : 8
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "word",
"position" : 9
},
{
"token" : "th",
"start_offset" : 16,
"end_offset" : 18,
"type" : "word",
"position" : 10
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "word",
"position" : 11
},
{
"token" : "Ch",
"start_offset" : 20,
"end_offset" : 22,
"type" : "word",
"position" : 12
},
{
"token" : "Cha",
"start_offset" : 20,
"end_offset" : 23,
"type" : "word",
"position" : 13
},
{
"token" : "Cham",
"start_offset" : 20,
"end_offset" : 24,
"type" : "word",
"position" : 14
},
{
"token" : "Chamb",
"start_offset" : 20,
"end_offset" : 25,
"type" : "word",
"position" : 15
},
{
"token" : "Chambe",
"start_offset" : 20,
"end_offset" : 26,
"type" : "word",
"position" : 16
},
{
"token" : "Chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "word",
"position" : 17
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "word",
"position" : 18
},
{
"token" : "Se",
"start_offset" : 31,
"end_offset" : 33,
"type" : "word",
"position" : 19
},
{
"token" : "Sec",
"start_offset" : 31,
"end_offset" : 34,
"type" : "word",
"position" : 20
},
{
"token" : "Secr",
"start_offset" : 31,
"end_offset" : 35,
"type" : "word",
"position" : 21
},
{
"token" : "Secre",
"start_offset" : 31,
"end_offset" : 36,
"type" : "word",
"position" : 22
},
{
"token" : "Secret",
"start_offset" : 31,
"end_offset" : 37,
"type" : "word",
"position" : 23
},
{
"token" : "Secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "word",
"position" : 24
}
]
}
Keyword 토크나이저
텍스트를 하나의 토큰으로 만든다.
옵션은 buffer_size가 있으며 텀을 버퍼로 읽어 들일 문자 수를 지정한다. (기본값은 256)
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_analyzer/_analyze?pretty -d '
{
"tokenizer": "keyword",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry Poter and the Chamber of Secrets",
"start_offset" : 0,
"end_offset" : 38,
"type" : "word",
"position" : 0
}
]
}
토큰 필터
토큰 필터 (Token Filter) 는 토크나이저에서 분리된 토큰 들을 변형/추가/삭제 시 사용한다.
토크나이저에 의해 토큰 들이 분리되어야 동작하기 때문에 독립적으로 사용이 불가능하다.
Ascii Folding 토큰 필터
아스키 코드에 해당하는 127개의 문자에 해당하지 않는 문자를 ASCII 요소로 변경한다.
테스트용 인덱스 생성
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_af_analyzer?pretty -d '
{
"settings": {
"analysis": {
"analyzer": {
"asciifolding_analyzer": {
"tokenizer": "standard",
"fitler": [ "standard", "asciifolding" ]
}
}
}
}
}
'
분석 결과
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_af_analyzer/_analyze?pretty -d '
{
"analyzer": "asciifolding_analyzer",
"text": "hello javacafe"
}
'
// 응답
{
"tokens" : [
{
"token" : "hello",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "javacafe",
"start_offset" : 6,
"end_offset" : 14,
"type" : "<ALPHANUM>",
"position" : 1
}
]
}
Lowercase 토큰 필터
토큰을 구성하는 전체 문자열을 소문자로 변환한다.
Uppercase 토큰 필터
토큰을 구성하는 전체 문자열을 대문자로 변환한다.
Stop 토큰 필터
불용어로 등록할 사전을 구축해서 사용할 필터를 의미한다.
등록한 단어는 인덱스 혹은 검색 대상에서 제외 한다.
파라미터 명 | 설명 |
stopwords | 불용어를 매핑에 직접 등록해서 사용 |
stopwords_path | 불용어 사전이 존재하는 경로를 지정한다. 엘라스틱서치 config 폴더 내부에 생성 |
ignore_case | true로 지정할 경우 모든 단어를 소문자로 변경해서 저장한다. 기본값은 false |
테스트용 인덱스 생성
curl -XPUT -H 'Content-Type: application/json' http://localhost:9200/dev_stop_analyzer?pretty -d '
{
"settings": {
"analysis": {
"analyzer": {
"stop_filter_analyzer": {
"tokenizer": "standard",
"fitler": [ "standard", "stop_filter" ]
}
},
"filter": {
"stop_filter": {
"type": "stop",
"stopwords": [ "and", "is", "the" ]
}
}
}
}
}
'
분석 결과
// 요청
curl -XPOST -H 'Content-Type: application/json' http://localhost:9200/dev_stop_analyzer/_analyze?pretty -d '
{
"analyzer": "stop_filter_analyzer",
"text": "Harry Poter and the Chamber of Secrets"
}
'
// 응답
{
"tokens" : [
{
"token" : "Harry",
"start_offset" : 0,
"end_offset" : 5,
"type" : "<ALPHANUM>",
"position" : 0
},
{
"token" : "Poter",
"start_offset" : 6,
"end_offset" : 11,
"type" : "<ALPHANUM>",
"position" : 1
},
{
"token" : "and",
"start_offset" : 12,
"end_offset" : 15,
"type" : "<ALPHANUM>",
"position" : 2
},
{
"token" : "the",
"start_offset" : 16,
"end_offset" : 19,
"type" : "<ALPHANUM>",
"position" : 3
},
{
"token" : "Chamber",
"start_offset" : 20,
"end_offset" : 27,
"type" : "<ALPHANUM>",
"position" : 4
},
{
"token" : "of",
"start_offset" : 28,
"end_offset" : 30,
"type" : "<ALPHANUM>",
"position" : 5
},
{
"token" : "Secrets",
"start_offset" : 31,
"end_offset" : 38,
"type" : "<ALPHANUM>",
"position" : 6
}
]
}
Stemmer 토큰 필터
Stemming 알고리즘을 사용해 토큰을 변형하는 필터이다.
옵션은 파라미터는 name이 있으며 english, light_english, minimal_english, possessive_english, poter2, lovins 등 다른 나라의 언어도 사용이 가능하지만 한글은 미지원 .. ㅠ
Stemming 이란 ?
형태론 및 정보 검색 분야에서 어형이 변형된 단어로부터 접사 등을 제거하고 그 단어의 어간을 분리해 내는 것을 의미한다. 여기서 어간은 반드시 어근과 같아야 할 필요는 없으며, 어근과 차이가 있더라도 관련이 있는 단어들이 일정하게 동일한 어간으로 맵핑되게 하는 것이 어간 추출의 목적이다. 1960년대부터 컴퓨터 과학 분야에서 다양한 어간 추출 관련 알고리즘들이 연구되어 왔다. 많은 웹 검색 엔진들은 동일한 어간을 가진 단어들을 동의어로 취급하는 방식으로 질의어 확장을 하여 검색 결과의 품질을 높인다.
Synonym 토큰 필터
동의어 처리가 가능한 필터이다.
ex) Harry → 해리로 변환이 가능하다.
파라미터 명 | 설명 |
synonyms | 동의어로 사용할 단어 등록 |
synonyms_path | 파일로 관리형 경우 config 하위에 생성 |
Trim 토큰 필터
앞뒤 공백을 제거하는 토큰 필터이다.
동의어 사전
동의어는 검색 기능을 풍부하게 할 수 있게 도와준다.
토큰 필터 중 Synonym 필터를 사용하면 동의어 처리가 가능하다.
동의 추가 방식은 크게 두가지가 있다.
- 매핑 설정 정보에 미리 파라미터로 등록
- 특정 파일을 별도로 생성해서 관리
실무에서 동의어 들을 모아둔 파일들을 동의어 사전 이라고 한다.
동의어 사전 생성
동의어 파일은 경로는 엘라스틱서치의 config 폴더 하위에 존재해야 한다.
동의어 추가
동의어를 추가할 때 쉼표 (,) 로 구분해 등록하는 방법
Elasticsearch, 엘라스틱서치
동의어 처리 기준은 앞서 동작한 필터 종류 및 작업에 영향을 받는다. 최신 버전에서는 대소문자를 구분하지 않고 동의어 처리가 된다.
동의어 치환
동의어 추가와 구분하기 위해 화살표(⇒) 로 표시한다.
동의어 사전은 실시간으로 적용되지 않기 때문에, 사용 중인 인덱스를 Reload 해야 한다. 주의할 점은 색인 시점에 사전 내용이 변경 되었다면 동의어 사전을 수정해도 적용되지 않는다.
Document API
엘라스틱서치는 인덱스 관리 목적으로 Document API를 제공한다.
대표적인 Document API
- Index API
- Get API
- Delete API
- Update API
- Bulk API
- Reindex API
문서를 생성할 때는 기본적으로 ID가 필요한데, 문서 생성 시 ID를 지정하지 않는다면 UUID를 사용해서 자동으로 생성한다.
엘라스틱서치의 색인 된 문서는 모두 버전 값을 가지고 있다.
Update API 사용 시 내부적으로 스냅샷을 생성해서 문서를 수정하고 다시 색인 한다.
이때 버전 정보가 다르다면 색인에 실패한다.
이전글 참고