php用swcs中文分词+sphinx实现全文检索

Sphinx是什么:由俄罗斯人Andrew Aksyonoff开发的一个全文检索引擎。
Sphinx创建索引的速度:创建100万条记录的索引只需3~4分钟,创建1000万条记录的索引可以在50分钟内完成,而只包含最新10万条记录的增量索引,重建一次只需几十秒。
但是sphinx只支持英文全文检索,对中文不太铭感,于是就引入了swcs分词,其实也可以用Coreseek来建立中文索引,他的内核就是sphinx;
下面就简单说下sphinx+swcs的用法:
1.环境:
PHP 5.6+centos 6.5+mysql Ver 14.14 Distrib 5.1.73
2.安装Sphinx:【安装之前检查是否安装有gcc】

cd /usr/local/src
wget http://sphinxsearch.com/files/sphinx-2.3.1-beta.tar.gz
tar -zxf sphinx-2.3.1-beta.tar.gz 
cd sphinx-2.3.1-beta
./configure --prefix=/usr/local/sphinx --with-mysql=/usr/local/mysql
./configure --prefix=/usr/local/sphinx    --with-mysql-includes=/usr/include/mysql/  --with-mysql-libs=/usr/lib/mysql/
make && make install

3.安装sphinxclient

cd /usr/local/src/sphinx-2.3.1-beta/api/libsphinxclient/
./configure --prefix=/usr/local/sphinx/libsphinxclient
make && make install 

4.安装pecl扩展sphinx

wget https://pecl.php.net/get/sphinx-1.3.3.tgz
tar -zxf sphinx-1.3.3.tgz 
cd sphinx-1.3.3
phpize
./configure --with-sphinx=/usr/local/sphinx/libsphinxclient --with-php-config=php-config
make && make install
echo "[Sphinx]" >> /etc/php.ini  
echo extension=sphinx.so > /etc/php.ini

5.安装swcs扩展

wget http://www.xunsearch.com/scws/down/scws-1.2.2.tar.bz2
tar -jxf scws-1.2.2.tar.bz2 
cd scws-1.2.2
./configure --prefix=/usr/local/scws/  
make && make install  
cd ./phpext/  
phpize 
./configure  --with-php-config=php-config 
make && make install

echo "[scws]" >> /etc/php.ini  
echo extension = scws.so   > /etc/php.ini
echo scws.default.charset = utf-8  >> /etc/php.ini
echo scws.default.fpath = /usr/local/scws/etc >> /etc/php.ini

6.安装词库

wget http://www.xunsearch.com/scws/down/scws-dict-chs-utf8.tar.bz2
tar xvjf scws-dict-chs-utf8.tar.bz2 -C /usr/local/scws/etc/  
chown www:www /usr/local/scws/etc/dict.utf8.xdb 

7.创建测试数据

