윈도우 환경에서 elasticsearch의 한글 블로그 검색 인덱스 구성
지난 글에서 elasticsearch의 간단한 사용법을 알아봤는데요,
윈도우 환경에서 curl.exe를 이용한 elasticsearch 6.x 기본 사용법
; https://www.sysnet.pe.kr/2/0/11663
이번에는 그것을 이용해 블로그 글을 검색하는 환경을 구성해 보겠습니다. 우선, 이전 글에 따라 한글 검색을 도와주는 플러그인은 설치해야 합니다.
윈도우 환경에서 elasticsearch의 한글 형태소 분석기 설치
; https://www.sysnet.pe.kr/2/0/11664
그 외에 블로그의 특성상 html 태그가 들어갈 것이므로 "openkoreantext-analyzer"를 기본으로 사용할 수는 없고, 다음과 같이 "html_strip" 필터를 추가로 구성해 analyzer를 만들어야 합니다.
{
"settings": {
"analysis": {
"analyzer": {
"blogtext_analyzer": {
"type": "custom",
"tokenizer": "openkoreantext-tokenizer",
"char_filter": [
"html_strip",
"openkoreantext-normalizer"
],
"filter": [
"openkoreantext-stemmer",
"openkoreantext-redundant-filter"
]
}
}
}
}
}
편의상 위의 내용을 settings.json 파일로 저장하고 다음과 같이 명령을 내리면 인덱스가 생성됩니다.
curl -XDELETE "http://localhost:9200/my_blog/"
curl -XPUT "http://localhost:9200/my_blog/" -H "Content-Type: application/json" -d "@settings.json"
또는 불편을 감수하고 다음과 같이 명령을 내리면 됩니다.
curl -XPUT "http://localhost:9200/my_blog/" -H "Content-Type: application/json" -d "{ \"settings\": { \"analysis\": { \"analyzer\": { \"blogtext_analyzer\": { \"type\": \"custom\", \"tokenizer\": \"openkoreantext-tokenizer\", \"char_filter\": [ \"html_strip\", \"openkoreantext-normalizer\" ], \"filter\": [ \"lowercase\", \"openkoreantext-stemmer\", \"openkoreantext-redundant-filter\" ] } } } } }"
사용자 정의 구성으로 blogtext_analyzer를 생성했으니, 이제 Type 정의에서 다음과 같이 적용할 수 있습니다.
{
"articles": {
"properties": {
"writer": {
"type": "text",
"index": "false"
},
"wid": { "type": "integer" },
"contents": {
"type": "text",
"analyzer": "blogtext_analyzer"
},
"registered": { "type": "date" }
}
}
}
위의 내용을 "articles.json" 파일로 저장하고 다음과 같이 명령을 내리거나,
curl -XPUT "http://localhost:9200/my_blog/articles/_mapping" -H "Content-Type: application/json" -d "@articles.json"
인라인으로 -d 옵션을 이용해 모두 써줘도 됩니다.
curl -XPUT "http://localhost:9200/my_blog/articles/_mapping" -H "Content-Type: application/json" -d "{ \"articles\" : { \"properties\" : { \"writer\" : {\"type\" : \"text\", \"index\" : \"false\"}, \"wid\" : {\"type\" : \"integer\"}, \"contents\" : {\"type\" : \"text\", \"analyzer\": \"blogtext_analyzer\" }, \"registered\" : {\"type\" : \"date\"} } } }"
정상적으로 타입이 생성되었는지 Type 정의를 확인하고,
curl -XGET "http://localhost:9200/my_blog/articles/_mapping?pretty"
데이터 몇 개를 넣은 다음,
curl -XPUT "http://localhost:9200/my_blog/articles/1" -H "Content-Type: application/json" -d
"{
"name" : "tester",
"wid": 16,
"contents": "^gacutil.exe^를 실행해 ^<a href='dotnet'^>닷넷^</a^> DLL을 GAC에 등록하려 할 때 다음과 같은 식의 오류가 발생한다면?",
"registered":"2017-04-29T10:16:00"
}"
curl -XPUT "http://localhost:9200/my_blog/articles/2" -H "Content-Type: application/json" -d
"{
"name" : "tester",
"wid": 17,
"contents": "한국어를 처리하는 예시입니닼ㅋㅋ",
"registered":"2017-04-29T10:16:00"
}"
curl -XPUT "http://localhost:9200/my_blog/articles/3" -H "Content-Type: application/json" -d
"{
"name" : "tester",
"wid": 17,
"contents": "테스트 이미지^<img alt=\\"test\\" ^>입니다.",
"registered":"2017-04-29T10:16:00"
}"
curl -XPUT "http://localhost:9200/my_blog/articles/1" -H "Content-Type: application/json" -d "{ \"name\" : \"tester\", \"wid\": 16, \"contents\": \"^<b^>gacutil.exe^</b^>를 실행해 ^<a href='dotnet'^>닷넷^</a^> DLL을 GAC에 등록하려 할 때 다음과 같은 식의 오류가 발생한다면?\", \"registered\":\"2017-04-29T10:16:00\" }"
curl -XPUT "http://localhost:9200/my_blog/articles/2" -H "Content-Type: application/json" -d "{ \"name\" : \"tester\", \"wid\": 17, \"contents\": \"한국어를 처리하는 예시입니닼ㅋㅋ\", \"registered\":\"2017-04-29T10:16:00\" }"
curl -XPUT "http://localhost:9200/my_blog/articles/3" -H "Content-Type: application/json" -d "{ \"name\" : \"tester\", \"wid\": 18, \"contents\": \"테스트 이미지^<img alt=\\\"test\\\" ^>입니다.\", \"registered\":\"2017-04-29T10:16:00\" }"
검색 쿼리를 날려볼 수 있습니다.
curl -XGET "http://localhost:9200/my_blog/articles/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"contents\": \"gacutil\" } } }"
{"took":1,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":1,"max_score":0.2876821,"hits":[{"_index":"my_blog","_type":"articles","_id":"1","_score":0.2876821,"_source":{ "name" : "tester", "wid": 16, "contents": "<b>gacutil.exe</b>를 실행해 <a href='dotnet'>닷넷</a> DLL을 GAC에 등록하려 할 때 다음과 같은 식의 오 류가 발생한다면?", "registered":"2017-04-29T10:16:00" }}]}}
html_strip 필터를 적용했기 때문에 html 태그 내에 있던 내용은 검색되지 않습니다.
curl -XGET "http://localhost:9200/my_blog/articles/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"contents\": \"href\" } } }"
{"took":0,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
html_strip이 좋긴 한데, 아쉽게도 img의 alt 태그와 같은 내용에 대한 배려가 없군요. ^^
curl -XGET "http://localhost:9200/my_blog/articles/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"contents\": \"test\" } } }"
{"took":0,"timed_out":false,"_shards":{"total":5,"successful":5,"skipped":0,"failed":0},"hits":{"total":0,"max_score":null,"hits":[]}}
curl.exe에서 HTML에 대한 인코딩이 잘못되면 다음과 같이 황당한 오류가 발생합니다.
c:\temp> curl -XPUT "http://localhost:9200/my_blog/articles/5" -H "Content-Type: application/json" -d "{ \"name\" : \"tester\", \"wid\": 17, \"address\": \"한국어를 <b>처리</b> 예시\", \"registered\":\"2017-04-29T10:16:00\" }"
The system cannot find the file specified.
왜냐하면, Windows Shell 명령어에서 <, > 문자는 Redirection 용도로 사용하기 때문입니다. 따라서 <, >와 같은 문자는 '^' 문자를 이용해 escape 처리를 해야 합니다.
참고로 openkoreantext-analyzer의 기본 설정은 "
open-korean-text/elasticsearch-analysis-openkoreantext" 문서에 의하면 다음과 같다고 합니다.
"openkoreantext-analyzer": {
"type": "custom",
"tokenizer": "openkoreantext-tokenizer",
"char_filter": [
"openkoreantext-normalizer"
],
"filter": [
"openkoreantext-stemmer",
"openkoreantext-redundant-filter",
"classic",
"length",
"lowercase"
]
}
위의 구성에서 filter 부분의 순서가 의미가 있는데, 만약 lowercase를 다음과 같이 위로 설정해 놓으면,
"filter": [
"lowercase"
"openkoreantext-stemmer",
"openkoreantext-redundant-filter",
]
다음과 같은 오류가 발생합니다.
{"error":{"root_cause":[{"type":"class_cast_exception","reason":"org.apache.lucene.analysis.LowerCaseFilter cannot be cast to org.apache.lucene.analysis.ko.KoreanTokenPrepareable"}],"type":"class_cast_exception","reason":"org.apache.lucene.analysis.LowerCaseFilter cannot be cast to org.apache.lucene.analysis.ko.KoreanTokenPrepareable"},"status":500}
만약 위와 같은 오류가 발생한다면 filter 항목의 순서를 조정할 필요가 있는 것입니다.
[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]