database.sarang.net
UserID
Passwd
Database
DBMS
MySQL
ㆍPostgreSQL
Firebird
Oracle
Informix
Sybase
MS-SQL
DB2
Cache
CUBRID
LDAP
ALTIBASE
Tibero
DB 문서들
스터디
Community
공지사항
자유게시판
구인|구직
DSN 갤러리
도움주신분들
Admin
운영게시판
최근게시물
PostgreSQL Devel 9488 게시물 읽기
No. 9488
testsearch-ja 모듈을 이용한 우리말 full text search
작성자
김상기(ioseph)
작성일
2014-11-25 01:47ⓒ
2014-11-25 01:50ⓜ
조회수
8,262

textsearch-ja 모듈을 이용한 우리말 full text search

들어가며

이 글에서는 PostgreSQL에서 한글 기반 전문 검색 full text search 기능을 사용하는 것에 대한 한 방법을 소개합니다. 
 
database.sarang.net에서는 아주 오래 전부터 한글 full text search에 대한 논의를 여러 번 해왔지만, 지금까지 뚜렷한 성과가 없었습니다. 
 
PostgreSQL에서 제공하는 full text search 기능은 꽤 쓸만한데, 한글 형태소 분석기와, 한국어 사전이 마땅치 않아서 아까운 기능을 못 쓰고 있었습니다. 
 
우연찮게  은전한닢 프로젝트를 알게 되었고, 이것을 PostgreSQL 안으로 녹일 가능성이 보여 이 작업을 진행 보았습니다. 
 
조금은 미흡하지만, 그럭저럭 쓸만한 결과가 나와서 이 글에서 공유합니다.
 

사전준비

세부분이 필요합니다.

mecab-ko

mecab 프로젝트의 한국어판이라는데, 뭘 고쳤는지는 모르겠지만, 이 프로그램을 설치합니다.

$ git clone https://bitbucket.org/eunjeon/mecab-ko.git
$ cd mecab-ko
$ ./configure 
$ make all && make install

mecab-ko-dic

한국어 사전을 만듭니다. 이놈은 git clone으로 잘 안되네요.
https://bitbucket.org/eunjeon/mecab-ko-dic/downloads
페이지의 마지막 파일을 다운로드하고 압축 풀고 설치합니다.
$ wget https://bitbucket.org/eunjeon/mecab-ko-dic/downloads/mecab-ko-dic-1.6.1-20140814.tar.gz
$ tar xzf mecab-ko-dic-1.6.1-20140814.tar.gz
$ cd mecab-ko-dic-1.6.1-20140814
$ ./configure
$ make all && make install

형태소분석기 테스트

모두 설치가 끝나면 이 프로그램이 정상 작동하는지를 테스트해봅니다.
$ echo '설치가 완료되었습니다.' | mecab
설치    NNG,*,F,설치,*,*,*,*,*
가      JKS,*,F,가,*,*,*,*,*
완료    NNG,*,F,완료,*,*,*,*,*
되      XSV,*,F,되,*,*,*,*,*
었      EP,*,T,었,*,*,*,*,*
습니다  EF,*,F,습니다,*,*,*,*,*
.       SF,*,*,*,*,*,*,*,*
EOS

잘 안되면 mecab-ko 프로젝트 작업자에게 연락하세요.

이제 이 프로그램을 textsearch-ja 모듈에 심는 작업을 진행합니다.

textsearch-ja

이 모듈은 PostgreSQL 8.4.x 버전(?)부터 있었던 아주 오래된 일본에서 만든 PostgreSQL 확장 모듈입니다.
일단 설치.
$ wget http://pgfoundry.org/frs/download.php/2943/textsearch_ja-9.0.0.tar.gz
$ tar xzf textsearch_ja-9.0.0.tar.gz
$ cd textsearch_ja-9.0.0

해킹

이 모듈은 일본어 전용으로 만들어졌습니다.

최소한의 변경으로 한국어 지원을 가능하도록 이리 저리 소스를 살펴보고, 다음 정도만 고치면 충분히 한국어 환경에서 사용할 수 있겠더군요.

$ diff textsearch_ja.c textsearch_ko.c
39c39
< #define MECAB_BASIC           6       /* 基本形 */
---
> #define MECAB_BASIC           3       /* 基本形 */

