본문 바로가기

Monitoring/Elasticsearch

07. Analyzer

 Elasticsearch 에 텍스트 입력시 필드를 인덱싱하고 Documents 화 할때
 
Lucene 엔진에 의해 텍스트가 분석되어 입력된다.
 
 
이때 텍스트를 분석하는 엔진을 Analyer 라고 한다.
 
Lucene 에서 제공하는 Analyer 는 하나의 Tokenizer 와 다수의 Filter 로 구성된다.
 
Filter 는 CharFilter 와 TokenFilter 의 두 가지가 있는데
 
CharFilter 는 입력된 문자열에서 불필요한 문자를 normalization 하기 위해
 
사용되며 TokenFilter 는 tokenizer 에 의해 분해된 token 에 대한 Filter 처리를 하게 된다.
 
 
기본적으로 CharFilter 에 의해 공백 콤마 등의 문자를 삭제하며
 
예로 문서의 유형별로 xml 일 경우 <..> 의 문자열들을 삭제하게 된다.
 
Tokenizer 는 CharFilter 와 비슷한 일을 하지만 입력 값이 CharFilter 가 
 
입력이 character stream 에 반해 Tokenizer 는 token stream 을 사용하게 된다.
 
 
처음 Elasticsearch 를 설치하면 standard analyzer 를 사용하게 되는데 여기에는 CharFilter 가 없다.
 
따라서 모든 값이 바로 Tokenizer 로 전달된다.
 
 
그리고 기본 설치된 Standard Tokenizer 는 단순히 whitespace 기준으로 토큰을 자른다.
 
다음으로 기본  Standard Token Filter 는 단순히 모든 대문자를 소문자로 바꾸는 역할만 한다.
 
 
 
Using Analyzer API
 
예를 들어 아래와 같이 default 로 적용되는
 
standard analyzer 의 Tokenizer 까지만 사용하여 결과를 조회시
 
파싱된 토큰 결과가 어떻게 나오는지 살펴보자.
 
POST _analyze
{
  "tokenizer": "standard",
  "text":"I'm in the mood for drinking semi-dry red wine!"
}
 
그 결과는 아래와 같은데 공백과 - 문자를 파싱하였고 대문자를 소문자로 바꿔 저장하지 않았다.
 
{
  "tokens" : [
    {
      "token" : "I'm",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "in",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "the",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "mood",
      "start_offset" : 11,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "for",
      "start_offset" : 16,
      "end_offset" : 19,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "drinking",
      "start_offset" : 20,
      "end_offset" : 28,
      "type" : "<ALPHANUM>",
      "position" : 5
    },
    {
      "token" : "semi",
      "start_offset" : 29,
      "end_offset" : 33,
      "type" : "<ALPHANUM>",
      "position" : 6
    },
    {
      "token" : "dry",
      "start_offset" : 34,
      "end_offset" : 37,
      "type" : "<ALPHANUM>",
      "position" : 7
    },
    {
      "token" : "red",
      "start_offset" : 38,
      "end_offset" : 41,
      "type" : "<ALPHANUM>",
      "position" : 8
    },
    {
      "token" : "wine",
      "start_offset" : 42,
      "end_offset" : 46,
      "type" : "<ALPHANUM>",
      "position" : 9
    }
  ]
}
 
이번에는 Tokenizer 까지 사용하는게 아니라 Analyzer 를 사용해보자.
 
POST _analyze
{
  "analyzer":"standard",
  "text":"I'm in the mood for drinking semi-dry red wine!"
}
 
이 경우 CharFilter 와 TokenFilter 를 다 적용하게 되므로 아래와 같이 영문자가 소문자로 바뀌게 된다.
 
