`
varsoft
  • 浏览: 2504055 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

[转]PostgreSQL的FTI(TSearch)与中文全文索引的实践

阅读更多

安装 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;

使用 tsearch2

OK,所有东西都安装完毕,我们需要试验一下了,下面是一个基本过程:

例子1:在原有的表上增加全文索引字段

准备全文索引的表

假设我们准备使用全文索引的数据库名字叫 BBS,首先要在这个数据库上安装 tsearch2:

psql -d BBS -f tsearch2.sql

在编译完成tsearch2之后,tsearch2.sql 在 $PGSRC/contrib/tsearch2 里面。

假设我们有这样一个表,里面保存着BBS的帖子:

create table BbsPost(
 id serial,
 topic varchar(100),
 content text,
 posterId integer,
 postTime timestamp
);
增加一个全文索引字段

现在我们希望我们能够对 topic 字段和 content 字段进行全文索引,那么我们需要添加一个字段给表

BbsPost

psql BBS
alter table BbsPost add column idxFti tsvector;

然后,我们要更新这个字段,使之包含合理的分词字段:

update BbsPost set idxFti = to_tsvector(coalesce(carriecharseg(topic, 1), '') || ' ' || 
coalesce(carriecharseg(content, 1), '')); 
创建全文索引

然后,我们需要在这个字段上创建特殊的索引:

create index bbspost_idxfti_idx on BbsPost using gist(idxFti);
清理数据库

之后,我们再清理一下数据库:

vacuum full analyze analyze;

这样,全文索引就准备完成了!

例子二:通过扩展表使用全文索引

首先,仍然对BBS数据库进行处理,这次我们不直接在原有全文字段表上增加字段,而是另外建立一个表:

create table bbsPost_fti (id integer, idxfti tsvector);

我们创建两个字段的表,用于对

bbsPost

表进行全文索引。然后,我们给

bbsPost_fti

加一个外键:

alter table bbsPost_fti add foreign key (id) references bbsPost(id);

这样保证

bbsPost_fti

的 id 一定是

bbsPost

中存在的。然后,我们可以向

bbsPost_fti

里面插入数据:

insert into bbsPost_fti (id, idxfti) select id, to_tsvector(coalesce(carriecharseg(topic, 1), '') 
|| ' ' || coalesce(carriecharseg(content, 1), '')) from bbsPost order by id;

这样,初始化所有数据到

bbsPost_fti

中。然后创建索引:

create index idxfti_idx on bbsPost_fti using gist(idxfti);
create index bbsPost_fti_id_idx on bbsPost_fti (id);

我们也可以用倒排索引(GIN)进行全文索引,这样检索速度比 GIST 更快:

create index idxfti_idx on bbsPost_fti using gin(idxfti);

在我的试验中,GIN比GIST快了将近5倍,唯一的缺点是更新速度明显变慢。这个需要用户自己去平衡了。

最后,也是清理数据库:

vacuum full analyze;

然后我们也可以开始使用了!

使用全文索引

我们上面用的是分字程序,那么我们可以这样使用SQL查询来进行数据检索:

select * from BbsPost where idxFti @@ '中&文';

这样,就把包含“中”和“文”字的帖子都找了出来。@@ 操作符还支持与或非的逻辑:

select * from BbsPost where idxFti @@ '(中&文)|(英&文)';

用圆括弧:() 和 & 和 | 和! 可以组合查询条件,对数据表进行查询。目前,我们只拥有分字的程序,将来在完成中文分词程序之后,我们可以实现更简单的搜索,类似:

select * from BbsPost where idxFti @@ '(中文)|(英文)&(中国)';

这样的查询,大家可以用 explain analyze 看看,这样的查询的效率和 like 查询的效率差距有多大?;)

中文屏蔽词的添加

在语言里面有很多没有用的屏蔽词,比如英文里面的 "the",那么中文也有许多这样的词,比如“的”字,在这些词上建立索引,没必要也浪费,因此,我们可以用一种叫做 stop word (屏蔽词)的机制,给关闭这些词。具体做法是:

  • 找到$PGHOME/share/contrib/
  • echo "的" | iconv -f gbk -t utf8 > chinese.stop.utf8 (如果你的终端是UTF8的,则可以忽略 iconv 语句。)
  • 更多的 chinese stop word 可以用 echo "屏蔽词" | iconv -f gbk -t utf8 >> chinese.stop.utf8 实现


至此,使用 tsearch2 进行中文全文索引的最基本的要点已经在此列出,更多相关的信息,我会在有进一步的试验结果之后再详细展开。

【参考】

另外一个中文PostgreSQL分词组件参考:http://code.google.com/p/nlpbamboo/wiki/TSearch2

TSearch官方网站和文档:http://www.sai.msu.su/~megera/postgres/gist/tsearch/V2/

PostgreSQL官方TSearch文档参考:http://www.postgresql.org/docs/8.3/static/textsearch.html
Using tsearch with PostgreSQL:http://www.michaelhinds.com/tech/pg/tsearch.html

本文来源:http://www.pgsqldb.org/mwiki/index.php/PostgreSQL%E7%9A%84FTI%E4%B8%8E%E4%B8%AD%E6%96%87%E5%85%A8%E6%96%87%E7%B4%A2%E5%BC%95%E7%9A%84%E5%AE%9E%E8%B7%B5#.E4.B8.AD.E6.96.87.E5.B1.8F.E8.94.BD.E8.AF.8D.E7.9A.84.E6.B7.BB.E5.8A.A0

分享到:
评论

相关推荐

    PostgreSQL中文全文索引技术研究

    ### PostgreSQL中文全文索引技术研究 #### 摘要与背景 随着信息技术的发展,数据库管理系统在数据管理和查询方面发挥着至关重要的作用。其中,PostgreSQL作为一款强大的开源关系型数据库系统,因其高度可扩展性和...

    postgreSQL 拼音字库 汉字转拼音 8.4

    网上传的postgreSQL汉字转拼音的方法,是针对9.3的版本(相信你要是8.4的也搜到了但不能直接用,才来看我的),尝试了8.4的版本来做拼音处理。发现网上的《postgreSQL汉字转拼音》里面坑比较多,尝试纠正了一下,发...

    MySQL与PostgreSQL数据库高可用分析实践.pdf

    MySQL与PostgreSQL数据库高可用分析实践 本文档详细介绍了MySQL和PostgreSQL数据库高可用的分析实践,涵盖了高可用原理、MySQL高可用和PostgreSQL高可用等方面的知识点。 一、高可用原理 高可用是指系统在无中断...

    mysql转换postgresql工具

    国外大牛写的一个mysql数据库转换postgresql的脚本。亲试可以无错误运行。

    postgresql汉字转拼音函数

    支持2万+汉字转拼音

    postgresql汉字转拼音首字母函数

    支持2万+汉字转拼音首字母

    PostgreSQL中文手册9.2

    PostgreSQL中文学习手册 PostgreSQL PostgreSQL PostgreSQL学习手册 学习手册 学习手册 (数据表 数据表 ) 4 一、表的定义: 一、表的定义: 一、表的定义: . 4 PostgreSQL PostgreSQL PostgreSQL学习手册 学习手册...

    PostgreSQL与腾讯云国产数据库技术实践.pptx

    PostgreSQL与腾讯云国产数据库技术实践 PostgreSQL是开源关系数据库管理系统,具有高性能、可靠性和可扩展性等特点。腾讯云国产数据库技术实践则是基于PostgreSQL的国产数据库解决方案,旨在提供高性能、可靠性和...

    DB2到GreenPlum/PostgreSQL的转换指南

    - **主键**:DB2中的主键定义与GreenPlum/PostgreSQL相似,但需要注意的是,在GreenPlum/PostgreSQL中,主键约束通常会自动创建一个唯一索引。 - **外键**:DB2支持外键约束,但在GreenPlum/PostgreSQL中,外键约束...

    MySQL和PostgreSQL的比较

    ### MySQL与PostgreSQL对比分析 #### 一、实例与服务管理 **MySQL**与**PostgreSQL**在实例和服务管理方面存在显著差异。MySQL通过执行`mysqld`命令启动实例,一个实例能够管理一个或多个数据库,且一台服务器可...

    postgresql汉字转拼音首字母函数,用于批量生成助记码、拼音码,包含两万多个汉字

    postgresql汉字转拼音首字母函数,用于批量生成助记码、拼音码,包含两万多个汉字。

    PostgreSQL 14.1 中文手册

    PostgreSQL 14.1 手册 PostgreSQL 全球开发组 翻译:彭煜玮1,PostgreSQL中文社区2文档翻译组

    PostgreSQL12.2中文手册.chm.7z

    4. **索引与查询优化**:索引是提升查询速度的关键,PostgreSQL支持B树、哈希、GiST、SP-GiST、GIN和BRIN等多种索引类型。手册将详细解析如何创建、管理和使用索引,以及如何通过EXPLAIN分析查询计划进行优化。 5. ...

    sql全文索引 sphinx

    SQL全文索引是一种在数据库中实现全文搜索的技术,它允许用户使用自然语言查询数据库,而不仅仅是精确匹配。Sphinx是一个高性能、开源的全文搜索引擎,它为SQL数据库提供了强大的全文索引功能。Sphinx与SQL结合使用...

    PostgreSQL分区表实践与思考.pptx

    PostgreSQL 分区表实践与思考 PostgreSQL 分区表是指将一个大型物理表分解成多个小型物理表,以便于管理和优化数据库性能。分区表的出现是为了解决大型表带来的性能问题,例如历史数据归档、空间问题和查询性能...

    PostgreSQL高维向量检索索引插件介绍.pptx

    PostgreSQL高维向量检索索引插件介绍 PostgreSQL高维向量检索索引插件是为了解决高维向量检索问题的一种解决方案。高维向量检索是指在高维空间中搜索最近邻居的过程。本文将介绍PostgreSQL高维向量检索索引插件的...

    PostgreSQL中文全文搜索zhparser.zip

    Zhparser 是一个 PostgreSQL 的扩展,用于中文的全文搜索。实现了一个中文解析器,基于 SCWS。 标签:zhparser

    PostgreSQL11.2-中文手册.pdf

    经过PostgreSQL中文社区文档翻译小组的不懈努力,PostgreSQL 11.2手册终于翻译完成正式上线了! 《PostgreSQL 11.2手册》是基于《PostgreSQL 10.1手册》翻译,并且合并了武汉大学彭煜玮老师翻译的《PostgreSQL ...

    postgresql索引类型区别

    本文将详细介绍 PostgreSQL 中的多种索引类型,包括 Hash 索引、B-tree 索引、GiST 索引、GIN 索引和 SP-GiST 索引,帮助读者更好地理解和选择合适的索引类型。 Hash 索引 --------- Hash 索引是一种特殊的索引...

Global site tag (gtag.js) - Google Analytics