[ELK] discard_punctuation

홍종훈·2024년 6월 22일
0

ELK

목록 보기
4/4
post-thumbnail

1. 문제상황(기존 세팅)

    "nori_user_tokenizer": {
      "type": "nori_tokenizer",
      "user_dictionary_rules": ["1+1", "(G)I-DLE", "(G)IDLE", "{G}IDLE", "(주)교보문고", "교보문고", "C#"],
      "decompound_mode": "discard",
      "discard_punctuation": true
    }

1-1. “C#”

“C#” 을 명사사전에 등록할 경우 “C”로 명사사전에 색인이 된다. 따라서 사용자가 Cursed라는 단어를 검색한다면 “C” "ursed"로 형태소 분석되어 예상과 다른 결과를 출력해낸다. 해당 문제는 ES에선 기본적으로 tokenizer세팅 중 discard_punctuation 옵션이 true이기 때문에 구두점이 들어간 것들은 무시한채 형태소분석이 되기 때문이다.

1-2. “(주)교보문고”, “(G)IDLE”

1-2의 두 예시들은 형태소분석이 아예 되지 않는다.

2. discard_punctuation

discard_punctuation 옵션은 nori_tokenizer 내에서 구두점을 제거할지 여부를 설정한다.

true로 설정하면, 구두점이 포함된 단어가 토큰화 과정에서 제거된다.

false로 설정하면, 구두점이 포함된 단어도 토큰으로 유지된다.

2-1. "discard_punction": true 옵션으로 인해 형태소 분석 시 제거되는 구두점