{
  "tokens" : [
    {
      "token" : "i'm",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "in",
      "start_offset" : 4,
      "end_offset" : 6,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "the",
      "start_offset" : 7,
      "end_offset" : 10,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "mood",
      "start_offset" : 11,
      "end_offset" : 15,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "for",
      "start_offset" : 16,
      "end_offset" : 19,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "drinking",
      "start_offset" : 20,
      "end_offset" : 28,
      "type" : "<ALPHANUM>",
      "position" : 5
    },
    {
      "token" : "semi",
      "start_offset" : 29,
      "end_offset" : 33,
      "type" : "<ALPHANUM>",
      "position" : 6
    },
    {
      "token" : "dry",
      "start_offset" : 34,
      "end_offset" : 37,
      "type" : "<ALPHANUM>",
      "position" : 7
    },
    {
      "token" : "red",
      "start_offset" : 38,
      "end_offset" : 41,
      "type" : "<ALPHANUM>",
      "position" : 8
    },
    {
      "token" : "wine",
      "start_offset" : 42,
      "end_offset" : 46,
      "type" : "<ALPHANUM>",
      "position" : 9
    }
  ]
}
 
 
 
Index
 
Elasticsearch 에서 위처럼 Analyzer 를 사용하여 Token 을 빼고나면
 
이를 이용해서 Indexing 을 한다.
 
 
그래서 검색시 해당 Token 이 Docuemnt 에 포함되었는지 쉽게 찾을 수 있는 것이다.
 
 
 
 
CharFilter
 
Elasticsearch 는 기본적으로 3가지 CharFilter 를 제공한다.
 
 
HTML Strip Character Filter (html_strip)
 
 
 
Mapping Character Filter(mapping)
 
Im so _sad_ 
 
