본문 바로가기

Elasticsearch (ELK)/Elasticsearch

7-5. 데이터 색인과 텍스트 분석(실전) - Token Filter

토크나이저를 이용한 텀 분리 과정 이후에는 분리된 각각의 텀 들을 지정한 규칙에 따라 처리를 해 주는데 이 과정을 담당하는 것이 토큰 필터이다. 특징은 :

  • 배열 값으로 저장해야 된다(하나만 사용하더라도)
  • 나열된 순서대로 처리되기 때문에 순서를 잘 골라야 한다.

1. Lowercase, Uppercase

대문자 소문자

GET _analyze
{
  "filter": [ "lowercase" ],
  "text": [ "Harry Potter and the Philosopher's Stone" ]
}

GET _analyze
{
  "filter": [ "uppercase" ],
  "text": [ "Harry Potter and the Philosopher's Stone" ]
}

 

2. Stop 

불용어(stopword)를 걸러낸다 (사용자임의로 불용어를 만들어 적용가능)

또한 "_english_", "_german_" 같이 언어를 지정해서 해당 언어팩에 있는 불용어를 지정할 수도 있다.

// my_stop 인덱스에 my_stop_filter 토큰필터 생성
PUT my_stop
{
  "settings": {
    "analysis": {
      "filter": {
        "my_stop_filter": {
          "type": "stop",
          "stopwords": [
            "in",
            "the",
            "days"
          ]
        }
      }
    }
  }
}

// my_stop_filter 토큰 필터로 문장 분석
GET my_stop/_analyze
{
  "tokenizer": "whitespace",
  "filter": [
    "lowercase",
    "my_stop_filter"
  ],
  "text": [ "Around the World in Eighty Days" ]
}

 

파일을 import하여 적용도 가능하다

// 파일 생성
$ mkdir config/user_dic
$ echo 'in
the
eighty' > config/user_dic/my_stop_dic.txt

// stopwords_path 설정을 가진 my_stop_filter 토큰필터 생성
PUT my_stop
{
  "settings": {
    "analysis": {
      "filter": {
        "my_stop_filter": {
          "type": "stop",
          "stopwords_path": "user_dic/my_stop_dic.txt"
        }
      }
    }
  }
}

// my_stop_filter 토큰 필터로 문장 분석
GET my_stop/_analyze
{
  "tokenizer": "whitespace",
  "filter": [
    "lowercase",
    "my_stop_filter"
  ],
  "text": [ "Around the World in Eighty Days" ]
}

 

3. Synonym

지정된 텀으로 저장 ("A, B => C")

왼쪽의 A, B 대신 오른쪽의 C 텀을 저장합니다. A, B 로는 C 의 검색이 가능하지만 C 로는 A, B 가 검색되지 않습니다.

// "amazon => aws" 동의어를 지정하는 my_synonym 인덱스 생성

PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon => aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}

// term 쿼리로 amazon 검색
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "aws"
    }
  }
}
// 2개의 결과 검색

// 도큐먼트 2개 입력
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }

// term 쿼리로 amazon 검색
GET my_synonym/_search
{
  "query": {
    "term": {
      "message": "amazon"
    }
  }
}
// 결과 없음

// match 쿼리로 amazon 검색
GET GET my_synonym/_search
{
  "query": {
    "match": {
      "message": "amazon"
    }
  }
}
// 2개의 결과 검색 aws term 쿼리와 결과가 같음

각 텀을 저장 ("A, B")

A, B 각 텀이 A 와 B 두개의 텀을 모두 저장합니다. A 와 B 모두 서로의 검색어로 검색이 됩니다.

// "amazon, aws" 동의어를 지정하는 my_synonym 인덱스 생성
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms": [
            "amazon, aws"
          ]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}

// AWS, Amazon Web Service 도큐먼트 저장
PUT my_synonym/_doc/1
{ "message" : "Amazon Web Service" }
PUT my_synonym/_doc/2
{ "message" : "AWS" }

