借助solr 的QueryElevationComponent实现竞价排名,相关的wiki如下:
http://wiki.apache.org/solr/QueryElevationComponent
相关配置:
在$solr_home/data/目录或者在$solr_home/conf/目录下加入文件elevate.xml, 内容如下
<?xml version="1.0" encoding="UTF-8" ?>
<elevate>
<query text="hello">
<doc id="4" />
</query>
</elevate>
然后配置下solrconfig.xml
<searchComponent name="elevator" class="solr.QueryElevationComponent" >
<!-- pick a fieldType to analyze queries -->
<str name="queryFieldType">string</str>
<str name="config-file">elevate.xml</str>
</searchComponent>
<requestHandler name="standard" class="solr.SearchHandler" default="true">
<lst name="defaults">
<str name="echoParams">explicit</str>
<str name="rows">10</str>
<str name="fl">*,score</str>
</lst>
<arr name="last-components">
<str>elevator</str>
</arr>
</requestHandler>
有两个重要的参数作为启动竞价排名组件:enableElevation=true&forceElevation=true
enableElevation=true:启动 elevator,默认为false
forceElevation=true:强制性使用竞价排名优先,当使用例如sort=id desc ,则默认为不破坏该排序,如果加了该 参数forceElevation=true则为强制使用竞价优先原则,否则相反
example:请求URL如下
由于solr3.5中的QueryElevationComponent 还是要求唯一值要使用StrField类型,如果不使用则会抛出如下 异常:
QueryElevationComponent requires the schema to have a uniqueKeyField implemented using StrField
当然很多时候我们的唯一值是数值类型,刚好看到正在开发的solr版本已支持其它类型,但现在也没有发布,所以也只能由自己来hack了。。
https://issues.apache.org/jira/browse/SOLR-1520
还有一个不满的就是QueryElevationComponent 还不支持分布式,看来也得自己来实现 了
solr实现竞价排名的思路大概如下:
1)该组件会在每次reader变化时加载竞价排名的配置,将关键对应的文档建成一个map
2)搜索的时候增加一个booleanQuery,将原来的query加入,并加入一个命中竞价排名关键词所对应的ids,变成一个query加入这个booleanQuery,这样就可以搜索出竞价对应的文档
3)修改排序,新增一个sort作为第一排序字段,而这个字段只有命中竞价的文档才有分值,否则都为0,这样就可优先竞价的文档
else {
BooleanQuery newq = new BooleanQuery( true );
newq.add( query, BooleanClause.Occur.SHOULD );
newq.add( booster.include, BooleanClause.Occur.SHOULD );
if( booster.exclude != null ) {
for( BooleanClause bq : booster.exclude ) {
newq.add( bq );
}
}
rb.setQuery( newq );
}
// if the sort is 'score desc' use a custom sorting method to
// insert documents in their proper place
SortSpec sortSpec = rb.getSortSpec();
if( sortSpec.getSort() == null ) {
sortSpec.setSort( new Sort( new SortField[] {
new SortField(idField, booster.comparatorSource, false ),
new SortField(null, SortField.SCORE, false)
}));
}
else {
// Check if the sort is based on score
boolean modify = false;
SortField[] current = sortSpec.getSort().getSort();
ArrayList<SortField> sorts = new ArrayList<SortField>( current.length + 1 );
// Perhaps force it to always sort by score
if( force && current[0].getType() != SortField.SCORE ) {
sorts.add( new SortField(idField, booster.comparatorSource, false ) );
modify = true;
}
for( SortField sf : current ) {
if( sf.getType() == SortField.SCORE ) {
sorts.add( new SortField(idField, booster.comparatorSource, sf.getReverse() ) );
modify = true;
}
sorts.add( sf );
}
if( modify ) {
sortSpec.setSort( new Sort( sorts.toArray( new SortField[sorts.size()] ) ) );
}
}
}