본문 바로가기
ElasticSearch & OpenSearch

[OpenSearch] Settings, Mapping 설정하기

by yonikim 2022. 5. 3.
728x90

ElasticSearch 와 AWS OpenSearch 의 settings, mappings 작성 방법은 크게 다르지 않지만,

 

OpenSearch 의 경우 플러그인 커스텀이 안되기 때문에 nori 가 지원되지 않는다. 

 

nori 의 조상님인 은전한닢 을 사용하여 만든 settings 는 아래와 같다.

 

▷ Settings

`index.mapping.ignore_malformed: true` 를 설정해준 까닭은 Mappings 에서 설정해준 해당 필드의 DataType 과 실제로 Insert 한 데이터의 DataType 이 같지 않은 경우, 해당 Document 는 에러를 내뱉으며 Insert 하지 않는다. 즉, 데이터의 누락이 발생한다.

물론 필드들의 DataType 을 설정해주고 그에 맞춰 넣을 수 있다면 더할나위 없이 좋겠지만, 그렇지 못할 가능성이 크기 때문에 데이터의 누락을 방지하기 위해 설정해주는 것이 좋다.

(나의 예시로, updatedDate 의 DataType 을 date 를 설정해줬지만 신규 생한 데이터의 경우 null 이기 때문에 DataType 이 매칭되지 않아서 오류가 나며 해당 데이터가 Insert 되지 못했다)

{
  "settings": {
    "index.mapping.ignore_malformed": true,
    "index.search.slowlog.threshold.query.warn": "10s",
    "index": {
      "analysis": {
        "analyzer": {
          "standard": {
            "type": "custom",
            "tokenizer": "standard",
            "char_filter": ["remove_special_char", "remove_number"],
            "filter": ["lowercase", "trim", "synonyms", "stopwords"]
          },
          "seunjeon": {
            "type": "custom",
            "tokenizer": "seunjeon_tokenizer",
            "filter": ["lowercase", "trim", "synonyms", "stopwords"]
          },
          "ngram": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "filter": ["lowercase", "trim"]
          },
          "ngram_all": {
            "type": "custom",
            "tokenizer": "ngram_all_tokenizer",
            "filter": ["lowercase", "trim"]
          },
          "autocomplete_analyzer": {
            "tokenizer": "standard",
            "filter": ["lowercase", "trim", "autocomplete"]
          },

          "remove_html": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "char_filter": ["html_strip"],
            "dictionary_filter": ["stopwords"],
            "filter": ["lowercase", "trim"]
          },
          "remove_special_char": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "char_filter": ["remove_whitespace", "remove_special_char"],
            "dictionary_filter": ["stopwords"],
            "filter": ["lowercase", "trim"]
          }
        },
        "tokenizer": {
          "seunjeon_tokenizer": {
            "type": "seunjeon_tokenizer",
            "user_dict_path": "${사용자사전 패키지 ID}",
            "index_poses": [
              "UNK",
              "I",
              "M",
              "N",
              "S",
              "SL",
              "SH",
              "SN",
              "V",
              "VCP",
              "XP",
              "XS",
              "XR"
            ],
            "index_eojeol": false,
            "decompound": true,
            "pos_tagging": false
          },
          "ngram_tokenizer": {
            "type": "edge_ngram",
            "min_gram": 2,
            "max_gram": 10,
            "token_chars": ["letter", "digit"]
          },
          "ngram_all_tokenizer": {
            "type": "ngram",
            "token_chars": ["letter", "digit"]
          }
        },
        "filter": {
          "autocomplete": {
            "type": "edge_ngram",
            "min_gram": 1,
            "max_gram": 20,
            "token_chars": ["letter", "digit", "whitespace"]
          },
          "synonyms": {
            "type": "synonym",
            "synonyms_path": "${동의어 패키지 ID}",
            "lenient": true
          },
          "stopwords": {
            "type": "stop",
            "synonyms_path": "${금칙어 패키지 ID}",
            "updateable": true
          }
        },
        "dictionary_filter": {
          "synonyms": {
            "type": "synonym",
            "synonyms_path": "${동의어 패키지 ID}",
            "lenient": true
          },
          "stopwords": {
            "type": "stop",
            "synonyms_path": "${금칙어 패키지 ID}",
            "updateable": true
          }
        },
        "char_filter": {
          "remove_special_char": {
            "pattern": "[^ㄱ-ㅎ|가-힣|A-Z|a-z|0-9]",
            "type": "pattern_replace",
            "replacement": " "
          },
          "remove_number": {
            "pattern": "(?<=[가-힣|ㄱ-ㅎ])(?:[0-9]+)",
            "type": "pattern_replace",
            "replacement": ""
          },
          "remove_whitespace": {
            "type": "pattern_replace",
            "pattern": " ",
            "replacement": ""
          }
        }
      }
    }
  }
}

 

▷ Mappings

