您的位置:首页 > 编程语言 > PHP开发

windows sphinx postgresql php 系统中文检索为0---都是编码惹的祸

2012-06-03 17:01 375 查看
首先介绍我的搭建的这个系统,主要参考了张晏的一个博客《基于Sphinx构建准实时更新的分布式通用搜索引擎平台》,分布式那一块现在还没有能力去实施。



系统的安装,在网络上特别是coreseek 中文网站有比较详细的资料。虽然coreseek 是在sphinx 0.9.X版本基础之上开发研究出来并加上中文分词模块,但是两者在全文搜索方面的是大同小异的。

安装完之后,首先需要做的就是完成一些基本的测试:

在建立索引之前,建议一定不要忘记了,查询一下自己所已建立的数据库的编码方式是什么样的。sphinx默认建立索引的编码方式是UTF-8,如果数据库的编码方式不是UTF-8的话,那很可能会给后面索引的建立以及查询带来致命的麻烦。

我本人就出现了这个问题。数据库编码方式选择的是SQL_ACSII,sphinx索引编码方式是UTF-8,PHP网页的编码方式是GBK,结果死活搜索不出中文关键字。下面就将我怎么样解决这个问题的全过程做一个记录。

一、检查数据库编码方式:

服务器的编码方式:

mydb=# \l
List of databases
Name    | Owner | Encoding
-----------+-------+-----------
mydb      | jun   | UTF8
postgres  | jun   | SQL_ASCII
template0 | jun   | SQL_ASCII
template1 | jun   | SQL_ASCII

客户端的编码方式:
mydb=# show client_encoding;
client_encoding
-----------------
GBK
(1 row)


二、在windows console下测试indexer :

1、sphinx.conf配置文件如下:

source test
{
type   = pgsql

#####################################################################
## SQL settings (for 'mysql' and 'pgsql' types)
#####################################################################
sql_host  	= localhost
sql_user  	= jung
sql_pass  	= 123456
sql_db    	= mydb
sql_port  	= 5432 # optional, default is 3306
sql_query_pre  = SET NAMES 'utf8'
sql_query  	= SELECT id,auhor_id,title,content FROM documents

sql_attr_uint  = auhor_id
}

#############################################################################
## index definition
#############################################################################

index test
{
source   	= test
path   	= ..\data\test
docinfo   	= extern
mlock   	= 0
morphology  	= none
min_word_len 	= 1
charset_type  	= utf-8
min_prefix_len =0
html_strip  	= 1
charset_table 	= 0..9, A..Z->a..z, _, a..z, U+410..U+42F->U+430..U+44F, U+430..U+44F

ngram_len  	= 1
ngram_chars  	= U+3000..U+2FA1F

}

.....
(indexer 和 searchd部分省略.)


2、执行:indexer -c sphinx.conf --all

这里需要注意的就是一些告警

WARNING: attribute 'id' not found - IGNORING

WARNING: Attribute count is 0: switching to none docinfo

一开始比较疑惑的是,明明我的sql_query 语句和目标table中有id这个属性,而且设置的是整型,却警告说id 没有找到。

2.1 attribute 'id' not found 告警消除

2.1.1、首先要了解shpinx中属性的定义是什么:

http://www.coreseek.cn/forum/2_517_0.html 该发帖作者就被小小的批评了下,因为没有好好去看sphinx的手册)

sphinx中文文档有一段如下的资料:

属性是附加在每个文档上的额外的信息(值),可以在搜索的时候用于过滤和排序。

搜索结果通常不仅仅是进行文档的匹配和相关度的排序,经常还需要根据其他与文档相关联的值,对结果进行额外的处理。例如,用户可能需要对新闻检索结果依次按日期和相关度排序,检索特定价格范围内的产品,检索某些特定用户的blog日志,或者将检索结果按月分组。为了高效地完成上述工作,Sphinx允许给文档附加一些额外的属性,并把这些值存储在全文索引中,以便在对全文匹配结果进行过滤、排序或分组时使用。

属性与字段不同,不会被全文索引。他们仅仅是被存储在索引中,属性进行全文检索式不可能的。如果要对属性进行全文检索,系统将会返回一个错误。

例如,如果column被设置为属性,就不能使用扩展表达式
@column 1
去匹配column为1的文档;如果数字字段按照普通的方式被索引,那么就可以这样来匹配。

