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

비밀번호

댓글 작성자
 




... 46  47  [48]  49  50  51  52  53  54  55  56  57  58  59  60  ...
NoWriterDateCnt.TitleFile(s)
12439정성태12/4/202010293디버깅 기술: 176. windbg - 특정 Win32 API에서 BP가 안 걸리는 경우 (2) [1]
12438정성태12/2/202010212오류 유형: 688. .Visual C++ - Error C2011 'sockaddr': 'struct' type redefinition
12437정성태12/1/20209905VS.NET IDE: 155. pfx의 암호 키 파일을 Visual Studio 없이 등록하는 방법
12436정성태12/1/202010172오류 유형: 687. .NET Core 2.2 빌드 - error MSB4018: The "RazorTagHelper" task failed unexpectedly.
12435정성태12/1/202015633Windows: 181. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (4) - ReuseUnicastPort를 이용한 포트 고갈 문제 해결 [1]파일 다운로드1
12434정성태11/30/202010951Windows: 180. C# - dynamicport 값의 범위를 알아내는 방법
12433정성태11/29/202010000Windows: 179. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (3) - SO_PORT_SCALABILITY파일 다운로드1
12432정성태11/29/202011349Windows: 178. 윈도우 환경에서 클라이언트 소켓의 최대 접속 수 (2) - SO_REUSEADDR [1]파일 다운로드1
12431정성태11/27/20209287.NET Framework: 976. UnmanagedCallersOnly + C# 9.0 함수 포인터 사용 시 x86 빌드에서 오동작하는 문제파일 다운로드1
12430정성태11/27/202010025오류 유형: 686. Ubuntu - E: The repository 'cdrom://...' does not have a Release file.
12429정성태11/25/202010107디버깅 기술: 175. windbg - 특정 Win32 API에서 BP가 안 걸리는 경우
12428정성태11/25/20209020VS.NET IDE: 154. Visual Studio - .NET Core App 실행 시 dotnet.exe 실행 화면만 나오는 문제
12427정성태11/24/202010211.NET Framework: 975. .NET Core를 직접 호스팅해 (runtimeconfig.json 없이) EXE만 배포해 실행파일 다운로드1
12426정성태11/24/20208791오류 유형: 685. WinDbg Preview - error InitTypeRead
12425정성태11/24/20209898VC++: 141. Visual C++ - "Treat Warnings As Errors" 옵션이 꺼져 있는데도 일부 경고가 에러 처리되는 경우
12424정성태11/24/202010281VC++: 140. C++의 연산자 동의어(operator synonyms), 대체 토큰 [1]
12423정성태11/22/202011169.NET Framework: 974. C# 9.0 - (16) 제약 조건이 없는 형식 매개변수 주석(Unconstrained type parameter annotations)파일 다운로드1
12422정성태11/21/20208972.NET Framework: 973. .NET 5, .NET Framework에서만 허용하는 UnmanagedCallersOnly 사용예파일 다운로드1
12421정성태11/19/20208751.NET Framework: 972. DNNE가 출력한 NE DLL을 직접 생성하는 방법파일 다운로드1
12420정성태11/19/20207914오류 유형: 684. Visual C++ - MSIL .netmodule or module compiled with /GL found; restarting link with /LTCG; add /LTCG to the link command line to improve linker performance
12419정성태11/19/20209167VC++: 139. Visual C++ - .NET Core의 nethost.lib와 정적 링크파일 다운로드1
12418정성태11/19/202011224오류 유형: 683. Visual C++ - error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MT_StaticRelease' doesn't match value 'MDd_DynamicDebug'파일 다운로드1
12417정성태11/19/20208619오류 유형: 682. Visual C++ - warning LNK4099: PDB '...pdb' was not found with '...lib(pch.obj)' or at '...pdb'; linking object as if no debug info
12416정성태11/19/20209897오류 유형: 681. Visual C++ - error LNK2001: unresolved external symbol _CrtDbgReport
12415정성태11/18/20209944.NET Framework: 971. UnmanagedCallersOnly 특성과 DNNE 사용파일 다운로드1
12414정성태11/18/202011017VC++: 138. x64 빌드에서 extern "C"가 아닌 경우 ___cdecl name mangling 적용 [4]파일 다운로드1
... 46  47  [48]  49  50  51  52  53  54  55  56  57  58  59  60  ...