// 결과 확인 
GET my_synonym/_termvectors/1?fields=message
GET my_synonym/_termvectors/2?fields=message
// 도큐먼트 1,2 양쪽에 AWS 와 AMAZON 두개의 인덱스가 생성된것이 확인된다.

 

파일로 관리 (동의어는 하나의 규칙당 한 줄씩 입력해야 하며 파일은 UTF-8로 인코딩 되어야 합니다.)

 

// config/user_dic 디렉토리 아래에 my_syn_dic.txt 파일 생성
$ echo 'quick, fast
hop, jump' > config/user_dic/my_syn_dic.txt

// synonyms_path 설정을 가진 my_synonym 인덱스 생성
PUT my_synonym
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_syn": {
          "tokenizer": "whitespace",
          "filter": [
            "lowercase",
            "syn_aws"
          ]
        }
      },
      "filter": {
        "syn_aws": {
          "type": "synonym",
          "synonyms_path": "user_dic/my_syn_dic.txt"
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "message": {
        "type": "text",
        "analyzer": "my_syn"
      }
    }
  }
}

 

  • expand (true / false. 디폴트는 true)

    "expand": false 로 설정하게 되면 "synonyms": "aws, amazon" 같은 설정에 토큰들을 모두 저장하지 않고 맨 처음에 명시된 토큰 하나만 저장합니다. 앞의 설정은 "synonyms": "aws, amazon => aws" 로 설정한 것과 동일하게 동작합니다.

  • lenient (true / false. 디폴트는 false)

    "lenient": true 로 설정하면 synonym 설정에 오류가 있는 경우 오류가 있는 부분을 무시하고 실행합니다.

 

4. NGram, Edge NGram, Shingle

과학용어집과 같은 경우 단어의 일부만 가지고도 검색해야 하는 기능이 필요하다.

Wildcard나 Regex를 사용해도 되지만 메모리 소모가 많고 느리기 때문에 사용하지 않기를 권고한다.

 

NGram

저장되는 텀의 갯수가 기하급수적으로 늘어나며 잘못된 검색결과를 초래할수 있으므로 보통 사용안함

 

 

ngram 토큰 필터에는 min_gram (디폴트 1), max_gram (디폴트 2) 옵션이 있습니다. 짐작할 수 있듯이 최소, 최대 문자수의 토큰을 구분하는 단위입니다. house"min_gram": 2, "max_gram": 3 으로 설정하면 다음과 같이 분석되어 총 7개의 토큰을 저장합니다.

 

// my_ngram 인덱스 생성
PUT my_ngram
{
  "settings": {
    "analysis": {
      "filter": {
        "my_ngram_f": {
          "type": "nGram",
          "min_gram": 2,
          "max_gram": 3
        }
      }
    }
  }
}

// my_ngram_f 토큰필터로 "house" 분석
GET my_ngram/_analyze
{
  "tokenizer": "keyword",
  "filter": [
    "my_ngram_f"
  ],
  "text": "house"
}

 

Edge NGram

상위 예제에서 "type": "edgeNGram" 만 변경하면 된다.

edgeNGram의 옵션을 "min_gram": 1, "max_gram": 4 으로 설정하고 "house" 를 분석하면 다음과 같이 "h", "ho", "hou", "hous" 4개의 토큰이 생성됩니다.

 

Shingle

NGramEdge NGram은 모두 하나의 단어로부터 토큰을 확장하는 토큰 필터입니다. 문자가 아니라 단어 단위로 구성된 묶음을 Shingle 이라고 하며 "type": "shingle" 토큰 필터의 이용이 가능합니다. "this is my sweet home" 라는 문장을 분리해서 2 단어씩 Shingle 토큰 필터를 적용하면 다음과 같은 4개의 shingle 들이 생성됩니다.

 

 

 

  • min_shingle_size / max_shingle_size : shingle의 최소 / 최대 단어 개수를 지정합니다. 디폴트는 모두 2 입니다.

  • output_unigrams : Shingle 외에도 각각의 개별 토큰(unigram)도 저장 하는지의 여부를 설정합니다. 디폴트는 true 입니다.

  • output_unigrams_if_no_shingles : shingle 을 만들 수 없는 경우에만 개별 토큰을 저장하는지의 여부를 설정합니다. 디폴트는 false 입니다.

  • token_separator : 토큰 구분자를 지정합니다. 디폴트는 " " (스페이스) 입니다.

  • filler_token : shing을 만들 텀이 없는 경우 (보통은 stop 토큰 필터와 함께 사용되어 offset 위치만 있고 텀이 없는 경우입니다) 대체할 텍스트를 지정합니다. 디폴트는 _ 입니다.