属性可用于过滤,或者限制返回的数据,以及排序或者 结果分组;
也有可能是完全基于属性排序的结果, 而没有任何搜索相关功能的参与. 此外, 属性直接从搜索服务程序返回信息, 而被索引的文本内容则没有返回.

论坛帖子表是一个很好的例子。假设只有帖子的标题和内容这两个字段需要全文检索,但是有时检索结果需要被限制在某个特定的作者的帖子或者属于某个子论坛的帖子中(也就是说,只检索在SQL表的author_id和forum_id这两个列上有特定值的那些行),或者需要按post_date列对匹配的结果排序,或者根据post_date列对帖子按月份分组,并对每组中的帖子计数。

为实现这些功能,可以将上述各列(除了标题和内容列)作为属性做索引,之后即可使用API调用来设置过滤、排序和分组。以下是一个例子:

示例: sphinx.conf 片段:

...
sql_query = SELECT id, title, content, \
author_id, forum_id, post_date FROM my_forum_posts
sql_attr_uint = author_id
sql_attr_uint = forum_id
sql_attr_timestamp = post_date
...

示例: 应用程序代码 (PHP):

// only search posts by author whose ID is 123
$cl->SetFilter ( "author_id", array ( 123 ) );

// only search posts in sub-forums 1, 3 and 7
$cl->SetFilter ( "forum_id", array ( 1,3,7 ) );

// sort found posts by posting date in descending order
$cl->SetSortMode ( SPH_SORT_ATTR_DESC, "post_date" );

可以通过名字来指示特定的属性,并且这个名字是大小写无关的(注意:直到目前为止,Sphinx还不支持中文作为属性的名称)。属性并不会被全文索引,他们只是按原封不动的存储在索引文件中。目前支持的属性类型如下:

无符号整数(1-32位宽);
UNIX 时间戳(timestamps);
浮点值(32位,IEEE 754单精度);
字符串序列 (尤其是计算出的整数值);
多值属性 MVA( multi-value attributes
) (32位无符号整型值的变长序列).

2.1.2、参考一下coreseek中文网站中对于sphinx.conf的详细注释:

http://www.coreseek.cn/products-install/mysql/

数据表字段取值对应到Coreseek的索引中,其关系如下:

数据库:SELECT id, group_id, UNIX_TIMESTAMP(date_added) AS date_added, score, title, content FROM documents

id      : 自增字段,表的主键,整数
group_id   : 整数字段
date_added  : 整数字段,使用UNIX_TIMESTAMP可将datetime类型转换为整数的timestamp
score     : 浮点数字段
title    : 字符串字段
content    : 文本字段业务分析:文档编号:id查询过滤:分组(group_id),时间(date_added),score全文检索:title、contentCoreseek

索引配置:
id       :
ID属性,必须提供,在SQL语句中字段名称不限. 对应SQL查询的第一个字段,系统自动使用,内部属性名为@id,不需要也不能在配置中设定使用SetFilter()过滤或者使用SetIDRange()过滤;

sql_attr_uint:
整数属性,以上group_id、date_added都可用此设置,使用SetFilter()过滤,或者使用SetFilterRange()过滤; 

sql_attr_float:
浮点数属性,以上score可用此设置,使用SetFilterFloatRange()进行范围过滤.

sql_attr_timestamp:
timestamp属性,整数,以上date_added可用此设置,可用SetFilter()过滤或者使用SetFilterRange()过滤;

sql_attr_str2ordinal:
字符串序列属性,以上title可用此设置,仅用于根据该字段排序但是设置后,该属性不可用于过滤,也不会保存实际字符串内容,更不能全文检                      索搜索结果,其对应的信息为整数,由系统计算出来的排序序列值

全文检索字段   :
全文检索字段,以上title、content等字符串或者文本的字段都可用此设置。任何出现在SQL语句中既不是ID属性,也没有使用“sql_attr_类型"设置的字段,都是全文字段,使用Query()搜索;


对于我测试过程中,出现的警告现在就很好解释了。

sphinx.conf 文件中 sql_query 中的第一个字段为系统自动使用,名称随便定义。因此我的查询语句中第一个字段 id不需要设置为 sql_attr_unit 。将其去掉之后就可以消除告警了。

这里还需要再次强调一下,为什么sphinx要设置sql_attr_xx 这个键名:属性是附加在每个文档上的额外的信息(值),可以在搜索的时候用于过滤和排序。