CREATE TABLE IF NOT EXISTS `items` (  
  `id` int(11) NOT NULL AUTO_INCREMENT,  
  `title` varchar(255) NOT NULL,  
  `content` text NOT NULL,  
  `created` datetime NOT NULL,  
  PRIMARY KEY (`id`)  
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COMMENT='全文检索测试的数据表' AUTO_INCREMENT=11 ;
INSERT INTO `items` (`id`, `title`, `content`, `created`) VALUES  
(1, 'linux mysql集群安装', 'MySQL Cluster 是MySQL 适合于分布式计算环境的高实用、可拓展、高性能、高冗余版本', '2016-09-07 00:00:00'),  
(2, 'mysql主从复制', 'mysql主从备份(复制)的基本原理 mysql支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器', '2016-09-06 00:00:00'),  
(3, 'hello', 'can you search me?', '2016-09-05 00:00:00'),  
(4, 'mysql', 'mysql is the best database?', '2016-09-03 00:00:00'),  
(5, 'mysql索引', '关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车', '2016-09-01 00:00:00'),  
(6, '集群', '关于MySQL索引的好处,如果正确合理设计并且使用索引的MySQL是一辆兰博基尼的话,那么没有设计和使用索引的MySQL就是一个人力三轮车', '0000-00-00 00:00:00'),  
(9, '复制原理', 'redis也有复制', '0000-00-00 00:00:00'),  
(10, 'redis集群', '集群技术是构建高性能网站架构的重要手段,试想在网站承受高并发访问压力的同时,还需要从海量数据中查询出满足条件的数据,并快速响应,我们必然想到的是将数据进行切片,把数据根据某种规则放入多个不同的服务器节点,来降低单节点服务器的压力', '0000-00-00 00:00:00'); 
CREATE TABLE IF NOT EXISTS `sph_counter` (  
  `counter_id` int(11) NOT NULL,  
  `max_doc_id` int(11) NOT NULL,  
  PRIMARY KEY (`counter_id`)  
) ENGINE=MyISAM DEFAULT CHARSET=utf8 COMMENT='增量索引标示的计数表';

8.配置sphinx.conf

##数据源items
source items {

    ##说明数据源的类型。数据源的类型可以是:mysql,pgsql,mssql,xmlpipe,odbc,python
    type = mysql

    ## 下面是sql数据库特有的端口,用户名,密码,数据库名等。
    sql_host = localhost
    sql_user = root
    sql_pass = root
    sql_db = test

    ## 如果是使用unix sock连接可以使用这个。
    # sql_sock      = /tmp/mysql.sock

    ## indexer和mysql之间的交互,需要考虑到效率和安全性。
    ## 比如考虑到效率,他们两者之间的交互需要使用压缩协议;考虑到安全,他们两者之间的传输需要使用ssl
    ## 那么这个参数就代表这个意思,0/32/2048/32768 无/使用压缩协议/握手后切换到ssl/Mysql 4.1版本身份认证。
    # mysql_connect_flags = 32


    ## 当mysql_connect_flags设置为2048(ssl)的时候,下面几个就代表ssl连接所需要使用的几个参数。
    # mysql_ssl_cert = /etc/ssl/client-cert.pem
    # mysql_ssl_key = /etc/ssl/client-key.pem
    # mysql_ssl_ca = /etc/ssl/cacert.pem

    ## mssql特有,是否使用windows登陆
    # mssql_winauth     = 1

    ## mssql特有,是使用unicode还是单字节数据。
    # mssql_unicode     = 1 # request Unicode data from server


    ## odbc的dsn串
    # odbc_dsn      = DBQ=C:\data;DefaultDir=C:\data;Driver={Microsoft Text Driver (*.txt; *.csv)};


    ## sql某一列的缓冲大小,一般是针对字符串来说的。
    ## 为什么要有这么一种缓冲呢?
    ## 有的字符串,虽然长度很长,但是实际上并没有使用那么长的字符,所以在Sphinx并不会收录所有的字符,而是给每个属性一个缓存作为长度限制。
    ## 默认情况下非字符类型的属性是1KB,字符类型的属性是1MB。
    ## 而如果想要配置这个buffer的话,就可以在这里进行配置了。
    # sql_column_buffers = content=12M, comments=1M


    ## indexer的sql执行前需要执行的操作。
    sql_query_pre = SET NAMES utf8
    sql_query_pre = SET SESSION query_cache_type = OFF
    sql_query_pre = REPLACE INTO sph_counter SELECT 1, MAX(id) FROM items


    ## 有的时候有多个表,我们想要查询的字段在其他表中。这个时候就需要对sql_query进行join操作。
    ## 而这个join操作可能非常慢,导致建立索引的时候特别慢,那么这个时候,就可以考虑在sphinx端进行join操作了。
    ## sql_joined_field是增加一个字段,这个字段是从其他表查询中查询出来的。
    ## 这里封号后面的查询语句是有要求的,如果是query,则返回id和查询字段,如果是payload-query,则返回id,查询字段和权重。
    ## 并且这里的后一个查询需要按照id进行升序排列。
    # sql_joined_field = tags from query; SELECT docid, CONCAT('tag',tagid) FROM tags ORDER BY docid ASC
    # sql_joined_field = wtags from payload-query; SELECT docid, tag, tagweight FROM tags ORDER BY docid


    ## 当数据源数据太大的时候,一个sql语句查询下来往往很有可能锁表等操作。
    ## 那么我么就可以使用多次查询,那么这个多次查询就需要有个范围和步长,sql_query_range和sql_range_step就是做这个使用的。
    ## 获取最大和最小的id,然后根据步长来获取数据。比如下面的例子,如果有4500条数据,这个表建立索引的时候就会进行5次sql查询。
    ## 而5次sql查询每次的间隔时间是使用sql_ranged_rhrottle来进行设置的。单位是毫秒。
    sql_query_range = SELECT MIN(id), MAX(id) FROM items \
                            WHERE id<=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1)
    sql_range_step = 1000
    sql_ranged_throttle = 1000

    ## indexer的sql执行语句
    sql_query = SELECT id, title, content, created, 0 as deleted FROM items \
                    WHERE id<=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1) \
                                AND id >= $start AND id <= $end
    ## 时间戳属性,经常被用于做排序
    sql_attr_timestamp = created

    ## bool属性
    sql_attr_bool = deleted
}

