6주 동안 실전 프로젝트를 진행하면서 학습한 내용을 기록하고, 팀의 리더로서 프로젝트 매니징 경험을 기록하는 시리즈입니다.
검색 서비스의 기본 요구 사항 중 하나는 ‘오타 교정’ 기능입니다. 심지어 초보자를 위한 특허 검색 서비스라면 ‘오타 교정’ 기능이 필수적이라 생각하여 기능을 추가하였습니다.
하지만 '오타 교정' 기능을 구현한 뒤로 검색 결과 정확도가 저하되거나 검색 결과가 누락되는 문제가 있었습니다.
v1. Nori + Jamo 필터 +edge-Ngram 필터 | v2. Nori, Jamo, Edge n-gram+ 사용자 사전 | v3. Nori + Jamo + N-gram + 사용자 사전 | v4. Nori + Jamo + N-gram | |
---|---|---|---|---|
storage size | 56.87mb | 65.47mb | 70.9mb | 69.1mb |
추가 및 변경 사항 | 1) 필드별 색인 애널라이저 분리 2) 필드별 검색 애널라이저 분리 3) 사용자 정의 사전 추가 | 1) Edge n-gram 애널라이저를 Ngram 애널라이저로 변경 | 1) 사용자 정의 사전을 적용하지 않음 | |
원문 키워드 검색 시 결과 반환 | ❌ | ⭕ | ⭕ | ⭕ |
띄어쓰기 변화 시 결과 반환 | ⭕ | ⭕ | ⭕ | ⭕ |
오타 교정 결과 반환 | ❌ | ❌ (결과 누락) | ⭕ | ⭕ |
다음은 사용자 정의 사전 적용 과정 및 4단계의 인덱스 정의 테스트 과정을 자세히 담은 내용입니다.
PUT nori-plus-jamo-edge-filters
{
"settings": {
"analysis": {
"char_filter": { // 1️⃣ 캐릭터 필터
"special_character_filter": {
"pattern": "[\\p{Alpha}\\p{Digit}\\p{Blank}]",
"type": "pattern_replace",
"replacement": ""
}
},
"tokenizer": { // 2️⃣ 토크나이저
"korean_nori_tokenizer": {
"type": "nori_tokenizer",
"decompound_mode": "mixed"
}
},
"filter": { // 3️⃣ 토큰 필터
"nori_posfilter": {
"type": "nori_part_of_speech",
"stoptags": ["J","E","NNB","MAJ","MM","XSV","XSA","VCP","SE","XSN","VCN","SP","NA","UNA","VSV","XPN","IC","VV"]
},
"ngram2_filter": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 15
}
},
"analyzer": { // 4️⃣ 애널라이저
"nori_analyzer": {
"type": "custom",
"tokenizer": "korean_nori_tokenizer",
"filter" : [
"lowercase",
"nori_posfilter"
]
},
"jamo_index_analyzer": {
"type": "custom",
"char_filter": [
"special_character_filter"
],
"tokenizer": "korean_nori_tokenizer",
"filter": [
"nori_posfilter",
"jamo",
"ngram2_filter"
]
},
"jamo_search_analyzer": {
"type": "custom",
"char_filter": [
"special_character_filter"
],
"tokenizer": "standard",
"filter": [
"jamo"
]
},
"number_analyzer" : {
"type" : "pattern",
"pattern" :"[.]"
}
}
}
},
"mappings": { // 5️⃣ 필드에 데이터 타입 매핑
"properties": {
"발명의명칭": {
"type": "text",
"analyzer": "nori_analyzer",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
},
"spell": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer"
}
}
},
"요약": {
"type": "text",
"analyzer": "nori_analyzer",
"fields": {
"spell": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer"
}
}
},
"출원인": {
"type": "text",
"analyzer": "nori_analyzer",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
},
"spell": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer"
}
}
},
"CPC분류": {
"type": "text"
},
"IPC분류": {
"type": "text"
},
"event": {
"properties": {
"original": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"log": {
"properties": {
"file": {
"properties": {
"path": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
},
"공개번호": {
"type": "keyword"
},
"공고번호": {
"type": "keyword"
},
"등록번호": {
"type": "keyword"
},
"법적상태": {
"type": "keyword"
},
"출원번호": {
"type": "text",
"fields": {
"keyword" : {
"type" : "keyword",
"ignore_above": "256"
}
}
},
"출원일자": {
"type": "text",
"analyzer": "number_analyzer",
"fields": {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
정규 표현식을 사용하여 해당 문자들을 특정 문자열로 대체합니다.
pattern replace character filter가 제공하는 옵션은 다음과 같습니다.
표현식 | 의미 |
---|---|
\p{Alpha} | 문자 |
\p{Digit} | 숫자 |
{Blank} | space 또는 tab |
nori_tokenizer가 제공하는 옵션은 다음과 같습니다.
nori_part_of_speech 토큰 필터를 이용해서 제거할 품사(POS - Part Of Speech) 코드입니다.
태그 | 설명 | 태그 | 설명 | 태그 | 설명 |
---|---|---|---|---|---|
E | 어미 | IC | 감탄사 | J | 조사 |
MAJ | 접속 부사 | MM | 한정사 | NA | 알 수 없음 |
NNB | 의존명사 | SE | 줄임표 | SP | 공백 |
UNA | 알 수 없음 | VCN | 부정 지정사 | VCP | 긍정 지정사 |
VSV | 알 수 없음 | XPN | 접두사 | XSA | 형용사 파생 접미사 |
XSN | 명사 파생 접미사 | XSV | 동사 파생 접미사 | VV | 동사 |
edge-ngram token filter가 제공하는 옵션은 다음과 같습니다.
[발명의명칭], [출원인], [요약] 필드의 데이터에 한글 형태소 분석기(Nori)를 적용하였습니다.
[발명의명칭], [요약], [출원인] 필드를 멀티 필드로 두고 .spell 필드에 커스텀 애널라이저를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자, 공백을 제거하고 nori 토크나이저와 jamo, edge n-gram 필터를 사용합니다.
jamo_index_analyzer로 색인된 용어들을 잘 찾기 위해 검색 시에 search analyzer를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자, 공백을 제거하고 standard 토크나이저와 jamo 필터를 사용합니다.
[출원일자] 필드의 데이터는 ‘2022.09.21’의 형태를 가집니다.
그 중에서 연도(2022)만 추출하기 위해 패턴 “[.]”을 기준으로 텍스트를 분리하는 패턴 애널라이저를 적용했습니다.
Nori가 품사를 잘못 정의하고 잘라내는 문제
오일저장탱크
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 0,
"relation": "eq"
},
"max_score": null,
"hits": []
}
}
오일
을 오
와 일
로 잘라내기 때문 인덱스텀과 쿼리텀이 일치하지 않는 문제
ㅇㅗ
, ㅇㅣ
, ㅇㅣㄹ
ㅇㅗㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡㄹㅡㄹ
storage size: 56.87mb → 133.64mb
PUT user-dic-plus
{
"settings": {
"analysis": {
"char_filter": { // 1️⃣ 캐릭터 필터
"special_character_filter": {
"pattern": "[\\p{Alpha}\\p{Digit}]",
"type": "pattern_replace",
"replacement": ""
},
"blank_filter": {
"pattern": "[\\p{Blank}]",
"type": "pattern_replace",
"replacement": ""
}
},
"tokenizer": { // 2️⃣ 토크나이저
"korean_nori_tokenizer": {
"type": "nori_tokenizer",
"user_dictionary": "user_dic/pat_dic_v3.txt",
"decompound_mode": "mixed"
}
},
"filter": { // 3️⃣ 토큰 필터
"nori_posfilter": {
"type": "nori_part_of_speech",
"stoptags": ["J","E","NNB","MAJ","MM","XSV","XSA","VCP","SE","XSN","VCN","SP","NA","UNA","VSV","XPN","IC","VV"]
},
"ngram2_filter": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 20
}
},
"analyzer": { // 4️⃣ 애널라이저
"nori_analyzer": {
"type": "custom",
"tokenizer": "korean_nori_tokenizer",
"filter" : [
"lowercase",
"nori_posfilter"
]
},
"ngram_analyzer": {
"type": "custom",
"char_filter": [
"special_character_filter"
],
"tokenizer": "standard",
"filter" : [
"ngram2_filter"
]
},
"jamo_index_analyzer": {
"type": "custom",
"char_filter": [
"special_character_filter"
],
"tokenizer": "standard",
"filter": [
"jamo"
]
},
"jamo_search_analyzer": {
"type": "custom",
"char_filter": [
"blank_filter",
"special_character_filter"
],
"tokenizer": "standard",
"filter": [
"jamo"
]
},
"jamo_live_blank_search_analyzer": {
"type": "custom",
"char_filter": [
"special_character_filter"
],
"tokenizer": "standard",
"filter": [
"jamo"
]
},
"number_analyzer" : {
"type" : "pattern",
"pattern" :"[.]"
}
}
}
},
"mappings": { // 5️⃣ 필드에 데이터 타입 매핑
"properties": {
"발명의명칭": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer",
"fields": {
"lblank": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_live_blank_search_analyzer"
},
"ngram": {
"type": "text",
"analyzer": "ngram_analyzer"
},
"nori": {
"type": "text",
"analyzer": "nori_analyzer"
}
}
},
"요약": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer",
"fields": {
"lblank": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_live_blank_search_analyzer"
},
"ngram": {
"type": "text",
"analyzer": "ngram_analyzer"
},
"nori": {
"type": "text",
"analyzer": "nori_analyzer"
}
}
},
"출원인": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_search_analyzer",
"fields": {
"lblank": {
"type": "text",
"analyzer": "jamo_index_analyzer",
"search_analyzer": "jamo_live_blank_search_analyzer"
},
"ngram": {
"type": "text",
"analyzer": "ngram_analyzer"
},
"nori": {
"type": "text",
"analyzer": "nori_analyzer"
}
}
},
"CPC분류": {
"type": "text"
},
"IPC분류": {
"type": "text"
},
"event": {
"properties": {
"original": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"log": {
"properties": {
"file": {
"properties": {
"path": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
},
"공개번호": {
"type": "keyword"
},
"공고번호": {
"type": "keyword"
},
"등록번호": {
"type": "keyword"
},
"법적상태": {
"type": "keyword"
},
"출원번호": {
"type": "text",
"fields": {
"keyword" : {
"type" : "keyword",
"ignore_above": "256"
}
}
},
"출원일자": {
"type": "text",
"analyzer": "number_analyzer",
"fields": {
"keyword" : {
"type" : "keyword",
"ignore_above" : 256
}
}
}
}
}
}
정규 표현식을 사용하여 해당 문자들을 특정 문자열로 대체합니다.
pattern replace character filter가 제공하는 옵션은 다음과 같습니다.
표현식 | 의미 |
---|---|
\p{Alpha} | 문자 |
\p{Digit} | 숫자 |
{Blank} | space 또는 tab |
nori_tokenizer가 제공하는 옵션은 다음과 같습니다.
nori_part_of_speech 토큰 필터를 이용해서 제거할 품사(POS - Part Of Speech) 코드입니다.
⇒ “J","E","NNB","MAJ","MM","XSV","XSA","VCP","SE","XSN","VCN","SP","NA","UNA","VSV","XPN","IC","VV"
edge-ngram token filter가 제공하는 옵션은 다음과 같습니다.
[발명의명칭], [출원인], [요약] 필드를 멀티 필드로 두고 .nori 필드에 한글 형태소 분석기(Nori)를 적용하였습니다.
[발명의명칭], [출원인], [요약] 필드를 멀티 필드로 두고 .ngram 필드에 커스텀 애널라이저를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자를 제거하고 standard 토크나이저와 edge n-gram 필터를 사용합니다.
[발명의명칭], [요약], [출원인] 필드를 멀티 필드로 두고 .lblank 필드에 커스텀 애널라이저를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자를 제거하고 standard 토크나이저와 jamo 필터를 사용합니다.
jamo_index_analyzer로 색인된 용어들을 잘 찾기 위해 검색 시에 search analyzer를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자, 공백을 제거하고 standard 토크나이저와 jamo 필터를 사용합니다.
jamo_index_analyzer로 색인된 용어들을 잘 찾기 위해 검색 시에 search analyzer를 적용하였습니다.
이 커스텀 애널라이저는 캐릭터 필터를 통해 문자, 숫자를 제거하고 standard 토크나이저와 jamo 필터를 사용합니다.
[출원일자] 필드의 데이터는 ‘2022.09.21’의 형태를 가집니다.
그 중에서 연도(2022)만 추출하기 위해 패턴 “[.]”을 기준으로 텍스트를 분리하는 패턴 애널라이저를 적용했습니다.
오일저장탱크
{
"took": 59,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 3,
"relation": "eq"
},
"max_score": 26.637434,
"hits": [
{
"_index": "user-dic-plus",
"_id": "52ulk4MBvrSsT3SDS3_T",
"_score": 26.637434,
"_source": {
"요약": "본 고안은 오일저장탱크를 갖춘 차량용 브레이크의 마스터실린더에 관한 것으로, 그 목적은 오일감지장치의 플로트가 오일저장탱크의 천정면에 접착되는 것을 방지하는 것이다. 본 고안에 따른 오일저장탱크를 갖춘 차량용 브레이크의 마스터실린더는 오일저장탱크(20) 내에 마련되는 플로트(31)의 상면에 다수개의 돌기(33)를 형성하여 오일의 점성 및 표면장력에 의해 탱크(20)의 천정면에 달라붙는 것을 방지하였다. 따라서 플로트(31)는 항상 오일의 액면상에 떠있게 되어 본 기능을 원활하게 수행하며, 이의 전체적인 신뢰성이 향상되는 이점이 있다.",
"발명의명칭": "오일저장탱크를 갖춘 차량용 브레이크의 마스터실린더()"
}
},
{
"_index": "user-dic-plus",
"_id": "SGulk4MBvrSsT3SDioXv",
"_score": 18.489084,
"_source": {
"요약": "본 발명은 용기(1)와, 용기의 상부 일 측에 파이프에 연결 설치되어 오일이 유입되는 오일저장탱크(2)와, 상기 용기(1)의 다른 상부 일 측에 파이프에 연결 설치되며 용기내의 압력을 감압시키는 진공펌프(4)와, 상기 용기(1)의 하부 일 측에 파이프에 연결 설치되어 오존을 용기(1)내에 유입된 오일(7)에 용해시키도록 공급하는 오존발생장치(3)와, 상기 용기(1)내의 하부 다른 일 측에 설치된 배출구(5)와, 용기(1)내의 몸체에 중앙부에 설치된 관찰창(6)과, 용기(1)의 하부에 설치된 진동장치(8)로 구성된 저농도 오존을 함유하는 오일제제 제조장치 및 그 제법. 저농도 오존화 오일제제 제조장치. 저농도 오존화 오일제제 제법. 진공펌프. 오존발생장치. 진동장치.",
"발명의명칭": "저농도 오존을 함유하는 오일제제 제조장치 및 그 제법(Production method and apparatus for ozonized oil)"
}
},
{
"_index": "user-dic-plus",
"_id": "wmukk4MBvrSsT3SDxGpH",
"_score": 15.39892,
"_source": {
"요약": "본 발명은 음식물 쓰레기 처리장치에 관한 것으로서 그 기술적인 수단은, 본체부(1)의 상부 일측면에 쓰레기 투입구(7)가 설치되고, 상기 본체부(1)의 내측에 형성되는 몸체(13)의 저면에 스크류 컨베이어(11) (11´) (11˝)로 이루어진 분쇄수단(14)이 설치되며, 상기 분쇄수단(14)의 상측에 중공의 회전축(15)에 연결되는 다수의 패들(16)로서 구성되는 교반기(17)가 설치되고, 상기 회전축(15)에 다수의 에어홀(34)이 형성한다. 또한, 상기 몸체(13)의 측면 상, 하에 전동기(18) (19)를 각각 설치하여 그 상측의 전동기(18)를 교반기(17)에 체인(22) 연결하고, 상기 하측의 전동기(19)를 스크류 컨베이어(11) 결합되는 스프로킷(23)에 체인(24) 연결하며, 상기 스크류 컨베이어(11)와 그 일측의 스크류 컨베이어(11´)는 기어 (25) (26) 결합되고, 상기 스크류 컨베이어(11)와 그 타측의 스크류 컨베이어(11˝)는 체인(27) 연결되며, 상기 몸체(13)의 외 측면 하부에 오일 저장탱크(29)를 설치하고, 상기 오일저장탱크(29)의 외 측면 저부에 히터장치(30)를 설치하며, 상기 히터장치(30)의 일 측에 송풍기 (31)를 설치하여 회전축(15)에 형성되는 에어 홀(34)에 에어를 공급토록 연결하고, 상기 몸체(13)의 일측에 결로장치(32)를 설치하는 것을 요지로 한다.",
"발명의명칭": "음식물 쓰레기 처리장치()"
}
}
]
}
}
ㅇㅗㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡㄹㅡㄹ
✅ㅇㅗㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡㄹㅡㄹㄱㅏㅈㅊㅜㄴㅊㅏㄹㅑㅇㅇㅛㅇㅂㅡㄹㅔㅇㅣㅋㅡㅇㅡㅣㅁㅏㅅㅡㅌㅓㅅㅣㄹㄹㅣㄴㄷㅓ
ㅇㅗㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡㄹㅡㄹ
✅어일저장탱크
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": {
"value": 2,
"relation": "eq"
},
"max_score": 14.601166,
"hits": [
{
"_index": "user-dic-plus",
"_id": "wmukk4MBvrSsT3SDxGpH",
"_score": 14.601166,
"_source": {
"요약": "본 발명은 음식물 쓰레기 처리장치에 관한 것으로서 그 기술적인 수단은, 본체부(1)의 상부 일측면에 쓰레기 투입구(7)가 설치되고, 상기 본체부(1)의 내측에 형성되는 몸체(13)의 저면에 스크류 컨베이어(11) (11´) (11˝)로 이루어진 분쇄수단(14)이 설치되며, 상기 분쇄수단(14)의 상측에 중공의 회전축(15)에 연결되는 다수의 패들(16)로서 구성되는 교반기(17)가 설치되고, 상기 회전축(15)에 다수의 에어홀(34)이 형성한다. 또한, 상기 몸체(13)의 측면 상, 하에 전동기(18) (19)를 각각 설치하여 그 상측의 전동기(18)를 교반기(17)에 체인(22) 연결하고, 상기 하측의 전동기(19)를 스크류 컨베이어(11) 결합되는 스프로킷(23)에 체인(24) 연결하며, 상기 스크류 컨베이어(11)와 그 일측의 스크류 컨베이어(11´)는 기어 (25) (26) 결합되고, 상기 스크류 컨베이어(11)와 그 타측의 스크류 컨베이어(11˝)는 체인(27) 연결되며, 상기 몸체(13)의 외 측면 하부에 오일 저장탱크(29)를 설치하고, 상기 오일저장탱크(29)의 외 측면 저부에 히터장치(30)를 설치하며, 상기 히터장치(30)의 일 측에 송풍기 (31)를 설치하여 회전축(15)에 형성되는 에어 홀(34)에 에어를 공급토록 연결하고, 상기 몸체(13)의 일측에 결로장치(32)를 설치하는 것을 요지로 한다.",
"발명의명칭": "음식물 쓰레기 처리장치()"
}
},
{
"_index": "user-dic-plus",
"_id": "SGulk4MBvrSsT3SDioXv",
"_score": 14.172298,
"_source": {
"요약": "본 발명은 용기(1)와, 용기의 상부 일 측에 파이프에 연결 설치되어 오일이 유입되는 오일저장탱크(2)와, 상기 용기(1)의 다른 상부 일 측에 파이프에 연결 설치되며 용기내의 압력을 감압시키는 진공펌프(4)와, 상기 용기(1)의 하부 일 측에 파이프에 연결 설치되어 오존을 용기(1)내에 유입된 오일(7)에 용해시키도록 공급하는 오존발생장치(3)와, 상기 용기(1)내의 하부 다른 일 측에 설치된 배출구(5)와, 용기(1)내의 몸체에 중앙부에 설치된 관찰창(6)과, 용기(1)의 하부에 설치된 진동장치(8)로 구성된 저농도 오존을 함유하는 오일제제 제조장치 및 그 제법. 저농도 오존화 오일제제 제조장치. 저농도 오존화 오일제제 제법. 진공펌프. 오존발생장치. 진동장치.",
"발명의명칭": "저농도 오존을 함유하는 오일제제 제조장치 및 그 제법(Production method and apparatus for ozonized oil)"
}
}
]
}
}
오일저장탱크
차량
브레이크
마스터실린더
일
저장탱크
오일
오일저
오일저장
오일저장탱
오일저장탱크
오일저장탱크를
…어일
어일저
어일저장
어일저장탱
어일저장탱크
ㅇㅗㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡㄹㅡㄹ
ㄱㅏㅈㅊㅜㄴ
ㅊㅏㄹㅑㅇㅇㅛㅇ
…ㅇㅓㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡ
ㅇㅓㅇㅣㄹㅈㅓㅈㅏㅇㅌㅐㅇㅋㅡ
안녕하세요 설명이 너무 잘 작성되어서 도움이 많이 됐습니다.
한 가지 질문 드릴 내용이 있는데 jamo 필터는 elasticsearch에 없는 필터링인데 혹시 출처나 사용하셨던 분석기 파일을 받을 수 있을까요?