Microsoft MVP성태의 닷넷 이야기
글쓴 사람
정성태 (techsharer at outlook.com)
홈페이지
첨부 파일
 

(시리즈 글이 5개 있습니다.)
개발 환경 구성: 392. 윈도우 환경에서 curl.exe를 이용한 elasticsearch 6.x 기본 사용법
; https://www.sysnet.pe.kr/2/0/11663

개발 환경 구성: 393. 윈도우 환경에서 elasticsearch의 한글 형태소 분석기 설치
; https://www.sysnet.pe.kr/2/0/11664

개발 환경 구성: 394. 윈도우 환경에서 elasticsearch의 한글 블로그 검색 인덱스 구성
; https://www.sysnet.pe.kr/2/0/11669

.NET Framework: 791. C# - ElasticSearch를 위한 Client 라이브러리 제작
; https://www.sysnet.pe.kr/2/0/11676

개발 환경 구성: 507. Elasticsearch 6.6부터 기본 추가된 한글 형태소 분석기 노리(nori) 사용법
; https://www.sysnet.pe.kr/2/0/12309




Elasticsearch 6.6부터 기본 추가된 한글 형태소 분석기 노리(nori) 사용법

예전에는,

윈도우 환경에서 elasticsearch의 한글 형태소 분석기 설치
; https://www.sysnet.pe.kr/2/0/11664

한글 형태소 분석기 때문에 윈도우의 경우 최신 버전의 elasticsearch를 사용하고 싶어도 6.1.1로 고정했어야 하는 제약이, 이제는 6.6부터 elasticsearch 자체에서 제공하므로 마음껏 최신 버전 사용을 사용할 수 있습니다.

6.7.2 노리 (nori) 한글 형태소 분석기
; https://esbook.kimjmin.net/06-text-analysis/6.7-stemming/6.7.2-nori

자, 그럼 7.9 버전의 Elasticsearch로 클러스터도 구성해 보았으니,

Windows - 단일 머신에서 단일 바이너리로 여러 개의 ElasticSearch 노드를 실행하는 방법
; https://www.sysnet.pe.kr/2/0/12308

이참에 Nori도 설치해 보겠습니다. 방법이 매우 간단한데, "6.7.2 노리 (nori) 한글 형태소 분석기" 글에 나온 데로 Nori 플러그인을 설치하고,

D:\elk\elasticsearch> .\bin\elasticsearch-plugin install analysis-nori
-> Installing analysis-nori
-> Downloading analysis-nori from elastic
[=================================================] 100%
-> Installed analysis-nori

// 제거
// .\bin\elasticsearch-plugin remove analysis-nori

elasticsearch를 재시작하면 로드 도중 다음과 같은 메시지를 볼 수 있습니다.

[2020-09-02T11:07:48,023][INFO ][o.e.p.PluginsService     ] [TESTPC] loaded plugin [analysis-nori]




인덱스에 적용하기 전, 자신의 목적에 맞게 토큰을 잘 구획하는지는 다음과 같은 명령어로 확인할 수 있습니다.

curl -X POST "http://localhost:9200/_analyze" -H "Content-Type: application/json" -d "{ \"tokenizer\": \"nori_tokenizer\", \"text\": \"논쟁이 주를 이룹니다.\" }"

{"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":4,"end_offset":5,"type":"word","position":2},{"token":"를","start_offset":5,"end_offset":6,"type":"word","position":3},{"token":"이루","start_offset":7,"end_offset":11,"type":"word","position":4},{"token":"ㅂ니다","start_offset":7,"end_offset":11,"type":"word","position":5}]}


curl -X POST "http://localhost:9200/_analyze" -H "Content-Type: application/json" -d "{ \"tokenizer\": \"nori_tokenizer\", \"text\": \"동해물과 백두산이\" }"

{"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":3,"end_offset":4,"type":"word","position":2},{"token":"백두","start_offset":5,"end_offset":7,"type":"word","position":3},{"token":"산","start_offset":7,"end_offset":8,"type":"word","position":4},{"token":"이","start_offset":8,"end_offset":9,"type":"word","position":5}]}


보는 바와 같이, 기본 nori_tokenizer는 너무 세세하게 토큰을 나누기 때문에 일반적인 목적으로는 맞지 않습니다. 대신, "decompound_mode"의 옵션을 "none"으로 살짝 조정해 주면,

curl -X POST "http://localhost:9200/_analyze" -H "Content-Type: application/json" -d "{ \"tokenizer\": { \"type\": \"nori_tokenizer\", \"decompound_mode\": \"none\" }, \"text\": \"논쟁이 주를 이룹니다.\" }"

