项目中遇到两个千万数量级的表相关联的情况,join之后加上检索条件,检索的很慢,所以选择使用sphinx搜索引擎来提高检索效率。
我使用的是2.32版本的sphinx,如何部署和配置就不赘述了,网上都可以查到很详细的教程,主要来谈谈在java环境下的使用。
sphinx的文档中写了是对java支持的,但只是寥寥几句带过,文档中的示例也都是用的PHP。下载好sphinx的压缩包,解压出来可以找到其中对java的接口,有这样几个:
SphinxClient.java
SphinxException.java
SphinxMatch.java
SphinxResult.java
SphinxWordInfo.java
这几个类很久没有更新了,添加到项目中凑合着用吧。
具体使用:
1. 创建一个SphinxClient
String sphinxHost = "localhost";
int sphinxPort = 9312;
SphinxClient sphinxClient = new SphinxClient(sphinxHost, sphinxPort);
2. 配置SphinxClient
设置匹配模式
匹配模式有一下几种:
SPH_MATCH_ALL, 匹配所有查询词(默认模式);
SPH_MATCH_ANY, 匹配查询词中的任意一个;
SPH_MATCH_PHRASE, 将整个查询看作一个词组,要求按顺序完整匹配;
SPH_MATCH_BOOLEAN, 将查询看作一个布尔表达式 ;
SPH_MATCH_EXTENDED, 将查询看作一个Sphinx内部查询语言的表达式。这个选项被选项SPH_MATCH_EXTENDED2代替,它提供了更多功能和更佳的性能。保留这个选项是为了与遗留的旧代码兼容——这样即使Sphinx及其组件包括API升级的时候,旧的应用程序代码还能够继续工作。
SPH_MATCH_EXTENDED2, 使用第二版的“扩展匹配模式”对查询进行匹配;
SPH_MATCH_FULLSCAN, 强制使用下文所述的“完整扫描”模式来对查询进行匹配。注意,在此模式下,所有的查询词都被忽略,尽管过滤器、过滤器范围以及分组仍然起作用,但任何文本匹配都不会发生;
// 如果不设置的话,默认也是SPH_MATCH_EXTENDED2模式
sphinxClient.SetMatchMode(SphinxClient.SPH_MATCH_EXTENDED2);
设置分页查询的范围
sphinx的分页查询有四个参数:
offset、limit、max、cutoff
offset和limit定义获取数据的偏移量和数量,类似mysql中的limit
max对应max_matches参数,是控制最终返回的索引结果的最大数量,例如定义offset为0,limit为1000,max为100,这样sphinx会先检索出1000条数据,再按照排序和过滤条件倒序取出前100条。注意:这个参数可以在每次的请求中设置, 也可以在sphinx的配置文件中设置, 后者的优先级高一些, 即每次请求中的max_matches 不能高于sphinx 配置文件中的max_matches!
cutoff 也是控制最终返回的索引结果的最大数量,与max_matches 不同的是,max_matches是先排序和过滤再倒序取出max_matches 条数据,而cutoff是从第一个数据开始截取cutoff条数据然后过滤和排序后返回数据。注意,sphinx在处理时cutoff优先级大于max_matches,虽然cutoff性能更高,但是容易导致搜索结果不够优,建议不使用。
offset、limit是必传参数,max和cutoff是选传参数,不传入的话max默认为1000,cutoff为0。
sphinxClient.SetLimits SetLimits( int offset, int limit, int max, int cutoff );
设置排序模式
sphinx有这几种排序模式:
SPH_SORT_RELEVANCE 模式, 按相关度降序排列(最好的匹配排在最前面)
SPH_SORT_ATTR_DESC 模式, 按属性降序排列 (属性值越大的越是排在前面)
SPH_SORT_ATTR_ASC 模式, 按属性升序排列(属性值越小的越是排在前面)
SPH_SORT_TIME_SEGMENTS 模式, 先按时间段(最近一小时/天/周/月)降序,再按相关度降序
SPH_SORT_EXTENDED 模式, 按一种类似SQL的方式将列组合起来,升序或降序排列。
SPH_SORT_EXPR 模式,按某个算术表达式排序。
如果不设置的话默认为SPH_SORT_RELEVANCE
sphinxClient.SetSortMode(SphinxClient.SPH_SORT_ATTR_DESC, "gmt_modified");
设置过滤
使用sphinxClient.SetFilter(String attribute, int value, boolean exclude)方法设置过滤,
attribute为要过滤的字段,这个字段的数据类型需要是数字型,在sphinx配置文件中要设置为sql_attr_*,例如:sql_attr_bigint = label_status
value为过滤的值,这里可以使用整型和long型的数字和数组
exclude为true时正向过滤,为false时反向过滤,即为true时过滤掉值=value的数据保留值!=value的数据,为false过滤掉值!=value的数据保留值=value的数据,千万不要用反了
// 等价于sql语句 where label_status=0
sphinxClient.SetFilter("label_status", 0, false);
也可以使用SetFilterRange设置范围区间过滤,使用方法类似SetFilter
// 等价于sql语句 where label_status>0 and label_status<3
sphinxClient.SetFilterRange("label_status", 0, 3, false);
3.检索方法
配置完sphinxClient后,使用sphinxClient.Query(String query, String index)方法进行检索
不同的匹配模式对应的query语句写法不同,这里介绍SPH_MATCH_EXTENDED2(扩展匹配模式)的query语句写法
在扩展查询模式中可以使用如下特殊操作符:
或(OR)操作符:hello | world
非(NOT)操作符:hello -world hello !world
字段(field)搜索符:@title hello @body world
字段限位修饰符(版本Coreseek 3/Sphinx 0.9.9-rc1中引入):@body[50] hello
多字段搜索符:@(title,body) hello world
全字段搜索符:@* hello
词组搜索符:”hello world”
近似搜索符:”hello world”~10
阀值匹配符:”the world is a wonderful place”/3
严格有序搜索符(即“在前”搜索符):aaa << bbb << ccc
严格形式修饰符(版本Coreseek 3/Sphinx 0.9.9-rc1中引入):raining =cats and =dogs
字段开始和字段结束修饰符 (版本Coreseek 3.1/Sphinx 0.9.9-rc2中引入):^hello world$
一个检索条件对应多个值时,使用 | 拼接,例如
query="(@position=M|E|S)";
多个检索条件之间使用&拼接,例如
query="(@vocabulary=\"^左侧$\")&(@property=P)&(@position=M|E|S)";
index为检索要使用的索引名,对应sphinx配置中的索引名,多个索引直接以 | 隔开,例如
SphinxResult sphinxResult = sphinxClient.Query("(@vocabulary=\"^左侧$\")&(@property=P)&(@position=M|E|S)","index1|index2");
4.结果处理
遍历sphinxResult中的matches即可得到我们想要的数据
for (SphinxMatch m : sphinxResult.getMatches()) {
String s1=m.getAttrValues().get(0);
String s2=m.getAttrValues().get(1);
...
}