设为首页 收藏本站
查看: 1782|回复: 0

[经验分享] lucene、solr中的日期衰减方法-------function query

[复制链接]

尚未签到

发表于 2015-7-18 13:52:36 | 显示全部楼层 |阅读模式
经常有一种情景是这样的:我们索引了N年的文章,而查询时候无论直接用相关度、或者用时间排序,都是比较鲁莽的;我们想要一种既要相关度比较高,又要时间上比较新的文章。

这时候的解决办法就是,自定义日期衰减的ValueSourceQuery,然后在正常normalQuery的基础上后遭CustomScoreQuery即可。



下面给出2种在solr中使用日期衰减的方法

比如我们的索引中的时间字段是time,正常查询是title:哈哈 keyword:哈哈,

1、使用已有的各种functionQuery的组合

solr中日期衰减的查询方式则是:{!boost b=recip(ms(NOW/HOUR,time),3.16e-11,1,1)}title:哈哈 keyword:哈哈

前面这个式子的含义可以去查询solr wiki:http://wiki.apache.org/solr/FunctionQuery#What_is_a_Function.3F



这个方式,时间的衰减比较平缓,比如昨天的权重是0.999,前天是0.998,一年前的今天是0.5.。。。。。

如果我们需要一个时间衰减比较剧烈的方式,则需要自定义了。

2、自定义ValueSource:实现FieldCacheSource

这里我们以lucene4.1为例(各个版本的代码有所偏差,需要根据情况实现),大致原理是:给每个时间设置一个时间衰减因子,然后把文档的相关度乘上时间因子就是最后得分。

2.1和2.3中的实现方式,在得到相关度以后,每次搜索,都会获取所有文档的时间字段,并计算时间权重值。这在效率上是比较慢的,数据在千万级别的时候还可接受,更多的数据则会比较慢。

所以第3部分提供了这个思路的另一个实现方式,它只会计算搜索结果中的文档的时间权重,大大降低了时间。

2.1 先实现是 一个ValueSource。

  import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.valuesource.FieldCacheSource;
public class DateFunction extends FieldCacheSource {
private static final long serialVersionUID = 6752223682280098130L;
private static long now;
public DateFunction(String field) {
super(field);
now = System.currentTimeMillis();
}
@Override
public FunctionValues getValues(Map context,
AtomicReaderContext readerContext) throws IOException {
long[] times = cache.getLongs(readerContext.reader(), field, false);//获取各个记录中的时间字段毫秒数
final float[] weights = new float[times.length];
for (int i = 0; i < times.length; i&#43;&#43;) {
weights = ScoreUtils.getNewsScoreFactor(now, times);//获取每个记录的时间衰减因子
}
return new FunctionValues() {//返回
@Override
public float floatVal(int doc) {
return weights[doc];
}
@Override
public int intVal(int doc) {
return (int) weights[doc];
}
@Override
public String toString(int doc) {
return description() &#43; '=' &#43; intVal(doc);
}
};
}
}


其中用到的scoreutils定义如下:

  public class ScoreUtils {
private static float[] daysDampingFactor = new float[32];
private static float demoteboost = 0.5f;
static {
daysDampingFactor[0] = 1;
for (int i = 1; i < 7; i&#43;&#43;) {
daysDampingFactor = daysDampingFactor[i - 1] * demoteboost;
}
for (int i = 7; i < 31; i&#43;&#43;) {
daysDampingFactor = daysDampingFactor[i / 7 * 7 - 1]
* demoteboost;
}
for (int i = 31; i < daysDampingFactor.length; i&#43;&#43;) {
daysDampingFactor = daysDampingFactor[i / 31 * 31 - 1]
* demoteboost;
}
}
private static float dayDamping(int delta) {
return delta < daysDampingFactor.length ? daysDampingFactor[delta]
: daysDampingFactor[daysDampingFactor.length - 1];
}
public static float getNewsScoreFactor(long now, long time) {
float factor = 1;
int day = (int) (time / MiscConstants.DAY_MILLIS);
int nowDay = (int) (now / MiscConstants.DAY_MILLIS);
if (day < nowDay) {
factor = dayDamping(nowDay - day);
} else if (day > nowDay) {
factor = Float.MIN_VALUE;
} else if (now - time = time) {
factor = 2;
}
return factor;
}
public static float getNewsScoreFactor(long time) {
long now = System.currentTimeMillis();
return getNewsScoreFactor(now, time);
}
}
class MiscConstants {
/** 24x60x60x1000 */
public static final long DAY_MILLIS = 86400000;
/** 24x60x60x1000 */
public static final long DAY_SECONDS = 86400;
/** 60x1000 */
public static final int MINUTE_MILLIS = 60000;
/** 60x1000 */
public static final int HALF_HOUR_MILLIS = 1800000;
/** 60x1000 */
public static final int MINUTE_SECONDS = 60;
}