{
  "mappings": {
    "properties": {
      "ranking": {
        "type": "nested"
      },
      "name": {
        "type": "keyword",
        "fields": {
          "raw": {
            "type": "text",
            "analyzer": "standard"
          },
          "seunjeon": {
            "type": "text",
            "analyzer": "seunjeon"
          },
          "ngram": {
            "type": "text",
            "analyzer": "ngram"
          },
          "autocomplete": {
            "type": "text",
            "analyzer": "autocomplete_analyzer",
            "search_analyzer": "autocomplete_analyzer"
          }
        }
      },
      "description": {
        "type": "keyword",
        "ignore_above": 10000,
        "fields": {
          "raw": {
            "type": "text",
            "analyzer": "remove_html",
            "search_analyzer": "standard"
          },
          "seunjeon": {
            "type": "text",
            "analyzer": "seunjeon"
          },
          "ngram": {
            "type": "text",
            "analyzer": "ngram"
          }
        }
      }
      "createdDate": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      },
      "updatedDate": {
        "type": "date",
        "format": "yyyy-MM-dd HH:mm:ss"
      }
    }
  }
}

 

예시로 구현한 형태소 분석기와 관련하여 설명을 덧붙이자면 아래와 같다.

  • $.raw: 필드명에서 한글, 영문, 숫자 제외한 문자 제거 + 한글&숫자 조합의 경우 숫자 제거 + 대문자 -> 소문자 + 스페이스 별로 분리 
    • ex) [디플랫]포켓 밍크 베스트 F03DVT001 → 디플랫 / 포켓 / 밍크 / 베스트 / f03dvt001
    • ex) QA_쿠폰7 → qa / 쿠폰
  • $. ngram: 스페이스 별로 분리 + 대문자 -> 소문자 + 필드명 앞글자부터 2~10 자리수까지 분리
    • ex) [디플랫]포켓 밍크 베스트 F03DVT001 → 디플 / 디플랫 / 포켓 / 밍크 / 베스 / 베스트 / f0 / f03 / f03d / f03dv / f03dvt / f03dvt0 / f03dvt00 / f03dvt001
  • $.keyword: 필드명 그대로 사용
    • ex) [디플랫]포켓 밍크 베스트 F03DVT001 → [디플랫]포켓 밍크 베스트 F03DVT001
  • $.seunjeon: OpenSearch 에서 제공하는 은전한닢 형태소 분석기 사용

※ Kibana Dashboard 설정 방법

DELETE ${INDEX명}

PUT ${INDEX명}
{
  "settings": {
    "index.mapping.ignore_malformed": true,
    "index.search.slowlog.threshold.query.warn": "10s",
    "index": {
      "analysis": {
        "analyzer": {
          "standard": {
            "type": "custom",
            "tokenizer": "standard"
          },
          "seunjeon": {
            "type": "custom",
            "tokenizer": "seunjeon_tokenizer",
            "dictionary_filter": ["synonyms", "stopwords"],
            "filter": ["lowercase", "trim"]
          },
          "ngram": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "filter": ["lowercase", "trim"]
          },
          "autocomplete_analyzer": {
            "tokenizer": "standard",
            "filter": ["lowercase", "trim", "autocomplete"]
          },

          "remove_html": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "char_filter": ["html_strip"],
            "dictionary_filter": ["stopwords"],
            "filter": ["lowercase", "trim"]
          },
          "remove_special_char": {
            "type": "custom",
            "tokenizer": "ngram_tokenizer",
            "char_filter": ["remove_whitespace", "remove_special_char"],
            "dictionary_filter": ["stopwords"],
            "filter": ["lowercase", "trim"]
          }
        },
        "tokenizer": {
          "seunjeon_tokenizer": {
            "type": "seunjeon_tokenizer",
            "user_dictionary": "analyzers/F175892661",
            "index_poses": [
              "UNK",
              "I",
              "M",
              "N",
              "S",
              "SL",
              "SH",
              "SN",
              "V",
              "VCP",
              "XP",
              "XS",
              "XR"
            ],
            "index_eojeol": false,
            "decompound": true,
            "pos_tagging": false
          },
          "ngram_tokenizer": {
            "type": "edge_ngram",
            "min_gram": 2,
            "max_gram": 10,
            "token_chars": ["letter", "digit"]
          }
        },
        "filter": {
          "autocomplete": {
            "type": "edge_ngram",
            "min_gram": 1,
            "max_gram": 20,
            "token_chars": ["letter", "digit", "whitespace"]
          }
        },
        "dictionary_filter": {
          "synonyms": {
            "type": "synonym",
            "synonyms_path": "analyzers/F217095579",
            "updateable": true
          },
          "stopwords": {
            "type": "stop",
            "synonyms_path": "analyzers/F13804164",
            "updateable": true
          }
        },
        "char_filter": {
          "remove_special_char": {
            "pattern": "[^A-Za-z0-9]",
            "type": "pattern_replace",
            "replacement": ""
          },
          "remove_whitespace": {
            "type": "pattern_replace",
            "pattern": " ",
            "replacement": ""
          }
        }
      }
    }
  }
}
728x90