Im so :(
 
 
 
Pattern Replace Filter (pattern_replace)
 
Pattern: ([a-zA-Z0-9]+)(-?)
Replacement:$1
 
aaa-bbb-ccc
aaabbbccc
 
 
 
 
Tokenizer
 
 

Mapping Char Filter | Elasticsearch Reference [7.5] | Elastic

The mapping character filter accepts a map of keys and values. Whenever it encounters a string of characters that is the same as a key, it replaces them with the value associated with that key. Matching is greedy; the longest pattern matching at a given po

www.elastic.co

 

Mapping Char Filter | Elasticsearch Reference [7.5] | Elastic

The mapping character filter accepts a map of keys and values. Whenever it encounters a string of characters that is the same as a key, it replaces them with the value associated with that key. Matching is greedy; the longest pattern matching at a given po

www.elastic.co

 

Mapping Char Filter | Elasticsearch Reference [7.5] | Elastic

The mapping character filter accepts a map of keys and values. Whenever it encounters a string of characters that is the same as a key, it replaces them with the value associated with that key. Matching is greedy; the longest pattern matching at a given po

www.elastic.co

 

Mapping Char Filter | Elasticsearch Reference [7.5] | Elastic

The mapping character filter accepts a map of keys and values. Whenever it encounters a string of characters that is the same as a key, it replaces them with the value associated with that key. Matching is greedy; the longest pattern matching at a given po

www.elastic.co

Tokenizer 는 세 가지 범주로 분류된다.
 
 
Word Oriented Tokenizers
 
단순히 단어 수준에서 작동하기 때문에 출력을 사람이 쉽게 읽을 수 있다.
 
일반적으로 full text 를 tokenizing 하는데 사용한다.
 
 
Standard Tokenizer (standard)
 
공백 기반으로 Tokening 하며 특수분자는 제외된다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [I'm,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
 
Letter Tokenizer (letter)
 
char 가 아닌 문자 기반으로 Tokening 한다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [I,m,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
 
Lowercase Tokenizer (lowercase)
 
Letter Tokenizer + 소문자 변경이다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i,m,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
 
Whitespace Tokenizer (whitespace)
 
정확하게 공백으로만 Tokening 된다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [I'm,in,the,mood,for,drinking,semi,dry,red,wine!]
 
 
 
UAX URL Email Tokenizer (uax_url_email)
 
 
 
 
Partial Word Tokenizers
 
부분 문자열 기반으로 Tokening 한다.
 
 
N-Gram Tokenizer (ngram)
 
모든 부분 문자열들로 Tokening 한다.
 
"Red wine"
=>[Re,Red,ed,wi,win,wine,in,ine,ne]
 
 
Edge N-Gram Tokenizer (edge_ngram)
 
첫 문자를 포함하는 부분 문자열들로 Tokening 한다.
 
보통 검색에서 단어를 suggest 시 사용하게 된다.
 
"Red wine"
=>[Re,Red,,wi,win,wine]
 
 
 
Structured Text Tokenizers
 
e-mail, zip-code, ID 등의 정형화된 Text 를 Tokening 할 때 사용한다.
 
 
Keyword Tokenizer (keyword)
 
이상하게 들리겠지만 입력을 받아 키워드로 저장한다.
 
도시 이름인 New Yock 처럼 특정 키워드가 의미가 있는 경우 사용한다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [I'm in the mood for drinking semi dry red wine!]
 
 
 
Pattern Tokenizer (pattern)
 
 분할 텍스트 정규 표현식 기반으로 Tokening 한다. 
 
기본 패턴은 \W+단어가 아닌 문자를 만날 때마다 텍스트를 분할하는 패턴이다.
 
"I, like, red, wine!"
=>[I,like,red,wine!]
 
 
 
 
TokenFilter
 
Text 를 Token 으로 분리하는 필터이다
 
 
Standard Token Filter (standard)
 
기본적으로 아무것도 하지 않는다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'm,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
Lowercase Token Filter (lowercase)
 
입력된 Token 입력을 소문자로 변경하는 Token  필터이다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [i'm,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
Uppercase Token Filter (lowercase)
 
입력된 Token 입력을 대문자로 변경하는 Token  필터이다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'M,IN,THE,MOOD,FOR,DRINKING,SEMI,DRY,RED,WINE]
 
 
NGram Token Filter (nGram)
 
입력된 Token 입력을 nGram 으로 변경하는 Token 필터이다.
 
[Red,wine]
=>[Re,Red,ed,wi,win,wine,in,ine,ne]
 
 
Edge NGram Token Filter (edgeNGram)
 
입력된 Token 입력을 Edge nGram 으로 변경하는 Token 필터이다.
 
[Red,wine]
=>[Re,Red,wi,win,wine]
 
 
Stop Token Filter (stop)
 
의미있는 단어 ( stop word ) 기반의 Token 필터이다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'm,mood,drinking,semi,dry,red,wine]
 
 
Word Delimiter Token Filter (word-delimiter)
 
대문자 및 영문자가 아닌 부분을 나누며 숫자도 분리되는
 
Token 필터로 한개 이상의 char 를 가진 것만 결과로 나오게 된다.
 
[Wi-Fi,PowerShell,CE1000,Andy's]
=> [Wi,Fi,Power,Shell,CE,1000,Andy]
 
 
Stemmer Token Filter (stemmer)
 
Stem word 기반으로 분리하는 Token 필터이다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'm,mood,drink,semi,dry,red,wine]
 
 
Keyword Marker Token Filter (keyword_marker)
 
Stem word 기반으로 분리하는 Token 필터이지만 Keyword 는 분리하지 않는다.
 
Protected terms:[drinking]
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'm,mood,drinking,semi,dry,red,wine]
 
 
Snowball Token Filter (snowball)
 
Stem word 기반으로 분리하는 Token 필터인데 Snawball 이라는 알고리즘 기반이다.
 
[I'm,in,the,mood,for,drinking,semi,dry,red,wine]
=> [I'm,mood,drink,semi,dry,red,wine]
 
 
Synonym Token Filter (synonym)
 
동의어를 넣어주는 Token 필터이다.
 
[I,am,very,happy]
=> [I,am,very,happy/delighted]
 
 
 
Analyzer
 
앞서 다룬 Filter 와 Tokenizer 를 포함하고 있는 컴포넌트이다.
 
 
Standard Analyzer (standard)
 
기본적으로 텍스트를 단어 경계로 단어를 분리하고 구두점을 제거하고 소문자로 변경한다.
 
옵션으로 stop words 를 제거할 수 있다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i,m,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
Simple Analyzer (simple)
 
기본적으로 텍스트를 단어 경계로 단어를 분리하고 구두점을 제거하고 소문자로 변경한다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i,m,in,the,mood,for,drinking,semi,dry,red,wine]
 
 
Stop Analyzer
 
Simple Analyzer + Stop Word 제거
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i,m,,mood,drinking,semi,dry,red,wine]
 
 
Language Analyzers (english, ...)
 
각 언어별 형태소 분석된 기반으로 Analysis 한다.
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i'm,mood,drink,semi,dry,red,wine]
 
 
Keyword Analyzers (keyword)
 
" I'm in the mood for drinking semi-dry red wine! "
=> [i'm in the mood for drinking semi-dry red wine!]
 
 
Pattern Analyzers (pattern)
 
"I, like, red, wine! "
=> [I,like,red,wine!]
 
 
 
Creating Custom Analyzer 
 
아래와 같이 my_custom_analyzer 를 Index 생성시 설정하였다.
 
char_filter 로 happy 와 sad 를 🙂 와 😞 로 설정하였고
 
tokenizer 로 특수문자를 제거하였고
 
token_filter 로 stop 토큰 필터를 넣었다. ( 의미없는 문자 and ,a , the 삭제) 
 
PUT my_index
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_custom_analyzer": {
          "type": "custom",
          "char_filter": [
            "emoticons"
          ],
          "tokenizer": "punctuation",
          "filter": [
            "lowercase",
            "english_stop"
          ]
        }
      },
      "tokenizer": {
        "punctuation": {
          "type": "pattern",
          "pattern": "[ .,!?]"
        }
      },
      "char_filter": {
        "emoticons": {
          "type": "mapping",
          "mappings": [
            ":) => _happy_",
            ":( => _sad_"
          ]
        }
      },
      "filter": {
        "english_stop": {
          "type": "stop",
          "stopwords": "_english_"
        }
      }
    }
  }
}
 
그리고 사용한 결과는 아래와 같다.
 
 POST my_index/_analyze
{
  "text":     "I'm a :) person, and you?"
}
{
  "tokens" : [
    {
      "token" : "i'm",
      "start_offset" : 0,
      "end_offset" : 3,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "_happy_",
      "start_offset" : 6,
      "end_offset" : 8,
      "type" : "word",
      "position" : 2
    },
    {
      "token" : "person",
      "start_offset" : 9,
      "end_offset" : 15,
      "type" : "word",
      "position" : 3
    },
    {
      "token" : "you",
      "start_offset" : 21,
      "end_offset" : 24,
      "type" : "word",
      "position" : 5
    }
  ]
}
 
 
 
 
Using Analyzer in mappings
 
위의 my_index 에 Document 삽입 전 Mapping 단계에서 사용할 Analyzer 를 지정하고 
 
실제로 Document 삽입후 변경된 문자열이 잘 검색되는지 확인해 보자. 
 
PUT /my_index/default/_mapping
{
  "properties": {
    "description": {
      "type":"text",
      "analyzer":"my_custom_analyzer"
    },
    "teaser":{
      "type":"text",
      "analyzer": "standard"
    }
  }
}
 
POST /my_index/default/1
{
  "description":":)",
  "teaser":":)"
}
 
GET /my_index/default/_search
{
  "query": {
    "match": {
      "description":"_happy_"
    }
  }
}
{
  "took" : 17,
  "timed_out" : false,
  "_shards" : {
    "total" : 5,
    "successful" : 5,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : 1,
    "max_score" : 0.2876821,
    "hits" : [
      {
        "_index" : "my_index",
        "_type" : "default",
        "_id" : "1",
        "_score" : 0.2876821,
        "_source" : {
          "description" : ":) drinking :(",
          "teaser" : ":) drinking :("
        }
      }
    ]
  }
}
 
위와 같이  "description":"_happy_" 인 Document 찾는 쿼리를 실행하였는데
 
검색이 되는 것을 확인할 수 있다. 
 
 
 
Adding Analyzer in an existing index
 
기존 Index 가 있을 때 Analyzer 를 변경하고자 하면 _close / _open API 를 사용하면 된다.
 
POST /my_index/_close
 
PUT /my_index/_settings
{
  "analysis": {
    "analyzer": {
      "french_stop":{
        "type":"standard",
        "stopwords":"_french_"
      }
    }
  }
}
 
POST /my_index/_open
 
 

'Monitoring > Elasticsearch' 카테고리의 다른 글

09. Compound Query  (0) 2020.01.17
08. Query  (0) 2020.01.17
06. Mapping  (0) 2020.01.17
05. Document  (0) 2020.01.17
04. Cluster  (0) 2020.01.17