{"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":4,"end_offset":5,"type":"word","position":2},{"token":"를","start_offset":5,"end_offset":6,"type":"word","position":3},{"token":"이룹니다","start_offset":7,"end_offset":11,"type":"word","position":4}]}


curl -X POST "http://localhost:9200/_analyze" -H "Content-Type: application/json" -d "{ \"tokenizer\": { \"type\": \"nori_tokenizer\", \"decompound_mode\": \"none\" }, \"text\": \"동해물과 백두산이\" }"

{"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":3,"end_offset":4,"type":"word","position":2},{"token":"백두 산","start_offset":5,"end_offset":8,"type":"word","position":3},{"token":"이","start_offset":8,"end_offset":9,"type":"word","position":4}]}


여전히 "동해물"을 "동해" + "물"로, "백두산"을 "백두 산"으로 토큰을 나누는 것이 좀 마음에 안 들지만... 기본 모드였던 "discard"보다는 그나마 낫고 (별다른 대안이 없으므로) "none"으로 설정하는 것이 최선일 듯합니다.




tokenizer의 옵션 값이 결정되었으면 이제 인덱스에 반영해 보겠습니다. 다음의 글에 따라,

4.2 CRUD - 입력, 조회, 수정, 삭제
; https://esbook.kimjmin.net/04-data/4.2-crud

간단하게 "html_strip"과 "lowercase" 필터가 함께 적용된 tokenizer로 인덱스를 생성하고,

c:\temp> curl -XPUT "http://localhost:9200/my_org/" -H "Content-Type: application/json" -d "{ \"settings\":{ \"analysis\":{ \"analyzer\":{ \"nori_analyzer\": { \"tokenizer\": \"nori_tokenizer\", \"decompound_mode\": \"none\", \"char_filter\":[ \"html_strip\" ], \"filter\": [ \"lowercase\" ] } } } } }"

{"acknowledged":true,"shards_acknowledged":true,"index":"my_org"}

문서 구조를 정의한 다음,

c:\temp> curl -XPUT "http://localhost:9200/my_org/_mapping" -H "Content-Type: application/json" -d "{ \"properties\" : { \"name\" : {\"type\" :  \"text\", \"index\" : \"false\"}, \"age\" : {\"type\" : \"integer\"}, \"address\" : {\"type\" : \"text\", \"analyzer\": \"nori_analyzer\" }, \"registered\" : {\"type\" : \"date\"} } }"

{"acknowledged":true}

샘플 데이터를 넣으면,

/* doc_data1.json
{
    "name": "tester1",
    "age": 16,
    "address": "동해물과 백두산이 <h>스키마를</h>",
    "registered": "2017-04-29T10:16:00"
}
*/

/* doc_data2.json
{
    "name": "tester2",
    "age": 15,
    "address": "<span title='test'>김이지 Shine</span>",
    "registered": "2017-04-29T10:16:00"
}
*/

C:\temp> curl -XPUT "http://localhost:9200/my_org/_doc/1" -H "Content-Type: application/json" -d @doc_data1.json
{"_index":"my_org","_type":"_doc","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":0,"_primary_term":1}

C:\temp> curl -XPUT "http://localhost:9200/my_org/_doc/2" -H "Content-Type: application/json" -d @doc_data2.json
{"_index":"my_org","_type":"_doc","_id":"2","_version":1,"result":"created","_shards":{"total":2,"successful":2,"failed":0},"_seq_no":1,"_primary_term":1}

이제 다음과 같이 검색할 수 있습니다.

C:\temp> curl -XGET "http://localhost:9200/my_org/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"address\": \"동해물\" } } }"

{"took":12,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.5753642,"hits":[{"_index":"my_org","_type":"_doc","_id":"1","_score":0.5753642,"_source":{  "name" : "tester", "age": 16, "address": "동해물과 백두산이 <h>스키마를</h>", "registered":"2017-04-29T10:16:00" }}]}}

그런데, "서해물"로도 검색이 되는군요. ^^;

C:\temp> curl -XGET "http://localhost:9200/my_org/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"address\": \"서해물\" } } }"
{"took":3,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":1,"relation":"eq"},"max_score":0.2876821,"hits":[{"_index":"my_org","_type":"_doc","_id":"1","_score":0.2876821,"_source":{  "name" : "tester", "age": 16, "address": "동해물과 백두산이 <h>스키마를</h>", "registered":"2017-04-29T10:16:00" }}]}}

다행히, "서해"로는 검색이 안 되고.

C:\temp> curl -XGET "http://localhost:9200/my_org/_search" -H "Content-Type: application/json" -d "{ \"query\": { \"match\": { \"address\": \"서해\" } } }"
{"took":2,"timed_out":false,"_shards":{"total":1,"successful":1,"skipped":0,"failed":0},"hits":{"total":{"value":0,"relation":"eq"},"max_score":null,"hits":[]}}




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]