## sphinx的source是有继承这么一种属性的,意思就是除了父source之外,这个source还有这个特性
source items_delta : items {
    sql_query_pre = SET NAMES utf8
    sql_query_range = SELECT MIN(id), MAX(id) FROM items \
                            WHERE id > (SELECT max_doc_id FROM sph_counter WHERE counter_id=1)
    sql_query = SELECT id, title, content, created, 0 as deleted FROM items \
                    WHERE id>( SELECT max_doc_id FROM sph_counter WHERE counter_id=1 ) \
                        AND id >= $start AND id <= $end


    ## 取后查询,在sql_query执行后立即操作。
    ## 它和sql_query_post_index的区别就是执行时间不同 ## sql_query_post是在sql_query执行后执行,而sql_query_post_index是在索引建立完成后才执行。
    ## 所以如果要记录最后索引执行时间,那么应该在sql_query_post_index中执行。
    sql_query_post_index = set @max_doc_id :=(SELECT max_doc_id FROM sph_counter WHERE counter_id=1)
    sql_query_post_index = REPLACE INTO sph_counter SELECT 2, IF($maxid, $maxid, @max_doc_id)
}



#主索引
index items {
    source = items
    path = /usr/local/sphinx/var/data/items
    docinfo = extern
    morphology = none
    min_word_len = 1
    min_prefix_len = 0
    html_strip = 1
    html_remove_elements = style, script
    ngram_len = 1
    ngram_chars = U+3000..U+2FA1F
    charset_type = utf-8
    charset_table = 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F
    preopen = 1
    min_infix_len = 1
}

#增量索引
index items_delta : items {
    source = items_delta
    path = /usr/local/sphinx/var/data/items-delta
}

#分布式索引
index master {
    type = distributed
    local = items
    local = items_delta
}

indexer {
    mem_limit = 256M
}

searchd {
    listen                   = 9312
    listen                   = 9306:mysql41 #Used for SphinxQL
    log                      = /usr/local/sphinx/var/log/searchd.log
    query_log                = /usr/local/sphinx/var/log/query.log
    compat_sphinxql_magics   = 0
    attr_flush_period        = 600
    mva_updates_pool         = 16M
    read_timeout             = 5
    max_children             = 0
    dist_threads             = 2
    pid_file                 = /usr/local/sphinx/var/log/searchd.pid
    max_matches              = 1000
    seamless_rotate          = 1
    preopen_indexes          = 1
    unlink_old               = 1
    workers                  = threads # for RT to work
    binlog_path              = /usr/local/sphinx/var/data

}

9.sphinx创建索引

./indexer -c /usr/local/sphinx/etc/sphinx.conf --all  
#启动sphinx
./searchd -c /usr/local/sphinx/etc/sphinx.conf
#停止Searchd:  
./searchd -c /usr/local/sphinx/etc/sphinx.conf --stop  
#查看Searchd状态:  
./searchd -c /usr/local/sphinx/etc/sphinx.conf --status 

10.简单测试

$key = "主从备份";  
$index = "items";  

$so = scws_new();  
$so->set_charset('utf-8');    
$so->add_dict(ini_get('scws.default.fpath') . '/dict.utf8.xdb'); 
$so->set_rule(ini_get('scws.default.fpath') . 'rules.utf8.ini');  
$so->set_ignore(true); 
$so->set_multi(false);  
$so->set_duality(false);   
// 自定义词库
// $so->add_dict('./dd.txt',SCWS_XDICT_TXT);  

$so->send_text($key);  
$words_array = $so->get_result();  
$words = "";  
foreach($words_array as $v)  
{  
     $words = $words.'|('.$v['word'].')';  
}  
  
//加入全词  
#$words = '('.$key.')'.$words;  
$words = trim($words,'|');  
$so->close();  
echo '<p>输入:'.$key.'</p>';  
echo '<p>分词:'.$words.'</p>';  

$sc = new SphinxClient();  
$sc->SetServer('127.0.0.1',9312);  
#$sc->SetMatchMode(SPH_MATCH_ALL);  
$sc->SetMatchMode(SPH_MATCH_EXTENDED);  
$sc->SetArrayResult(TRUE);  
$res = $sc->Query($words,$index);
print_r($res);  
添加新评论