[转]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很简单,首先: 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,'nlword','{chinese_stem_utf8}'); insert into pg_ts_cfgmap(ts_name,'word','email','{simple}'); insert into pg_ts_cfgmap(ts_name,'url','host','sfloat','version','part_hword','nlpart_hword','lpart_hword','hword','lhword','nlhword','uri','file','float','int','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,所有东西都安装完毕,我们需要试验一下了,下面是一个基本过程: 准备全文索引的表 假设我们准备使用全文索引的数据库名字叫 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,''));创建全文索引 然后,我们需要在这个字段上创建特殊的索引: 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,'') || ' ' || coalesce(carriecharseg(content,'')) 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 (屏蔽词)的机制,给关闭这些词。具体做法是:
【参考】 另外一个中文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 本文来源: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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
- 正则表达式:如果某行不以特定字符串开头,请将其添加为前缀
- c – 在rdstate和rdbuf中rd代表什么?
- React createClass 和 Component 有什么区别
- c# – `fixed` vs GCHandle.Alloc(obj,GCHandleType.Pinned
- userdefault 存储archivedData ,JSONmodel兼容
- 使用Flex Bison 和LLVM编写自己的编译器
- objective-c – Rails RestKit POST请求缺少json的根类
- xml读取异常Invalid byte 1 of 1-byte UTF-8 sequence
- 命名空间“System.Data”中不存在类型或命名空间名称“SQLi
- Cocos Studio和Cocos2d-x版本对应关系 附1.6Cocostudio版本