[최초 등록일: ]
[최종 수정일: 8/15/2021]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer at outlook.com

비밀번호

댓글 작성자
 




... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...
NoWriterDateCnt.TitleFile(s)
12703정성태7/11/202113597개발 환경 구성: 575. Azure VM에 (ICMP) ping을 허용하는 방법
12702정성태7/11/20218738오류 유형: 733. TaskScheduler에 등록된 wacs.exe의 Let's Encrypt 인증서 업데이트 문제
12701정성태7/9/20218386.NET Framework: 1075. C# - ThreadPool의 스레드는 반환 시 ThreadStatic과 AsyncLocal 값이 초기화 될까요?파일 다운로드1
12700정성태7/8/20218802.NET Framework: 1074. RuntimeType의 메모리 누수? [1]
12699정성태7/8/20217587VS.NET IDE: 167. Visual Studio 디버깅 중 GC Heap 상태를 보여주는 "Show Diagnostic Tools" 메뉴 사용법
12698정성태7/7/202111556오류 유형: 732. Windows 11 업데이트 시 3% 또는 0%에서 다운로드가 멈춘 경우
12697정성태7/7/20217443개발 환경 구성: 574. Windows 11 (Insider Preview) 설치하는 방법
12696정성태7/6/20218013VC++: 146. 운영체제의 스레드 문맥 교환(Context Switch)을 유사하게 구현하는 방법파일 다운로드2
12695정성태7/3/20218055VC++: 145. C 언어의 setjmp/longjmp 기능을 Thread Context를 이용해 유사하게 구현하는 방법파일 다운로드1
12694정성태7/2/20219966Java: 24. Azure - Spring Boot 앱을 Java SE(Embedded Web Server)로 호스팅 시 로그 파일 남기는 방법 [1]
12693정성태6/30/20217743오류 유형: 731. Azure Web App Site Extension - Failed to install web app extension [...]. {1}
12692정성태6/30/20217630디버깅 기술: 180. Azure - Web App의 비정상 종료 시 남겨지는 로그 확인
12691정성태6/30/20218498개발 환경 구성: 573. 테스트 용도이지만 테스트에 적합하지 않은 Azure D1 공유(shared) 요금제
12690정성태6/28/20219307Java: 23. Azure - 자바(Java)로 만드는 Web App Service - Tomcat 호스팅
12689정성태6/25/20219830오류 유형: 730. Windows Forms 디자이너 - The class Form1 can be designed, but is not the first class in the file. [1]
12688정성태6/24/20219545.NET Framework: 1073. C# - JSON 역/직렬화 시 리플렉션 손실을 없애는 JsonSrcGen [2]파일 다운로드1
12687정성태6/22/20217544오류 유형: 729. Invalid data: Invalid artifact, java se app service only supports .jar artifact
12686정성태6/21/20219980Java: 22. Azure - 자바(Java)로 만드는 Web App Service - Java SE (Embedded Web Server) 호스팅
12685정성태6/21/202110200Java: 21. Azure Web App Service에 배포된 Java 프로세스의 메모리 및 힙(Heap) 덤프 뜨는 방법
12684정성태6/19/20218654오류 유형: 728. Visual Studio 2022부터 DTE.get_Properties 속성 접근 시 System.MissingMethodException 예외 발생
12683정성태6/18/202110168VS.NET IDE: 166. Visual Studio 2022 - Windows Forms 프로젝트의 x86 DLL 컨트롤이 Designer에서 오류가 발생하는 문제 [1]파일 다운로드1
12682정성태6/18/20217836VS.NET IDE: 165. Visual Studio 2022를 위한 Extension 마이그레이션
12681정성태6/18/20217176오류 유형: 727. .NET 2.0 ~ 3.5 + x64 환경에서 System.EnterpriseServices 참조 시 CS8012 경고
12680정성태6/18/20218267오류 유형: 726. python2.7.exe 실행 시 0xc000007b 오류
12679정성태6/18/20218875COM 개체 관련: 23. CoInitializeSecurity의 전역 설정을 재정의하는 CoSetProxyBlanket 함수 사용법파일 다운로드1
12678정성태6/17/20218094.NET Framework: 1072. C# - CoCreateInstance 관련 Inteop 오류 정리파일 다운로드1
... 31  32  33  34  35  36  [37]  38  39  40  41  42  43  44  45  ...