$ diff encoding_utf8.c.orig encoding_utf8.c
453a454,479
> /* for korean */
> static const char JOSA_JKS[] = "JKS,";
> static const char JOSA_JKC[] = "JKC,";
> static const char JOSA_JKG[] = "JKG,";
> static const char JOSA_JKO[] = "JKO,";
> static const char JOSA_JKB[] = "JKB,";
> static const char JOSA_JKV[] = "JKV,";
> static const char JOSA_JKQ[] = "JKQ,";
> static const char JOSA_JX[] = "JX,";
> static const char JOSA_JC[] = "JC,";
> static const char KOSIGN_SF[] = "SF,";
> static const char KOSIGN_SE[] = "SE,";
> static const char KOSIGN_SSO[] = "SSO,";
> static const char KOSIGN_SSC[] = "SSC,";
> static const char KOSIGN_SC[] = "SC,";
> static const char KOSIGN_SY[] = "SY,";
> static const char KONN_NNB[] = "NNB,";
> static const char KONN_NP[] = "NP,";
> static const char KOIC[] = "IC,";
> static const char KOTAIL_EP[] = "EP,";
> static const char KOTAIL_EF[] = "EF,";
> static const char KOTAIL_EC[] = "EC,";
> static const char KOTAIL_ETN[] = "ETN,";
> static const char KOTAIL_ETM[] = "ETM,";
>
>
456,463c482,512
<       { lengthof(JOSHI), JOSHI },             /* 助詞 */
<       { lengthof(JODOU), JODOU },             /* 助動詞 */
<       { lengthof(KIGOU), KIGOU },             /* 記号 */
<       { lengthof(BYWORD), BYWORD },   /* 名詞,代名詞 */
<       { lengthof(INSUFF), INSUFF },   /* 名詞,非自立 */
<       { lengthof(KANDO), KANDO },             /* 感動詞 */
<       { lengthof(FILLER), FILLER },   /* フィラー */
<       { lengthof(OTHERS), OTHERS },   /* その他 */
---
> //    { lengthof(JOSHI), JOSHI },             /* 助詞 */
> //    { lengthof(JODOU), JODOU },             /* 助動詞 */
> //    { lengthof(KIGOU), KIGOU },             /* 記号 */
> //    { lengthof(BYWORD), BYWORD },   /* 名詞,代名詞 */
> //    { lengthof(INSUFF), INSUFF },   /* 名詞,非自立 */
> //    { lengthof(KANDO), KANDO },             /* 感動詞 */
> //    { lengthof(FILLER), FILLER },   /* フィラー */
> //    { lengthof(OTHERS), OTHERS },   /* その他 */
>       { lengthof(JOSA_JKS)-1, JOSA_JKS }, /* 조사들 */
>         { lengthof(JOSA_JKC)-1, JOSA_JKC},
>         { lengthof(JOSA_JKG)-1, JOSA_JKG},
>         { lengthof(JOSA_JKO)-1, JOSA_JKO},
>         { lengthof(JOSA_JKB)-1, JOSA_JKB},
>         { lengthof(JOSA_JKV)-1, JOSA_JKV},
>         { lengthof(JOSA_JKQ)-1, JOSA_JKQ},
>         { lengthof(JOSA_JX)-1, JOSA_JX},
>         { lengthof(JOSA_JC)-1, JOSA_JC},
>         { lengthof(KOSIGN_SF)-1, KOSIGN_SF}, /* 기호들 */
>         { lengthof(KOSIGN_SE)-1, KOSIGN_SE},
>         { lengthof(KOSIGN_SSO)-1, KOSIGN_SSO},
>         { lengthof(KOSIGN_SSC)-1, KOSIGN_SSC},
>         { lengthof(KOSIGN_SC)-1, KOSIGN_SC},
>         { lengthof(KOSIGN_SY)-1, KOSIGN_SY},
>         { lengthof(KONN_NNB)-1, KONN_NNB}, /* 의존명사 */
>         { lengthof(KONN_NP)-1, KONN_NP},   /* 대명사 */
>         { lengthof(KOIC)-1, KOIC},         /* 감탄사 */
>         { lengthof(KOTAIL_EP)-1, KOTAIL_EP},         /* 선어말어미 */
>         { lengthof(KOTAIL_EF)-1, KOTAIL_EF},         /* 종결어미 */
>         { lengthof(KOTAIL_EC)-1, KOTAIL_EC},         /* 연결어미 */
>         { lengthof(KOTAIL_ETN)-1, KOTAIL_ETN},         /* 명사전성어미 */
>         { lengthof(KOTAIL_ETM)-1, KOTAIL_ETM},         /* 관형사전성어미 */

두 개 파일만 수정하고, 모듈을 만듭니다.

$ make USE_PGXS=1
$ make USE_PGXS=1 install

이 작업은 알아서 잘 하세요.

테스트

모듈설치가 잘 끝났으면, 이제 이 모듈을 사용하는 함수와 textsearch 관련 환경 설정을 합니다.

다음은 install_textsearch_ko.sql 파일의 내용입니다.
이것을 실행하면 해당 데이터베이스에 한국어 full text search 기능을 사용할 수 있습니다.

CREATE FUNCTION ts_ja_start(internal, integer) RETURNS internal
    LANGUAGE c STRICT
    AS '$libdir/textsearch_ja', 'ts_ja_start';

CREATE FUNCTION ts_ja_gettoken(internal, internal, internal) RETURNS internal
    LANGUAGE c STRICT
    AS '$libdir/textsearch_ja', 'ts_ja_gettoken';

CREATE FUNCTION ts_ja_end(internal) RETURNS void
    LANGUAGE c STRICT
    AS '$libdir/textsearch_ja', 'ts_ja_end';