문장 부호: 마침표(.), 쉼표(,), 물음표(?), 느낌표(!), 세미콜론(;), 콜론(:)
괄호: 소괄호((, )), 중괄호({, }), 대괄호([, ])
인용 부호: 큰따옴표("), 작은따옴표(')
하이픈 및 대시: 하이픈(-), 대시(—)
기타 기호: 앰퍼샌드(&), 별표(*), 슬래시(/), 백슬래시(), 퍼센트(%), 샵(#), 골뱅이(@)

2-2. "discard_punctuation": true (default)

형태소 분석 결과에서 명사사전으로 등록한 “(주)교보문고”, “(G)I-DLE”이 형태소 분석되지 않은 것을 확인할 수 있다.

      "tokenizer": {
        "bigram_tokenizer": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 2
        },
        "nori_user_tokenizer": {
          "type": "nori_tokenizer",
          "user_dictionary_rules": ["(주)교보문고", "교보문고", "1+1", "(G)I-DLE", "(G)IDLE", "{G}IDLE", "C#"],
          "decompound_mode": "discard",
          "discard_punctuation": true
        }
      }
 

POST /my_index/_analyze
{
  "analyzer": "nori_user_analyzer",
  "text": "안녕하세요. (주)교보문고에서 (G)I-DLE의 책을 구매했습니다. 가격은 1+1입니다."
}
 

// 결과
{
  "tokens": [
    {
      "token": "안녕",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "하",
      "start_offset": 2,
      "end_offset": 3,
      "type": "word",
      "position": 1
    },
    {
      "token": "책",
      "start_offset": 27,
      "end_offset": 28,
      "type": "word",
      "position": 6
    },
    {
      "token": "구매",
      "start_offset": 30,
      "end_offset": 32,
      "type": "word",
      "position": 8
    },
    {
      "token": "하",
      "start_offset": 32,
      "end_offset": 33,
      "type": "word",
      "position": 9
    },
    {
      "token": "가격",
      "start_offset": 38,
      "end_offset": 40,
      "type": "word",
      "position": 12
    },
    {
      "token": "1+1",
      "start_offset": 42,
      "end_offset": 45,
      "type": "word",
      "position": 14
    },
    {
      "token": "이",
      "start_offset": 45,
      "end_offset": 48,
      "type": "word",
      "position": 15
    }
  ]
}

2-2. "discard_punctuation": false

형태소 분석 결과에서 명사사전으로 등록한 “(주)교보문고”, “(G)I-DLE”이 형태소 분석된 것을 확인할 수 있다.

      "tokenizer": {
        "bigram_tokenizer": {
          "type": "ngram",
          "min_gram": 2,
          "max_gram": 2
        },
        "nori_user_tokenizer": {
          "type": "nori_tokenizer",
          "user_dictionary_rules": ["(주)교보문고", "교보문고", "1+1", "(G)I-DLE", "(G)IDLE", "{G}IDLE", "C#"],
          "decompound_mode": "discard",
          "discard_punctuation": false
        }
      }
 


POST /my_index/_analyze
{
  "analyzer": "nori_user_analyzer",
  "text": "안녕하세요. (주)교보문고에서 (G)I-DLE의 책을 구매했습니다. 가격은 1+1입니다."
}
 

// 결과
{
  "tokens": [
    {
      "token": "안녕",
      "start_offset": 0,
      "end_offset": 2,
      "type": "word",
      "position": 0
    },
    {
      "token": "하",
      "start_offset": 2,
      "end_offset": 3,
      "type": "word",
      "position": 1
    },
    {
      "token": "(주)교보문고",
      "start_offset": 7,
      "end_offset": 14,
      "type": "word",
      "position": 6
    },
    {
      "token": "(g)i-dle",
      "start_offset": 17,
      "end_offset": 25,
      "type": "word",
      "position": 9
    },
    {
      "token": "책",
      "start_offset": 27,
      "end_offset": 28,
      "type": "word",
      "position": 12
    },
    {
      "token": "구매",
      "start_offset": 30,
      "end_offset": 32,
      "type": "word",
      "position": 15
    },
    {
      "token": "하",
      "start_offset": 32,
      "end_offset": 33,
      "type": "word",
      "position": 16
    },
    {
      "token": "가격",
      "start_offset": 38,
      "end_offset": 40,
      "type": "word",
      "position": 21
    },
    {
      "token": "1+1",
      "start_offset": 42,
      "end_offset": 45,
      "type": "word",
      "position": 24
    },
    {
      "token": "이",
      "start_offset": 45,
      "end_offset": 48,
      "type": "word",
      "position": 25
    }
  ]
}

2-3. 하지만 여전히 문제, “#”

discard_punctuation 옵션을 false로 주고 “C#”을 명사 사전에 등록하더라도 “C”와 “#”으로 분리되어 나오는 것을 볼 수 있다. “C###”을 명사 사전에 등록해도 “C”, “###”으로 분리가 된다. “#”이외의 다른 문자의 경우 정상적으로 출력이 되지만, “#”만 명사 사전을 따르지 않는다.

2-4. “#”을 “|@@|”으로

char_filter옵션에서 pattern_replace를 사용하여 “#”을 대부분의 유저가 검색하지 않을 것 같은 “|@@|”으로 변경했다. nori_tokenizer에서 “#”을 어떻게 예외처리하는지는 모르겠지만 “#”을 다른 특수문자로 변경하니 하나의 토큰으로 응답하는 것을 확인할 수 있었다.

  "char_filter": {
    "remove_whitespace": {
      "pattern": "\\\\s",
      "type": "pattern_replace",
      "replacement": ""
    },
    "hash_to_special": {
      "pattern": "#",
      "type": "pattern_replace",
      "replacement": "|@@|"
    }
  },
 



"user_dictionary_rules": [
   "1+1",
   ".G.IDLE", ",G,IDLE", "?G?IDLE", "!G!IDLE", ";G;IDLE", ":G:IDLE",
   "(G)IDLE", "{G}IDLE", "[G]IDLE",
   "IDLE", "'G'IDLE",
   "-G-IDLE",
   "&G&IDLE","*G*IDLE","/G/IDLE","\\\\G\\\\IDLE","%G%IDLE","|@@|G|@@|IDLE","@G@IDLE", "|G|IDLE",
   "(주)교보문고", "교보문고", "정보검색#3", "C|@@|"
 ],
 



POST /test-jh/_analyze
{
  "text": "#G#IDLE",
  "analyzer": "nori_custom_analyzer"
}
 


// 결과
{
  "tokens": [
    {
      "token": "|@@|g|@@|idle",
      "start_offset": 0,
      "end_offset": 7,
      "type": "word",
      "position": 0
    }
  ]
}

3. 주의해야할 점

3-1. 문서화

특정 문자를 임의로 변경하는 것은 나중에 다른 개발자가 왔을 때 “#”이 왜 다른 문자로 바뀌어 분석되는지 의아해할 수도 있다. 따라서 임의로 변경한 세팅값에 대해선 나중에 다른 개발자가 예외 사항에 대해서 쉽게 이해할 수 있도록 문서화를 잘 해둬야 한다.

3-2. discard_punction 옵션을 false로 바꾸면서 생기게 될 사이드이펙트

“#” 과 같은 예외사항을 찾긴 했으나, 추가적으로 다른 예외사항이 생길 수 있다. 구두점에 사이드이펙트에 대해서는 지속적으로 지켜봐야 한다.

3-3. 사전 세팅에 대한 내용을 적용하기 위해 사전관리도구의 코드도 변경해야 한다.

bo-search-es-api 의 코드에서도 “#”이 들어왔을 때 “|@@|”으로 바꾸어 사전에 저장하는 로직을 추가해야한다.

profile
Search Engineer

0개의 댓글