今天翻了一下meidawiki的源代码,由于它的中文搜索不太准确,想查查原因,就看了一下它的搜索是如何实现的。
数据库是mysql,使用了全文索引表进行搜索
1 2 3 4 5 6 7 8 | CREATE TABLE `searchindex` ( `si_page` int (10) unsigned NOT NULL , `si_title` varchar (255) NOT NULL DEFAULT '' , `si_text` mediumtext NOT NULL , UNIQUE KEY `si_page` (`si_page`), FULLTEXT KEY `si_title` (`si_title`), FULLTEXT KEY `si_text` (`si_text`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 |
mysql的FULLTEXT 对中文的支持一直不太好,如果直接用utf8字符串的话,没有分词分隔符,所以索引就没有效果,wiki通过取巧的方法,把utf8字符转换成U8xxxx进行保存,用英文空格分隔,所以就可以搜索了。
wiki的字符转换代码,比较有用,呵呵:
cat wiki/languages/classes/LanguageZh_cn.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?php /** * @addtogroup Language */ class LanguageZh_cn extends Language { function stripForSearch( $string ) { # MySQL fulltext index doesn't grok utf-8, so we # need to fold cases and convert to hex # we also separate characters as "words" if ( function_exists( 'mb_strtolower' ) ) { return preg_replace( "/([\\xc0-\\xff][\\x80-\\xbf]*)/e" , "' U8' . bin2hex( \"$1\" )" , mb_strtolower( $string ) ); } else { list( , $wikiLowerChars ) = Language::getCaseMaps(); return preg_replace( "/([\\xc0-\\xff][\\x80-\\xbf]*)/e" , "' U8' . bin2hex( strtr( \"\$1\", \$wikiLowerChars ) )" , $string ); } } } |
上面的代码就会把汉字转换为U8xxxx空格,然后就可以使用mysql的full text索引了,其实5.0之后的mysql可以使用utf8字符做全文索引了,但是由于分词的问题,还是需要把每个汉字用空格分开,而且要设置最小索引字符长度才行,所以还是wiki的这种方式方便。
因为它是一个汉字作为一个词,没有按顺序搜索,所以最后结果和中国人的语言习惯不太一样,其实只需要改一下源代码,使用冒号封装短语,就可以得出比较精确的结果了。
vim wiki/includes/SearchMySQL4.php
找到以下代码
1 2 3 4 | if ( $this ->strictMatching && ( $terms [1] == '' ) ) { $terms [1] = '+' ; } $searchon .= $terms [1] . $wgContLang ->stripForSearch( $terms [2] ); |
修改为
1 2 3 4 5 | if ( $this ->strictMatching && ( $terms [1] == '' ) ) { // $terms[1] = '+'; $terms [1] = '+"' ; } $searchon .= $terms [1] . $wgContLang ->stripForSearch( $terms [2] ) . '"' ; |
即可精确搜索。
已经修改仅是针对旧版本的wiki,如果是最新版本的话,已经自带了对应的逻辑,如果对这个结果还不满意的话,那只能用外部的扩展自己实现Mysql索引的这一块了,如果是公共网站的话最简单的方法就是用google搜索+site标签指定。呵呵。
附上新版的SearchMySQL.php中这部分的代码,写得比较隐晦,哈哈
1 2 3 4 5 6 7 8 9 10 11 12 | if ( preg_match_all( '/([-+<>~]?)(([' . $lc . ']+)(\*?)|"[^"]*")/' , $filteredText , $m , PREG_SET_ORDER ) ) { foreach ( $m as $bits ) { @list( /* all */ , $modifier , $term , $nonQuoted , $wildcard ) = $bits ; if ( $nonQuoted != '' ) { $term = $nonQuoted ; $quote = '' ; } else { $term = str_replace ( '"' , '' , $term ); $quote = '"' ; } |
看上去中文的会匹配 $quote = ‘”‘; 这个结果,就是自动帮你加上引号了。
July 16th, 2010 11:55:00
楼主,请问一下我按照你的设置了,好像不好使呢,为什么啊?
August 6th, 2010 13:10:03
呵呵,可以加我的qq大家讨论下。
January 22nd, 2011 03:25:59
是什么版本 1.15还是1.16?
你的wordpress有email回复吗?
January 25th, 2011 19:01:58
MediaWiki 1.11.1
公司的,很老了,也懒得升级了
估计新版本有些不同了吧,如果还是用mysql全文索引的话你看看应该也能优化下,呵呵。
April 17th, 2011 18:52:41
Hi~ 我搭建了自己的私人wiki使用的是以下配置
MediaWiki 1.15.1
PHP 5.3.2-1ubuntu4.7 (apache2handler)
MySQL 5.1.41-3ubuntu12.10
安装是通过ubuntu的apt-get方式。
使用了这篇文章中的修改方法。不过SearchMySQL4.php这个文件是空的(也许因为我用的是mysql5),所以我修改了SearchMySQL.php。
没有什么变化囧。
希望能加QQ讨论.(如果有邮箱,MSN,Gtalk之类的,我不太用QQ^_^)。
谢谢~~
April 19th, 2011 23:59:32
呵呵,我也不太上msn,邮箱是ssmax@126.com ,邮件交流下也行,你可以把你那两个文件发给我瞅瞅。
September 26th, 2011 16:19:10
话说这个问题现在怎么办?
September 27th, 2011 11:49:05
这个是没啥办法解决的了,新版已经和我的这种做法差不多了,要再精确一点除非针对中文进行分词了,wiki的做法其实偷懒,但是比较容易实现,一元分词,呵呵。