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)
12828정성태9/4/20218332.NET Framework: 1114. C# 10 - (13) 단일 파일 내에 적용되는 namespace 선언파일 다운로드1
12827정성태9/4/20218296스크립트: 27. 파이썬 - 웹 페이지 데이터 수집을 위한 scrapy Crawler 사용법 요약
12826정성태9/3/202110490.NET Framework: 1113. C# 10 - (12) 문자열 보간 성능 개선 [1]파일 다운로드1
12825정성태9/3/20218068개발 환경 구성: 603. GoLand - WSL 환경과 연동
12824정성태9/2/202117157오류 유형: 760. 파이썬 tensorflow - Dst tensor is not initialized. 오류 메시지
12823정성태9/2/20216850스크립트: 26. 파이썬 - PyCharm을 이용한 fork 디버그 방법
12822정성태9/1/202112067오류 유형: 759. 파이썬 tensorflow - ValueError: Shapes (...) and (...) are incompatible [2]
12821정성태9/1/20217660.NET Framework: 1112. C# - .NET 6부터 공개된 ISpanFormattable 사용법
12820정성태9/1/20217959VC++: 147. Golang - try/catch에 대응하는 panic/recover [1]파일 다운로드1
12819정성태8/31/20218062.NET Framework: 1111. C# - FormattableString 타입
12818정성태8/31/20217297Windows: 198. 윈도우 - 작업 관리자에서 (tensorflow 등으로 인한) GPU 연산 부하 보는 방법
12817정성태8/31/20219855스크립트: 25. 파이썬 - 윈도우 환경에서 directml을 이용한 tensorflow의 AMD GPU 사용 방법
12816정성태8/30/202115232스크립트: 24. 파이썬 - tensorflow 2.6 NVidia GPU 사용 방법 [2]
12815정성태8/30/20218357개발 환경 구성: 602. WSL 2 - docker-desktop-data, docker-desktop (%LOCALAPPDATA%\Docker\wsl\data\ext4.vhdx) 파일을 다른 디렉터리로 옮기는 방법
12814정성태8/30/202110664.NET Framework: 1110. C# 11 - 인터페이스 내에 정적 추상 메서드 정의 가능 (DIM for Static Members) [2]파일 다운로드1
12813정성태8/29/20218878.NET Framework: 1109. C# 10 - (11) Lambda 개선파일 다운로드1
12812정성태8/28/20218503.NET Framework: 1108. C# 10 - (10) 개선된 #line 지시자
12811정성태8/27/20218753Linux: 44. 윈도우 개발자를 위한 리눅스 fork 동작 방식 설명 (파이썬 코드)
12810정성태8/27/20217511.NET Framework: 1107. .NET Core/5+에서 동적 컴파일한 C# 코드를 (Breakpoint도 활용하며) 디버깅하는 방법 - #line 지시자파일 다운로드1
12809정성태8/26/20218180.NET Framework: 1106. .NET Core/5+에서 C# 코드를 동적으로 컴파일/사용하는 방법 [1]파일 다운로드1
12808정성태8/25/20219431오류 유형: 758. go: ...: missing go.sum entry; to add it: go mod download ...
12807정성태8/25/20219380.NET Framework: 1105. C# 10 - (9) 비동기 메서드가 사용할 AsyncMethodBuilder 선택 가능파일 다운로드1
12806정성태8/24/20216990개발 환경 구성: 601. PyCharm - 다중 프로세스 디버깅 방법
12805정성태8/24/20218245.NET Framework: 1104. C# 10 - (8) 분해 구문에서 기존 변수의 재사용 가능파일 다운로드1
12804정성태8/24/20219026.NET Framework: 1103. C# 10 - (7) Source Generator V2 APIs
12803정성태8/23/20218597개발 환경 구성: 600. pip cache 디렉터리 옮기는 방법
... 31  [32]  33  34  35  36  37  38  39  40  41  42  43  44  45  ...