// my_shingle_f 토큰필터로 "this is my sweet home" 분석
GET my_shingle/_analyze
{
  "tokenizer": "whitespace",
  "filter": [
    "my_shingle_f"
  ],
  "text": "this is my sweet home"
} 

 

5. Unique

"white fox, white rabbit, white bear" 같은 문장을 분석하면 "white" 텀은 총 3번 저장이 됩니다. 역 색인에는 텀이 1개만 있어도 텀을 포함하는 도큐먼트를 가져올 수 있기 때문에 중복되는 텀 들은 삭제 해 주어도 검색에는 보통 무방합니다. 이 경우 unique 토큰 필터를 사용해서 중복되는 텀 들은 하나만 저장하도록 할 수 있습니다.

다음은 "white fox, white rabbit, white bear" 문장을 unique 토큰 필터를 적용하지 않은 경우와 적용한 경우를 비교 한 예제입니다.

 

// 일반적인 "white fox, white rabbit, white bear" 문장 분석
GET _analyze
{
  "tokenizer": "standard",
  "filter": [
    "lowercase"
  ],
  "text": [
    "white fox, white rabbit, white bear"
  ]
}

일반적인 "white fox, white rabbit, white bear" 문장 분석 결과
{
  "tokens" : [
    {
      "token" : "white",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "fox",
      "start_offset" : 6,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "white",
      "start_offset" : 11,
      "end_offset" : 16,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "rabbit",
      "start_offset" : 17,
      "end_offset" : 23,
      "type" : "<ALPHANUM>",
      "position" : 3
    },
    {
      "token" : "white",
      "start_offset" : 25,
      "end_offset" : 30,
      "type" : "<ALPHANUM>",
      "position" : 4
    },
    {
      "token" : "bear",
      "start_offset" : 31,
      "end_offset" : 35,
      "type" : "<ALPHANUM>",
      "position" : 5
    }
  ]
}

 

 

// unique 토크나이저로 "white fox, white rabbit, white bear" 문장 분석
GET _analyze
{
  "tokenizer": "standard",
  "filter": [
    "lowercase",
    "unique"
  ],
  "text": [
    "white fox, white rabbit, white bear"
  ]
}

// unique 토크나이저로 "white fox, white rabbit, white bear" 문장 분석 결과
{
  "tokens" : [
    {
      "token" : "white",
      "start_offset" : 0,
      "end_offset" : 5,
      "type" : "<ALPHANUM>",
      "position" : 0
    },
    {
      "token" : "fox",
      "start_offset" : 6,
      "end_offset" : 9,
      "type" : "<ALPHANUM>",
      "position" : 1
    },
    {
      "token" : "rabbit",
      "start_offset" : 17,
      "end_offset" : 23,
      "type" : "<ALPHANUM>",
      "position" : 2
    },
    {
      "token" : "bear",
      "start_offset" : 31,
      "end_offset" : 35,
      "type" : "<ALPHANUM>",
      "position" : 3
    }
  ]
}​

 

match 쿼리를 사용해서 검색하는 경우 unique 토큰 필터를 적용한 필드는 텀의 개수가 1개로 되기 때문에
TF(Term Frequency)
값이 줄어들어 스코어 점수가 달라질 수 있습니다. match 쿼리를 이용해
정확도(relevancy)
를 따져야 하는 검색의 경우에는 unique 토큰 필터는 사용하지 않는 것이 바람직합니다