复制默认配置文件,重新创建一个配置文件
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语句中再选择多一次该字段就可以了
sql_query = SELECT emailid,fromid,fromid,toid,toid,subject,content,sendtime,sendtime,attachement FROM email
//设置完成记得重新建立索引
更多条件技巧
只是一些技巧,但不建议使用的部署环境中,至于为什么,请看文章结尾
<、<=、>、>=
默认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数据库搜索。