2.2 如果是在lucene中使用,则在正常的normalQuery基础上,包装一下即可,如下:

ValueSourceQuery dateBooster = new ValueSourceQuery(new DateFieldSource(&quot;ptime&quot;));

CustomScoreQuery dateScoreQuery = new CustomScoreQuery(normalQuery, dateBooster);

2.3 如果是在solr中使用个,还需要实现valuesourcepaser

  import org.apache.lucene.queries.function.ValueSource;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.FunctionQParser;
import org.apache.solr.search.SyntaxError;
import org.apache.solr.search.ValueSourceParser;
public class DateSourceParser extends ValueSourceParser {
@Override
public void init(NamedList namedList) {
}
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
return new DateFunction(&quot;ptime&quot;);// 被自定义排序的字段
}
}

并且要在solrconfig.xml的config标签中定义这个parser


这样在搜索的时候就可使用了{!boost b=dateDeboost()}title:哈哈 keyword:哈哈

ps:这里还支持参数;不用参数的时候dateDeboost(),这样调用就可以了。使用参数的时候dateDeboost(param),fqp.parseArg()可以获取参数。这样就可更自由的控制一下逻辑。



3、自定义ValueSource:重用ValueSource

阅读solr的代码后,发现solr中的function query的实现更优雅。

这里记录了solr自定义的各种函数的定义org.apache.solr.search.ValueSourceParser。

其实思路就是不再逐个记录的遍历,主要区别是getValues方法中的实现。具体实现如下:

3.1 实现一个valuesource

  import java.io.IOException;
import java.util.Map;
import org.apache.lucene.index.AtomicReaderContext;
import org.apache.lucene.queries.function.FunctionValues;
import org.apache.lucene.queries.function.ValueSource;
import org.apache.lucene.queries.function.docvalues.FloatDocValues;
import org.apache.lucene.search.IndexSearcher;
public class DateFunction extends ValueSource {
protected final ValueSource source;
public DateFunction(ValueSource source) {
this.source = source;
}
@Override
public FunctionValues getValues(Map context, AtomicReaderContext readerContext) throws IOException {
final FunctionValues vals = source.getValues(context, readerContext);
return new FloatDocValues(this) {
@Override
public float floatVal(int doc) {
long ptime = vals.longVal(doc);
return ScoreUtils.getNewsScoreFactor(ptime);
}
};
}
@Override
public void createWeight(Map context, IndexSearcher searcher) throws IOException {
source.createWeight(context, searcher);
}
@Override
public String description() {
return &quot;This is org.sling.DateFunction.&quot;;
}
@Override
public int hashCode() {
return source.hashCode();
}
@Override
public boolean equals(Object o) {
if (!(o instanceof DateFunction))
return false;
DateFunction other = (DateFunction) o;
return source.equals(other.source);
}
}


其中scoreutils的定义还是和上面一样。

3.2 在solr中使用

import org.apache.lucene.queries.function.ValueSource;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.search.FunctionQParser;
import org.apache.solr.search.SyntaxError;
import org.apache.solr.search.ValueSourceParser;
public class DateSourceParser extends ValueSourceParser {
    @Override
    public void init(NamedList namedList) {
    }
    @Override
    public ValueSource parse(FunctionQParser fp) throws SyntaxError {
        //ValueSource不能获取两次。所以fp.parseValueSourceList()和fp.parseValueSource()只能用一个
        ValueSource source = fp.parseValueSource();//获取这个ValueSource,并在一个sercher中重用它
        return new DateFunction(source);
    }
}

3.3在lucene中使用

读一下fp.parseValueSource()这部分代码,可以发现,其实这也是用了lucene中的一些类。下面直接给出实现吧

  ValueSource valueSource = new LongFieldSource(timeField);
FunctionQuery scoreField = new FunctionQuery(new DateFunction(valueSource));
CustomScoreQuery dateScoreQuery = new CustomScoreQuery(query, scoreField);
//TopDocs top = indexSearcher.search(query, 5);//普通查询
TopDocs top = indexSearcher.search(dateScoreQuery, 5);//日期衰减查询
ScoreDoc[] scoreDocs = top.scoreDocs;


可以发现,在lucene中普通查询和日期衰减查询的区别就是:构造的查询条件不一样而已。。。

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-88037-1-1.html 上篇帖子: Solr1.4.0源码分析二 Solr分布式搜索中URL的正确用法和原理 下篇帖子: 使用memcached缓存 替代solr中的LRUCache缓存
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表