5 분 소요

 안녕하세요 마개입니다.
Elasticsearch에서 데이터를 인덱싱할 때 Tokenizer를 통해 Token화된 이후에 필터링이 필요할 때 사용하는 것이 Filter입니다. Settings를 통해 filter를 지정할 수 있는데 몇 가지 filter에 대해 알아봅니다.


image



공식 문서(version 7.10)에 있는 자료를 바탕으로 정리하였습니다.



Stop

stop 필터의 경우 Token에서 불용어를 제거하는 역할을 합니다. 커스텀마이징을 하지 않는다면 기본적으로 영어의 불용어를 제거합니다.
영어의 불용어는 다음과 같습니다.
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


예시

1
2
3
4
5
6
GET /_analyze
{
  "tokenizer": "standard",
  "filter": [ "stop" ],
  "text": "a quick fox jumps over the lazy dog"
}

해당 필터를 통해 결과는 아래와 같이 나옵니다.


1
[ quick, fox, jumps, over, lazy, dog ]

영어의 불용어인 a, the가 빠진 것을 보실 수 있습니다.


Analyzer에 추가

인덱스를 생성할 때에 filter를 지정할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "my_analyzer": {
          "tokenizer": "whitespace",
          "filter": [ "stop" ]
        }
      }
    }
  }
}

filter를 지정할 때 설정할 수 있는 파라미터들이 있습니다.

  • stopwords (옵션, string) : 불용어를 적용할 언어를 선택합니다. _arabic_, _thai_등. 기본값은 _english_ 입니다. 각 언어는 루씬에서 사전 적용된 불용어 리스트를 이용합니다. 루씬에서 제공하는 언어들을 여기에서 확인하실 수 있습니다.
  • stopwords_path (옵션, string) : 불용어 리스트가 담겨있는 파일의 경로를 작성합니다. 해당 경로는 절대 경로 또는 config 위치에서의 상대 경로로 작성해야 합니다. 파일은 UTF-8 로 인코딩 되어있고 각 단어는 줄바꿈으로 구분해야 합니다.
  • ignore_case (옵션, Boolean) : true 로 설정하면 불용어는 대소문자를 가리지 않습니다. 기본 값은 false 입니다.
  • remove_trailing (옵션, Boolean) : true 로 설정하면 마지막 token이 불용어이면 삭제됩니다. 기본 값은 true 입니다.


커스텀마이징

새로운 token filter를 만들어 stop 필터를 커스텀마이징합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "tokenizer": "whitespace",
          "filter": [ "my_custom_stop_words_filter" ]
        }
      },
      "filter": {
        "my_custom_stop_words_filter": {
          "type": "stop",
          "ignore_case": true
        }
      }
    }
  }
}

기본적인 형태는 위와 같이 설정할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PUT /my-index-000001
{
  "settings": {
    "analysis": {
      "analyzer": {
        "default": {
          "tokenizer": "whitespace",
          "filter": [ "my_custom_stop_words_filter" ]
        }
      },
      "filter": {
        "my_custom_stop_words_filter": {
          "type": "stop",
          "ignore_case": true,
          "stopwords": [ "and", "is", "the" ]
        }
      }
    }
  }
}

또한 불용어를 stopwords로 직접 지정해서 사용할 수도 있습니다.



Synonym

synonym 필터의 경우 분석 과정에서 동의어를 다룰 때 사용합니다.


예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "synonym": {
            "tokenizer": "whitespace",
            "filter": [ "synonym" ]
          }
        },
        "filter": {
          "synonym": {
            "type": "synonym",
            "synonyms_path": "analysis/synonym.txt"
          }
        }
      }
    }
  }
}

위의 상황에서는 synonym 필터를 구성하는 것인데 analysis/synonym.txtconfig 에서 상대 경로로 지정합니다. 추가적인 세팅은 다음과 같습니다.

  • expand : 기본값은 true 입니다. synonym이 여러개 지정되어 있는데 expand가 false이면 맨 처음 단어만 적용이 됩니다..
  • lenient (기본값 - false) : true로 설정하면 동의어를 설정하는 과정에서 예외를 무시합니다. 파싱할 수 없는 규칙에 대해서는 무시해야하기 때문에 중요합니다. false인데 규칙을 무시한 형태가 있으면 에러를 발생합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "synonym": {
            "tokenizer": "standard",
            "filter": [ "my_stop", "synonym" ]
          }
        },
        "filter": {
          "my_stop": {
            "type": "stop",
            "stopwords": [ "bar" ]
          },
          "synonym": {
            "type": "synonym",
            "lenient": true,
            "synonyms": [ "foo, bar => baz" ]
          }
        }
      }
    }
  }
}