CREATE TEXT SEARCH PARSER pg_catalog.korean (
    START    = ts_ja_start,
    GETTOKEN = ts_ja_gettoken,
    END      = ts_ja_end,
    HEADLINE = pg_catalog.prsd_headline,
    LEXTYPES = pg_catalog.prsd_lextype
);

CREATE FUNCTION ts_ja_lexize(internal, internal, internal, internal) RETURNS internal
    LANGUAGE c STRICT
    AS '$libdir/textsearch_ja', 'ts_ja_lexize';

CREATE TEXT SEARCH TEMPLATE pg_catalog.mecab (
        LEXIZE = ts_ja_lexize
);

CREATE TEXT SEARCH DICTIONARY pg_catalog.korean_stem (
        TEMPLATE = pg_catalog.mecab
);

CREATE TEXT SEARCH CONFIGURATION pg_catalog.korean(PARSER = korean);

ALTER TEXT SEARCH CONFIGURATION pg_catalog.korean ADD MAPPING
    FOR email, url, url_path, host, file, version,
        sfloat, float, int, uint,
        numword, hword_numpart, numhword
    WITH simple;

ALTER TEXT SEARCH CONFIGURATION pg_catalog.korean ADD MAPPING
    FOR asciiword, hword_asciipart, asciihword
    WITH english_stem;

ALTER TEXT SEARCH CONFIGURATION pg_catalog.korean ADD MAPPING
    FOR word, hword_part, hword
    WITH korean_stem;

CREATE FUNCTION web_query(text) RETURNS text AS
$$
  SELECT regexp_replace(regexp_replace(regexp_replace($1,
    E'(^|\s+)-', E'\1!', 'g'),
    E'\s+OR\s+', '|', 'g'),
    E'\s+', '&', 'g');
$$
LANGUAGE sql IMMUTABLE STRICT;

작업 최소화를 위해서 쓸 수 있는 것은 일본 것 그대로 사용했습니다.

이 작업이 다 끝나면, 이제 실재 쿼리를 실행해서 의도된 대로 작동 하는지 확인해봅니다.

$ psql
psql (9.3.5)
Type "help" for help.

postgres=# select to_tsvector('korean','설치가 완료되었습니다');
       to_tsvector
--------------------------
 '되':3 '설치':1 '완료':2
(1 row)

나머지는 http://postgresql.kr/docs/current/textsearch.html 페이지에서 설명하고 있는 그대로 하면 됩니다.

마무리

아무 생각 없이 막 작업을 하면서 기록을 남겨 놓으니, 글이 많이 어수선 하지만, 개인적으로는 지금까지 작업한 한글 형태소 분석 기반 처리 가운데 그나마 제일 깔끔했습니다.

이 모듈은 엄격하게 따지면, 일본 mecab 프로젝트에 기반을 두고 있는데, 이 프로젝트의 라이선스가 GPL, LGPL, BSD 온갖 라이선스를 다 쓰고 있다고 합니다. 구체적으로 어떻게 쓰고 있고, 실무에서 어떻게 영향을 주는지는 모르겠지만, 일단 KST 프로젝트의 GPL 보다는 좀 더 유연한 것은 확실 할 것 같습니다. 구체적인 라이선스 영향에 대해서는 저보다는 오픈소스 라이선스 전문가가 살펴보는 것이 좋을 것 같습니다.

작업을 마무리 하면서 한 가지 아쉬운 점이 있다면, 동사 활용에 있어 동사 원형을 사용하는 것과 제외 단어들의 설정이 없다는 것입니다. 이 부분에 대한 것은 관심 있는 분의 참여로 남겨두어야 할 것 같습니다.  또한 원래의 mecab 프로젝트와 협업하여, 한국어와 일본어를 아우르는 모습으로 바뀌어가면 더 좋겠죠.

이 글에 대한 댓글이 총 1건 있습니다.

 이 작업은 

https://github.com/i0seph/textsearch_ko

공개 프로젝트로 전환해서 textsearch_ja 프로젝트랑 분리를 했습니다.

 

김상기(ioseph)님이 2014-12-18 12:51에 작성한 댓글입니다.
[Top]
No.
제목
작성자
작성일
조회
10432windows 환경에서 pg_hint_plan 빌드하기
김상기
2023-05-16
311
10420plpython3u 확장 모듈을 이용한 사용자 정의 함수 본문 코드 숨기기
김상기
2023-04-28
390
9594한글 두벌식 자판 영타 변환 [1]
김상기
2015-09-22
5847
9488testsearch-ja 모듈을 이용한 우리말 full text search [1]
김상기
2014-11-25
8262
8596full text search를 호출하는 트리거
김상기
2010-01-06
11123
8595full text search 또 한 번 바꾸어 보았습니다.
김상기
2010-01-05
9761
61241차원 배열 요소 삭제 함수 - anyarray, anyelement 사용 예제 [4]
김상기
2005-06-01
13851
Valid XHTML 1.0!
All about the DATABASE... Copyleft 1999-2023 DSN, All rights reserved.
작업시간: 0.052초, 이곳 서비스는
	PostgreSQL v16.1로 자료를 관리합니다