热门关键字:  ubuntu  分区  函数  Fedora  linux系统进程

PostgreSQL的FTI与中文全文索引的实践

来源: 作者: 时间:2008-05-23 Tag: 点击:

From PgsqlWiki

Jump to: navigation, search

 安装 postgresql

你可以像平常一样编译和安装 postgresql,使用 tsearch2 进行中文的全文索引的时候,真正的区别发生在初始化数据库的时候。

初始化数据库

在linux里面使用tsearch2,首先要把数据库初始化成支持中文的 locale,比如我用 zh_CN.utf8:

initdb --locale=zh_CN.utf8 --encoding=utf8 ... 

在一般用途的postgresql的使用时,一般会建议使用 C 做为初始化 locale,这样PG将会使用自身内部的比较函数对各种字符(尤其是中文字符)进行排序,这么做是合适的,因为大量OS的locale实现存在一些问题。对于tsearch2,因为它使用的是locale来进行基础的字串分析的工作,因此,如果错误使用locale,那么很有可能得到的是空字串结果,因为多字节的字符会被当做非法字符过滤掉。

我正在评估现代 OS/libc 的 locale 的实现是否已经成熟到可以接受的程度。如果是,则我们可以安全地使用 之,如果否,可能我们必须接合 slony 等工具来使用tsearch2。

安装 tsearch2

安装 tsearch2很简单,首先:

cd $PGSRC/contrib/tsearch2

然后:

gmake all
gmake install

 配置tsearch2

我们需要生成自己的字典和自己的对应locale 的配置选项,所以,要给 tsearch2的表里插入一些数据,简单起见,我先用一个最基本的做演示,将来丰富了中文独立的字典之后,将继续补充:

--
-- first you need to use zh_CN.utf8 as locale to initialize your database
-- initdb -D $YOUR_PG_DATA_DIR --locale zh_CN.utf8 --encoding utf8
--


insert into pg_ts_cfg (ts_name, prs_name, locale) values ('utf8_chinese', 'default', 
'zh_CN.utf8');

insert into pg_ts_dict (dict_name, dict_init, dict_initoption, dict_lexize, dict_comment) 
 values ('chinese_stem_utf8', 'snb_ru_init_utf8(internal)', 'contrib/chinese.stop.utf8', 
'snb_lexize(internal,internal,integer)', 'Chinese Stemmer, Laser. UTF8 Encoding');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'lword',
'{en_stem}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'nlword', 
'{chinese_stem_utf8}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'word', 
'{chinese_stem_utf8}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'email', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'url', '{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'host', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'sfloat', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'version', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'part_hword', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'nlpart_hword', 
'{chinese_stem_utf8}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'lpart_hword', 
'{en_stem}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'hword', 
'{chinese_stem_utf8}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'lhword', 
'{en_stem}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'nlhword', 
'{chinese_stem_utf8}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'uri', '{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'file', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'float', 
'{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'int', '{simple}');

insert into pg_ts_cfgmap(ts_name,tok_alias, dict_name) values('utf8_chinese', 'uint', 
'{simple}');

 基础的分字程序

下面是 Carrie (感谢 Carrie! :D )写的一个基础的分字程序,在很大程度上可以满足相当多应用的需要:

--
-- a basic Chinese word segment function
-- author: Carrie
--

create or replace function CarrieCharSeg( input text ) returns text as $
declare
        retVal text;
        i int;
        j int;
begin
        i := char_length(input);
        j := 0;
        retVal := '';
        LOOP
                retVal := retVal || substring(input from j for 1) || ' ';

                j := j+1;

        EXIT WHEN j=i+1;
        END LOOP;
        return retVal;
end;
$language plpgsql;

下面是一个重载的分词程序,区分了单词和汉字,对单词单独进行分词:

--
-- a basic Chinese word segment function
-- author: Carrie
--
create or replace function CarrieCharSeg(input text,int) returns text as $Q$
declare
        query text := '';
        retVal text := '';
        thisVal text := '';
        lastVal text := '';
        i integer := 0;
        j integer := 0;

begin
        query := lower(regexp_replace(input,'[[:punct:]]',' ','g'));
        --raise notice '123: %',query;
        i := char_length(query);
        LOOP
                thisVal := substring(query from j for 1);
                IF ((thisVal <> ' ')AND(thisVal <> ' ')) THEN
                        IF (((lastVal >= 'a') AND (lastVal <= 'z'))
                            OR((lastVal >= 'A')AND(lastVal <= 'Z'))) AND
                            (((thisVal >= 'a') AND (thisVal <= 'z')) OR
                            ((thisVal >= 'A')AND(thisVal <= 'Z'))) THEN
                                        retVal := retVal || thisVal;
                        ELSE
                                retVal := retVal || ' ' || thisVal;
                        END IF;
                END IF;
                lastVal := thisVal;
                j := j+1;
                EXIT WHEN j > i;
        END LOOP;
        RETURN trim(retVal);
end
$Q$language plpgsql;
 
最新评论共有 0 位网友发表了评论
发表评论
评论内容:不能超过250字,需审核,请自觉遵守互联网相关政策法规。
用户名: 密码:
匿名?
注册