위의 예에서는 bar는 무시되고 foo => baz는 유지됩니다.


synonym 작성 방법

2가지의 포맷으로 synonym을 작성할 수 있습니다.



Solr synonyms

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# Blank lines and lines starting with pound are comments.

# Explicit mappings match any token sequence on the LHS of "=>"
# and replace with all alternatives on the RHS.  These types of mappings
# ignore the expand parameter in the schema.
# Examples:
i-pod, i pod => ipod
sea biscuit, sea biscit => seabiscuit

# Equivalent synonyms may be separated with commas and give
# no explicit mapping.  In this case the mapping behavior will
# be taken from the expand parameter in the schema.  This allows
# the same synonym file to be used in different synonym handling strategies.
# Examples:
ipod, i-pod, i pod
foozball , foosball
universe , cosmos
lol, laughing out loud

# If expand==true, "ipod, i-pod, i pod" is equivalent
# to the explicit mapping:
ipod, i-pod, i pod => ipod, i-pod, i pod
# If expand==false, "ipod, i-pod, i pod" is equivalent
# to the explicit mapping:
ipod, i-pod, i pod => ipod

# Multiple synonym mapping entries are merged.
foo => foo bar
foo => baz
# is equivalent to
foo => foo bar, baz

Solr 작성 방법은 위와 같습니다. 이렇게 만든 파일은 synonyms_path를 통해 경로를 지정할 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "filter": {
          "synonym": {
            "type": "synonym",
            "synonyms": [
              "i-pod, i pod => ipod",
              "universe, cosmos"
            ]
          }
        }
      }
    }
  }
}

리스트가 적을 경우에는 synonyms에 직접 지정하는 것도 하나의 방법입니다.



WordNet synonyms

WordNet 기반 동의어들은 format을 이용하여 정의할 수 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
PUT /test_index
{
  "settings": {
    "index": {
      "analysis": {
        "filter": {
          "synonym": {
            "type": "synonym",
            "format": "wordnet",
            "synonyms": [
              "s(100000001,1,'abstain',v,1,0).",
              "s(100000001,2,'refrain',v,1,0).",
              "s(100000001,3,'desist',v,1,0)."
            ]
          }
        }
      }
    }
  }
}



nori_part_of_speech

nori_part_of_speech는 nori에서 제공하는 filter 중 하나로 품사 태그들에 매칭되는 token을 제거합니다. 제공되는 품사들의 종류는 여기에서 확인 가능합니다.
해당 필터에서는 아래와 같은 설정을 해야 합니다.

  • stoptags : 제거되어야 하는 품사 배열을 설정합니다.

기본적으로 stoptags는 다음과 같이 설정됩니다.

1
2
3
4
5
6
7
8
9
"stoptags": [
    "E",
    "IC",
    "J",
    "MAG", "MAJ", "MM",
    "SP", "SSC", "SSO", "SC", "SE",
    "XPN", "XSA", "XSN", "XSV",
    "UNA", "NA", "VSV"
]


예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
PUT nori_sample
{
  "settings": {
    "index": {
      "analysis": {
        "analyzer": {
          "my_analyzer": {
            "tokenizer": "nori_tokenizer",
            "filter": [
              "my_posfilter"
            ]
          }
        },
        "filter": {
          "my_posfilter": {
            "type": "nori_part_of_speech",
            "stoptags": [
              "NR"   
            ]
          }
        }
      }
    }
  }
}

GET nori_sample/_analyze
{
  "analyzer": "my_analyzer",
  "text": "여섯 용이"  
}

위의 예시에는 숫자를 의미하는 품사인 NR에 해당되는 단어들이 삭제됩니다.


결과

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
  "tokens" : [ {
    "token" : "용",
    "start_offset" : 3,
    "end_offset" : 4,
    "type" : "word",
    "position" : 1
  }, {
    "token" : "이",
    "start_offset" : 4,
    "end_offset" : 5,
    "type" : "word",
    "position" : 2
  } ]
}