3、执行:search -c sphinx.conf 报道

D:\Program Files\sphinx\bin>search  -c sphinx.conf 报道
Sphinx 2.0.4-release (r3135)
Copyright (c) 2001-2012, Andrew Aksyonoff
Copyright (c) 2008-2012, Sphinx Technologies Inc (http://sphinxsearch.com)

using config file 'sphinx.conf'...
index 'test': query '���� ': returned 0 matches of 0 total in 0.000 sec

words:

为什么我的关键字显示的是乱码呢?查看 终端->属性->选项:当前代码页 65001 (UTF-8),这表明当前终端采用的字符集是UTF-8,因此无法正确的显示中文了。(虽然可以通过右键粘贴中文到终端,但是不能输入中文。)

需要提醒的一个细节就是:sphinx默认是采用utf-8字符集建立索引的。所以如果我们将终端当前代码页切换至936(GBK)(在终端输入:chcp 936)的话,可以正常的输入中文,但是因为编码方式的问题,输入的中文以GBK方式编码,无法被sphinx正常地识别,最终中文查询结果依然为0.所以在windos 终端下没有中文查询结果为0的同学一定不要误认为索引建立出现了问题。其实sphinx已经正常的建立了索引,只是我们传递过去的关键字编码方式”与从不同“,不被sphinx认同罢了。

虽然在终端检索中文失败,但是还可以通过别的办法来测试,即PHP。

下面是我的测试代码:

<?php
require ('D:\Program files\sphinx\api\sphinxapi.php');

$dbnames="mydb";
$dbusername="jun";
$dbuserpass="123456";
$port=5432;

$conn=pg_connect("dbname=$dbnames user=$dbusername password=$dbuserpass port=$port");
$sql="select id,title from documents";
$result=pg_exec($conn,$sql);
$a = pg_fetch_row($result,0);
/*此处能够正常的显示中文,而不需要转码,是因为postgreslq 服务器端是UTF-8,客户端编码方式是GBK*/
print $a[0].$a[1];

/*
*	此处之所以需要转化,是因为sphinx建立的索引采用的是UTF-8编码方式,
*	而我们的中文简体操作系统下PHP默认字符编码方式是GBK。
*/
$keywords = iconv('gbk', 'utf-8', '愚人节');

$cl = new SphinxClient ();
$cl->SetServer ( '127.0.0.1', 9312 );
$cl->SetConnectTimeout ( 1 );
#$cl->SetMatchMode($mode);
$cl->SetArrayResult ( true );
$res = $cl->Query ( $keywords,'patent' );

if ( $res===false )
{
print "Query failed: " . $cl->GetLastError() . ".\n";

}elseif( is_array($res["words"]) )
{
foreach ( $res["words"] as $word => $info )
{
/*此处同样需要进行反转码,以便能将从sphinx中接受来的UTF-8编码转化为浏览器能显示的中文*/
$word = iconv('utf-8', 'gbk', $word);
print "    '$word' found $info[hits] times in $info[docs] documents\n";

}
}

?>


秀一秀检索的结果吧:

1愚人节最佳蛊惑爆料 谷歌300亿美元收购百度

'愚' found 1 times in 1 documents '人' found 1 times in 1 documents '节' found 1 times in 1 documents

三、总结:

编码方式看似一个很不起眼的问题,但是却会成为整个系统致命的问题。所以在搭建系统的时候一定要注意各种软件的编码方式,在输入和输出方面要严格的界定和做好转码的工作,使其协调一致。

四、补充参考资料:

1、postgresql 字符集支持:postgreslq 中文手册

2、windows下切换终端字符集显示方式:

chcp 437/* 设置英文环境 */

chcp 932/*设置日文环境*/

chcp 936/*设置简体中文环境 */

chcp 949/*设置韩文环境*/

chcp 950/*设置繁体中文环境*/

chcp 65001/*设置UTF-8环境*/

如果cmd的默认代码页属性修改不了,那么修改注册表:

1 win R打开运行,输入regedit打开注册表编辑器。

2 找到 [HKEY_CURRENT_USER\Console\%SystemRoot%_system32_cmd.exe]

3 修改”CodePage”=dword:000003a8

十六进制”000003a8″或十进制”936″,表示“936 (ANSI/OEM – 简体中文 GBK)”。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: