复制默认配置文件,重新创建一个配置文件
sphinx.conf.dist是完整版默认配置,有很多内容,我这里选择复制的是sphinx-min.conf.dist迷你版,只要满足基本查询需要即可
# cp /usr/local/sphinx/etc/sphinx-min.conf.dist /usr/local/sphinx/etc/sphinx.conf
sphinx.conf文件的配置说明
#
# Minimal Sphinx configuration sample (clean, simple, functional)
#
//数据源设置
source src1
{
// 下面是sql数据库特有的类型,地址,端口,用户名,密码,编码,数据库名等。
type= mysql
sql_host= 127.0.0.1
sql_user= root
sql_pass= 123456
sql_db= yiiadmin
sql_port= 3306# optional, default is 3306
sql_query_pre = SET NAMES utf8
}
//创建一个数据源,他的信息继承自 src1,继承和程序类的继承一样,子对象可以对父对象的值进行覆盖。使用继承可以省略我们编写重复的设置信息。
source article : src1
{
//要建立索引的SQL
sql_query = SELECT id,id as aid,class_one_id,class_two_id,ctime...... FROM yi_article
//下面是属性
sql_attr_uint = aid //注:sphinx不能使用主键来做属性字段 sql_attr_uint = id (id为表的主键)
sql_attr_uint = class_one_id
sql_attr_uint = class_two_id
sql_attr_timestamp = ctime
}
//索引设置
index articleindex
{
// 索引类型,包括有plain,distributed和rt。分别是普通索引/分布式索引/增量索引。默认是plain。
type = plain
// 索引数据源
source= article
// 索引文件存放路径
path= /usr/local/sphinx22/var/data/article
// docinfo指的就是数据的所有属性(field)构成的一个集合
docinfo = extern
// 设置了mlock就不会出现这个问题,这部分数据会一直存放在内存中的
mlock = 0
// 所以英语的词形处理器会讲dogs当做dog来进行处理
morphology = none
// 最小索引词长度,小于这个长度的词不会被索引
min_word_len = 1
// 字符集编码类型,可以为sbcs,utf-8,当前加上会报错,原因未知
#charset_type = utf-8
//##### 字符表,注意:如使用这种方式,则sphinx会对中文进行单字切分,
//##### 即进行字索引,若要使用中文分词,必须使用其他分词插件如 coreseek,sfc
//目前发现的情况是,如果不加中文将不匹配
charset_table = U+FF10..U+FF19->0..9, 0..9, U+FF41..U+FF5A->a..z, U+FF21..U+FF3A->a..z,A..Z->a..z, a..z, U+0149, U+017F, U+0138, U+00DF, U+00FF, U+00C0..U+00D6->U+00E0..U+00F6,U+00E0..U+00F6, U+00D8..U+00DE->U+00F8..U+00FE, U+00F8..U+00FE, U+0100->U+0101, U+0101,U+0102->U+0103, U+0103, U+0104->U+0105, U+0105, U+0106->U+0107, U+0107, U+0108->U+0109,U+0109, U+010A->U+010B, U+010B, U+010C->U+010D, U+010D, U+010E->U+010F, U+010F,U+0110->U+0111, U+0111, U+0112->U+0113, U+0113, U+0114->U+0115, U+0115, U+0116->U+0117,U+0117, U+0118->U+0119, U+0119, U+011A->U+011B, U+011B, U+011C->U+011D, U+011D,U+011E->U+011F, U+011F, U+0130->U+0131, U+0131, U+0132->U+0133, U+0133, U+0134->U+0135,U+0135, U+0136->U+0137, U+0137, U+0139->U+013A, U+013A, U+013B->U+013C, U+013C,U+013D->U+013E, U+013E, U+013F->U+0140, U+0140, U+0141->U+0142, U+0142, U+0143->U+0144,U+0144, U+0145->U+0146, U+0146, U+0147->U+0148, U+0148, U+014A->U+014B, U+014B,U+014C->U+014D, U+014D, U+014E->U+014F, U+014F, U+0150->U+0151, U+0151, U+0152->U+0153,U+0153, U+0154->U+0155, U+0155, U+0156->U+0157, U+0157, U+0158->U+0159, U+0159,U+015A->U+015B, U+015B, U+015C->U+015D, U+015D, U+015E->U+015F, U+015F, U+0160->U+0161,U+0161, U+0162->U+0163, U+0163, U+0164->U+0165, U+0165, U+0166->U+0167, U+0167,U+0168->U+0169, U+0169, U+016A->U+016B, U+016B, U+016C->U+016D, U+016D, U+016E->U+016F,U+016F, U+0170->U+0171, U+0171, U+0172->U+0173, U+0173, U+0174->U+0175, U+0175,U+0176->U+0177, U+0177, U+0178->U+00FF, U+00FF, U+0179->U+017A, U+017A, U+017B->U+017C,U+017C, U+017D->U+017E, U+017E, U+0410..U+042F->U+0430..U+044F, U+0430..U+044F,U+05D0..U+05EA, U+0531..U+0556->U+0561..U+0586, U+0561..U+0587, U+0621..U+063A, U+01B9,U+01BF, U+0640..U+064A, U+0660..U+0669, U+066E, U+066F, U+0671..U+06D3, U+06F0..U+06FF,U+0904..U+0939, U+0958..U+095F, U+0960..U+0963, U+0966..U+096F, U+097B..U+097F,U+0985..U+09B9, U+09CE, U+09DC..U+09E3, U+09E6..U+09EF, U+0A05..U+0A39, U+0A59..U+0A5E,U+0A66..U+0A6F, U+0A85..U+0AB9, U+0AE0..U+0AE3, U+0AE6..U+0AEF, U+0B05..U+0B39,U+0B5C..U+0B61, U+0B66..U+0B6F, U+0B71, U+0B85..U+0BB9, U+0BE6..U+0BF2, U+0C05..U+0C39,U+0C66..U+0C6F, U+0C85..U+0CB9, U+0CDE..U+0CE3, U+0CE6..U+0CEF, U+0D05..U+0D39, U+0D60,U+0D61, U+0D66..U+0D6F, U+0D85..U+0DC6, U+1900..U+1938, U+1946..U+194F, U+A800..U+A805,U+A807..U+A822, U+0386->U+03B1, U+03AC->U+03B1, U+0388->U+03B5, U+03AD->U+03B5,U+0389->U+03B7, U+03AE->U+03B7, U+038A->U+03B9, U+0390->U+03B9, U+03AA->U+03B9,U+03AF->U+03B9, U+03CA->U+03B9, U+038C->U+03BF, U+03CC->U+03BF, U+038E->U+03C5,U+03AB->U+03C5, U+03B0->U+03C5, U+03CB->U+03C5, U+03CD->U+03C5, U+038F->U+03C9,U+03CE->U+03C9, U+03C2->U+03C3, U+0391..U+03A1->U+03B1..U+03C1,U+03A3..U+03A9->U+03C3..U+03C9, U+03B1..U+03C1, U+03C3..U+03C9, U+0E01..U+0E2E,U+0E30..U+0E3A, U+0E40..U+0E45, U+0E47, U+0E50..U+0E59, U+A000..U+A48F, U+4E00..U+9FBF,U+3400..U+4DBF, U+20000..U+2A6DF, U+F900..U+FAFF, U+2F800..U+2FA1F, U+2E80..U+2EFF,U+2F00..U+2FDF, U+3100..U+312F, U+31A0..U+31BF, U+3040..U+309F, U+30A0..U+30FF,U+31F0..U+31FF, U+AC00..U+D7AF, U+1100..U+11FF, U+3130..U+318F, U+A000..U+A48F,U+A490..U+A4CF
//N-Gram是指不按照词典,而是按照字长来分词,这个主要是针对非英文体系的一些语言来做的(中文、韩文、日文)
ngram_len = 1
//加上这个选项,则会对每个中文,英文字词进行分割,速度会慢,当前情况如不设置匹配不出来数据
ngram_chars = U+4E00..U+9FBB, U+3400..U+4DB5, U+20000..U+2A6D6, U+FA0E, U+FA0F, U+FA11, U+FA13, U+FA14, U+FA1F, U+FA21, U+FA23, U+FA24, U+FA27, U+FA28, U+FA29, U+3105..U+312C, U+31A0..U+31B7, U+3041, U+3043, U+3045, U+3047, U+3049, U+304B, U+304D, U+304F, U+3051, U+3053, U+3055, U+3057, U+3059, U+305B, U+305D, U+305F, U+3061, U+3063, U+3066, U+3068, U+306A..U+306F, U+3072, U+3075, U+3078, U+307B, U+307E..U+3083, U+3085, U+3087, U+3089..U+308E, U+3090..U+3093, U+30A1, U+30A3, U+30A5, U+30A7, U+30A9, U+30AD, U+30AF, U+30B3, U+30B5, U+30BB, U+30BD, U+30BF, U+30C1, U+30C3, U+30C4, U+30C6, U+30CA, U+30CB, U+30CD, U+30CE, U+30DE, U+30DF, U+30E1, U+30E2, U+30E3, U+30E5, U+30E7, U+30EE, U+30F0..U+30F3, U+30F5, U+30F6, U+31F0, U+31F1, U+31F2, U+31F3, U+31F4, U+31F5, U+31F6, U+31F7, U+31F8, U+31F9, U+31FA, U+31FB, U+31FC, U+31FD, U+31FE, U+31FF, U+AC00..U+D7A3, U+1100..U+1159, U+1161..U+11A2, U+11A8..U+11F9, U+A000..U+A48C, U+A492..U+A4C6
// html标记清理,是否从输出全文数据中去除HTML标记
html_strip = 0
}
// 建立索引
indexer
{
// 建立索引的时候,索引内存限制
mem_limit= 32M
}
// 检索服务
searchd
{
// 监听端口
listen= 9312
listen= 9306:mysql41
// 监听日志
log = /usr/local/sphinx22/var/log/searchd.log
// 查询日志
query_log= /usr/local/sphinx22/var/log/query.log
// 客户端读超时时间
read_timeout= 5
// 并行执行搜索的数目
max_children= 30
// 进程id文件
pid_file= /usr/local/sphinx22/var/log/searchd.pid
// 无缝轮转。防止 searchd 轮换在需要预取大量数据的索引时停止响应
// 当进行索引轮换的时候,可能需要消耗大量的时间在轮换索引上。
// 但是启动了无缝轮转,就以消耗内存为代价减少轮转的时间
seamless_rotate= 1
// 索引预开启,是否强制重新打开所有索引文件
preopen_indexes= 1
// 索引轮换成功之后,是否删除以.old为扩展名的索引拷贝
unlink_old= 1
// 多处理模式(MPM)。 可选项;可用值为none、fork、prefork,以及threads。 默认在Unix类系统为form,Windows系统为threads
workers= threads # for RT to work
// 二进制日志路径
binlog_path= /usr/local/sphinx22/var/data
}
生成索引文件
# /usr/local/sphinx22/bin/indexer --all //会生成配置文件下所有索引
Sphinx 2.2.11-id64-release (95ae9a6)
Copyright (c) 2001-2016, Andrew Aksyonoff
Copyright (c) 2008-2016, Sphinx Technologies Inc (http://sphinxsearch.com)
using config file '/usr/local/sphinx22/etc/sphinx.conf'...
indexing index 'articleindex'...
collected 67 docs, 0.5 MB
sorted 0.1 Mhits, 100.0% done
total 67 docs, 524532 bytes
total 0.033 sec, 15713960 bytes/sec, 2007.18 docs/sec
total 4 reads, 0.000 sec, 47.3 kb/call avg, 0.0 msec/call avg
total 12 writes, 0.000 sec, 29.2 kb/call avg, 0.0 msec/call avg
注:报错:FATAL: failed to lock /usr/local/sphinx22/var/data/article.spl: Resource temporarily unavailable, will not index. Try --rotate option.
解决:
/usr/local/sphinx22/bin/indexer --all --rotate //强制更新索引
启动服务
# /usr/local/sphinx22/bin/searchd //启动进程
Sphinx 2.2.11-id64-release (95ae9a6)
Copyright (c) 2001-2016, Andrew Aksyonoff
Copyright (c) 2008-2016, Sphinx Technologies Inc (http://sphinxsearch.com)
using config file '/usr/local/sphinx22/etc/sphinx.conf'...
listening on all interfaces, port=9312
listening on all interfaces, port=9306
precaching index 'articleindex'
precached 1 indexes in 0.020 sec
# /usr/local/sphinx/bin/searchd --stop //停止
简单测试
以前 sphinx 的 bin 目录里面有个自带 search 程序,新版本没有了,所以只好使用api或扩展方式调用了。
本文使用的扩展方式,关于sphinx的安装与php扩展安装,请查看本站相关文章
$sphinx = new SphinxClient;
//sphinx的主机名和端口
$sphinx->setServer('127.0.0.1',9312);
//设置返回结果集为php数组格式
$sphinx->SetArrayResult ( true );
//匹配结果的偏移量,参数的意义依次为:起始位置,返回结果条数,最大匹配条数
$sphinx->SetLimits(0, 20, 1000); //可用来实现分页
//最大搜索时间
$sphinx->SetMaxQueryTime(10);
//执行简单的搜索,这个搜索将会查询所有字段的信息(不包括设置的属性字段)
$result = $sphinx->query('PHP','articleindex');//多个关键词 (服务端配置)|(详解)
//索引源是配置文件中的 articleindex ,如果有多个索引源可使用,号隔开:'email,diary' 或者使用'*'号代表全部索引源
print_r($result);
//注:属性不参与索引查询,返回结果中只会有数据源中设置的属性
//部分返回结果
Array
(
[error] =>
[warning] =>
[status] => 0
[fields] => Array
(
[0] => class_one
[1] => class_two
[2] => title
[3] => image
[4] => is_home
[5] => is_release
[6] => sort
[7] => content
[8] => click_num
[9] => manager_id
)
[attrs] => Array
(
[aid] => 1
[class_one_id] => 1
[class_two_id] => 1
[ctime] => 2
)
[matches] => Array
(
[47] => Array
(
[weight] => 3144
[attrs] => Array
(
[aid] => 47
[class_one_id] => 139
[class_two_id] => 158
[ctime] => 1479722607
)
)
[39] => Array
(
[weight] => 3084
[attrs] => Array
(
[aid] => 39
[class_one_id] => 139
[class_two_id] => 158
[ctime] => 1478050647
)
)
)
[total] => 67
[total_found] => 67
[time] => 0.044
[words] => Array
(
[php] => Array
(
[docs] => 67
[hits] => 768
)
)
)
//$result是一个数组,其中
//total是匹配到的数据总数量
//matches是匹配的数据,包含id,attrs这些信息
//words是搜索关键字的分词
你可能奇怪为什么没有title的内容这些信息,其实sphinx并不会返回像mysql那样的数据数组,因为sphinx本来就没有记录完整的数据,只记录被分词后的数据。
具体还要看matches数组,matches中的ID就是指配置文件中sql_query SELECT语句中的第一个字段,我们配置文件中是这样的
sql_query = SELECT id,id as aid,class_one,class_one_id,class_two,class_two_id,title,image,is_home,is_release,sort,content,click_num,manager_id,ctime FROM yi_article
所以matches中的ID是指id
至于weight是指匹配的权重,一般权重越高被返回的优先度也最高,匹配权重相关内容请参考官方文档
attrs是配置文件中sql_attr_ 中的信息,稍后会提到这些属性的用法
说了这么多,即使搜索到结果也不是我们想要的email数据,但事实sphinx是不记录真实数据的,所以要获取到真实email数据还要根据matches中的ID去搜索mysql的email表,但总体来说这样一来一回的速度还是远远比mysql的LIKE快得多,前提是几十万数据量以上,否则用sphinx只会更慢。
接下来介绍sphinx一些类似mysql条件的用法
//id的范围
$sphinx->SetIdRange($min, $max);
//属性过滤,可过滤的属性必需在配置文件中设置sql_attr_ ,之前我们定义了这些
sql_attr_uint = aid
sql_attr_uint = class_one_id
sql_attr_uint = class_two_id
sql_attr_timestamp = ctime
//如果你想再次修改这些属性,配置完成后记得重新建立索引才能生效
//指定一些值
$sphinx->SetFilter('fromid', array(1,2)); //fromid的值只能是1或者2
//和以上条件相反,可增加第三个参数
$sphinx->SetFilter('fromid', array(1,2), false); //fromid的值不能是1或者2
//指定一个值的范围
$sphinx->SetFilterRange('toid', 5, 200); //toid的值在5-200之间
//和以上条件相反,可增加第三个参数
$sphinx->SetFilterRange('toid', 5, 200, false); //toid的值在5-200以外
//执行搜索
$result = $sphinx->query('关键字', '*');
排序模式
可使用如下模式对搜索结果排序:
SPH_SORT_RELEVANCE 模式, 按相关度降序排列(最好的匹配排在最前面)
SPH_SORT_ATTR_DESC 模式, 按属性降序排列 (属性值越大的越是排在前面)
SPH_SORT_ATTR_ASC 模式, 按属性升序排列(属性值越小的越是排在前面)
SPH_SORT_TIME_SEGMENTS 模式, 先按时间段(最近一小时/天/周/月)降序,再按相关度降序
SPH_SORT_EXTENDED 模式, 按一种类似SQL的方式将列组合起来,升序或降序排列。
SPH_SORT_EXPR 模式,按某个算术表达式排序
//使用属性排序
//以fromid倒序排序,注意当再次使用SetSortMode会覆盖上一个排序
$sphinx->SetSortMode ( 'SPH_SORT_ATTR_DESC', 'fromid');
//如果要使用多个字段排序可使用SPH_SORT_EXTENDED模式
//@id是sphinx内置关键字,这里指emailid,至于为什么是emailid,自己思考一下
$sphinx->SetSortMode ( 'SPH_SORT_ATTR_DESC', 'fromid ASC, toid DESC, @id DESC');
//执行搜索
$result = $sphinx->query('关键字', '*');
//更多请查看官方文档排序模式的说明
匹配模式
有如下可选的匹配模式:
SPH_MATCH_ALL, 匹配所有查询词(默认模式);
SPH_MATCH_ANY, 匹配查询词中的任意一个;
SPH_MATCH_PHRASE, 将整个查询看作一个词组,要求按顺序完整匹配;
SPH_MATCH_BOOLEAN, 将查询看作一个布尔表达式
SPH_MATCH_EXTENDED, 将查询看作一个CoreSeek/Sphinx内部查询语言的表达式 . 从版本Coreseek 3/Sphinx 0.9.9开始, 这个选项被选项SPH_MATCH_EXTENDED2代替,它提供了更多功能和更佳的性能。保留这个选项是为了与遗留的旧代码兼容——这样即使Sphinx及其组件包括API升级的时候,旧的应用程序代码还能够继续工作。
SPH_MATCH_EXTENDED2, 使用第二版的“扩展匹配模式”对查询进行匹配.
SPH_MATCH_FULLSCAN, 强制使用下文所述的“完整扫描”模式来对查询进行匹配。注意,在此模式下,所有的查询词都被忽略,尽管过滤器、过滤器范围以及分组仍然起作用,但任何文本匹配都不会发生.
我们要关注的主要是SPH_MATCH_EXTENDED2扩展匹配模式,扩展匹配模式允许使用一些像mysql的条件语句
//设置扩展匹配模式
$sphinx->SetMatchMode ( 'SPH_MATCH_EXTENDED2' );
//查询中使用条件语句,字段用@开头,搜索内容包含测试,toid等于1的邮件:
$result = $sphinx->query('@content (测试) & @toid =1', '*');
//用括号和&(与)、|、(或者)、-(非,即!=)设置更复杂的条件
$result = $sphinx->query('(@content (测试) & @subject =呃) | (@fromid -(100))', '*');
//更多语法请查看官方文档匹配模式的说明
扩展匹配模式中值得一提的是搜索的字段,如果该字段被设置属性,那么扩展匹配搜索的字段默认是不包含这些属性的,只能用SetFilter()或者SetFilterRange()之类
之前我们设置了fromid、toid、sendtime为属性,但又想在扩展匹配模式中又想用作条件该怎么办?
只要在sql_query语句中再选择多一次该字段就可以了
1 |
|
//设置完成记得重新建立索引
更多条件技巧
只是一些技巧,但不建议使用的部署环境中,至于为什么,请看文章结尾
<、<=、>、>=
默认sphinx没有这些比较符。
假如我想邮件的发送时间大于某一日期怎么办?用SetFilterRange()方法模拟一下
//大于等于某一时间截$time
$sphinx->SetFilterRange('sendtime', $time, 10000000000) //时间截最大是10个9,再加1是不可超越了。。
//大于某一时间截$time
$sphinx->SetFilterRange('sendtime', $time+1, 10000000000)
//小于等于某一时间截$time
$sphinx->SetFilterRange('sendtime', -1, $time) //时间截最小是0,所以应该减1
//大于某一时间截$time
$sphinx->SetFilterRange('sendtime', -1, $time - 1)
IS NOT NULL
怎样搜索为空的字段,比如我要搜索附件为空的邮件,有人可能会想 @attachment ('')不就可以了吗?其实这是搜索两个单引号。。。sphinx搜索的字符串不用加引号的
目前sphinx是没有提供这样的功能,其实可以在mysql语句上作手脚:
sql_query = SELECT emailid,fromid,toidsubject,content,sendtime,attachement != '' as attach is not null FROM email //这里返回了一个新字段attachisnotnull,当attachisnotnull为1的时候附件就不为空了
//设置完成记得重新建立索引
FIND_IN_SET()
搜索包含某一附件的邮件,mysql习惯用FIND_IN_SET这么简单一句就搞定了,在sphinx中必需在配置里设置属性sql_attr_multi 多值属性(MVA):
sql_attr_multi = attachment #attachment可以是逗号分隔的附件ID,或者是空格、分号等sphinx都能识别
//设置完成记得重新建立索引
然后PHP中可以使用SetFilter()
//搜索包含附件ID为1或2邮件,mysql语法是这样FIND_IN_SET(`attachment`, '1,2')
$sphinx->SetFilter('attachment', array(1,2))
//可以使用SetFilterRange,搜索包含附件ID在50-100范围的邮件
$sphinx->SetFilterRange('attachment', 50, 100)
总结
如果你想一个免费、好用、极速的全文搜索引擎,sphinx无疑是最好的选择,但是不要忘记sphinx的目的:全文检索。不要去想那些乱七八糟条件。你想要把sphinx搜索变得像mysql那样灵活,可完全单独用在一些复杂的多条件搜索,像某些邮件的高级搜索,那么我建议你还是多花点时间在PHP或者mysql代码的优化上,因为那样可能会让你的搜索变得更慢。
最好的方法是以最简单的方法搜索到内容,将ID交还mysql数据库搜索。