转载自:http://blog.sina.com.cn/s/blog_4c925dca0102vga8.html
本篇文章,阿堂将和大家分享sorl的高级应用自动补全功能。实际上,我们在在一些电商平台上购物就可以经常看到自动补全功能。
如京东网上的截图所示
索引
设想你想在你的在线商店中,给用户一些提示,比如商品的名称。假设我们的索引构建如下:
<<
field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<<field name="title" type="textik" indexed="true" stored="true" multiValued="false" />
<<
field name="description" type="text" indexed="true" stored="true" multiValued="false" />
textik类型的定义为:

配置
开始前,首先考虑你要实现的功能:是要实现一个名字的提示,还是全名的提示。这都依赖于我们的选择,我们必须为需要引导的地方设置适当的域。
单词提示
在单词的情况下,我们使用的域也即一个token。在这种情况下,域名为title就足够了。但是,这属于一个词干,analysis的操作都在词干上,因此,我们最好换一个其他的类型。
全名提示
我们使用一个不同的域配置来定义全名提示--最好一个未被定义的域。但是我们不能使用基于类似string这种类型的域,基于这个原因,我们定义为一下的域:

textik类型的定义为:
为了不影响原有数据的格式,将原数据进行拷贝:
如何使用
为了使用这个数据,我们准备了一个简单的查询语句:
q=*:*
&facet=true
&facet.field=FIELD
&facet.mincount=1
&facet.prefix=USER_QUERY
需要替换的地方:
FIELD:我们打算提供建议的域,在本例中域名为title 或title_auto
USER_QUERY:用户输入的字符
这里可以设置rows=0,这样可以只返回facet的结果,而没有查询结果。当然这不是必须的。
查询的一个例子可以这样写:
fl=id,name
&rows=0
&q=*:*
&facet=true
&facet.field=name_auto
&facet.mincount=1
&facet.prefix=你们
查询结果会返回这样的结果:
<<
response>
<<
lst name="responseHeader">
<<
int name="status">0</</span>int>
<<
int name="QTime">0</</span>int>
<<
lst>
<<
result name="response" numFound="4" start="0"/>
<<
lst name="facet_counts">
<<
lst name="facet_queries"/>
<<
lst name="facet_fields">
<<
lst name="title_auto">
<<
int name="你们">2</</span>int>
<<
int name="你们好">1</</span>int>
<<
int name="你们好吗">1</</span>int>
<<
lst>
<<
lst>
<<
lst name="facet_dates"/></</span>lst>
<<
response>
阿堂通过代码测试效果截图如下


另外,阿堂附上了测试完整代码
public class SolrjCompleteContents {
public static void main(String[] args) {
CommonsHttpSolrServer service=null;
try {
service = new CommonsHttpSolrServer("http://localhost:9999/solr/");
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
List list = new ArrayList();
//查询响应结果
QueryResponse queryResponse = new QueryResponse();
//查询传输参数封装
SolrQuery query = new SolrQuery();
try {
query.setFacet(true);
query.setRows(0);
query.setQuery("title:你们");
query.setFacetPrefix("你们");
query.addFacetField("title_auto");
query.setFacetLimit(20);
System.out.println("query.toString() = "+query.toString());
queryResponse = service.query(query);
NamedListnl = queryResponse.getResponse();
System.out.println("nl = "+nl);
NamedListnl2 = (NamedList)nl.get("facet_counts");
NamedListnl3 = (NamedList)nl2.get("facet_fields");
NamedListnl4 = (NamedList)nl3.get("title_auto");
System.out.println("nl4.size() = "+nl4.size());
Iterator> it = nl4.iterator();
while (it.hasNext()) {
Entry entry = it.next();
System.out.println(entry.getKey() + "____" + entry.getValue());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
扩展功能
这里说一下他的一些常用的功能。
第一个是显示用户的一些额外的信息,比如当你选择某个提示词时,显示的结果的数量。这是一个很有意思的特性。
另一个是使用facet.sort参数进行排序。这依赖于你的需求,我们可以按文档的数量排序(默认方式,设参数为true即可),或者按字母序排序(设为false)。
我们也可以通过设置facet.mincount来显示比指定的数量更多的提示词。
另外一个很好的特性是提示词不仅可以通过用户的类型获取,还可以通过其他的属性获取,这类似于类别。举个例子,我们想给用户展示家庭用品相关的商品,我们假设现在用户对DVD类型的商品并不感兴趣,这样我们添加一个参数: fq=department:homeApplications(假设有这个department)。通过这样的一个查询,你就不需要在所有的索引中匹配了,而是在我们选择的department里选择。
结尾
跟其他方法一样,它有优点,也有缺点。优点就是易于使用、没有额外的组件依赖,并且能将结果约束在一个很小的范围内来更好的匹配用户的需求;另外一个很大的优点是它对每个提示词都附带了结果的统计。缺点就是需要添加额外的类型和字段;另外由于其facet的机制,对机器性能和load都非常消耗。
后记:
实际上,上面阿堂演示分享的只是自动补全的基本功能,要想真正到电商平台的分类功能展示效果,还需要进行数据分类的详细设计,当然,这不是本篇文章讨论的范围,属于电商平台数据库的设计,要要分成多种数据表,如分类表,商品明细表,商品附加属性表及扩展表 等数据表,这样就可以通过自动补全功能展示非常丰富的效果了。