lucene、solr中的日期衰减方法-------function query --尚未测试在solr4.8
经常有一种情景是这样的:我们索引了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++) {
weights[i] = ScoreUtils.getNewsScoreFactor(now, times[i]);//获取每个记录的时间衰减因子
}
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() + '=' + 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++) {
daysDampingFactor[i] = daysDampingFactor[i - 1] * demoteboost;
}
for (int i = 7; i < 31; i++) {
daysDampingFactor[i] = daysDampingFactor[i / 7 * 7 - 1]
* demoteboost;
}
for (int i = 31; i < daysDampingFactor.length; i++) {
daysDampingFactor[i] = 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 <= MiscConstants.HALF_HOUR_MILLIS && now >= 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("ptime"));
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("ptime");// 被自定义排序的字段
}
}
并且要在solrconfig.xml的config标签中定义这个parser
<valueSourceParser name="dateDeboost" class="org.netease.solr.custom.DateSourceParser" />
这样在搜索的时候就可使用了{!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 "This is org.sling.DateFunction.";
}
@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中普通查询和日期衰减查询的区别就是:构造的查询条件不一样而已。。。
lucene、solr中的日期衰减方法-------function query --尚未测试在solr4.8的更多相关文章
- Solr中的日期/时间表示
摘要: Solr的日期字段(TrieDateField 和DateRangeField)可以对一个时间点以毫秒精度表示. 格式 Solr中的日期有很严格的格式限制: YYYY-MM-DDThh:mm: ...
- Oracle中的日期处理方法
日期处理方法 当前日期和时间 Select sysdate from dual; 本月最后 ...
- 指尖上的电商---(8)Solr中Facet的使用方法
在大型电子商务站点中,在商品列表页,我们都能够看到商品按分类,品牌,价格的分类显示,例如以下图,这些我们能够使用solr中的facet功能实现. facet的基本功能就是对搜索结果中的商品进行分类. ...
- AS2在FLASH中调用EXE文件方法详细说明 已测试可行
熟悉FLASH功能的朋友都知道fscommand在FLASH中是一个经常用来控制窗口全屏或退出的命令,同时它也是FLASH调用外部可执行程序的一种方法,使用fscommand命令格式如下: fscom ...
- JavaScript 中的日期和时间
前言 本篇的介绍涵盖以下部分: 1. 时间标准指的是什么?UCT和GMT 的概念.关联和区别? 2. 时间表示标准有哪些? 3. JS 中时间的处理 日期时间标准 日期的标准就不多说了 -- 公元纪年 ...
- 【转】Java8中list转map方法总结
https://blog.csdn.net/zlj1217/article/details/81611834 背景在最近的工作开发之中,慢慢习惯了很多Java8中的Stream的用法,很方便而且也可以 ...
- PHP 中 16 个魔术方法详解
PHP 中 16 个魔术方法详解 前言 PHP中把以两个下划线__开头的方法称为魔术方法(Magic methods),这些方法在PHP中充当了举足轻重的作用. 魔术方法包括: __constru ...
- 在Lucene或Solr中实现高亮的策略
一:功能背景 近期要做个高亮的搜索需求,曾经也搞过.所以没啥难度.仅仅只是原来用的是Lucene,如今要换成Solr而已,在Lucene4.x的时候,散仙在曾经的文章中也分析过怎样在搜索的时候实现高亮 ...
- 在java中进行日期时间比较的4种方法
1. Date.compareTo() java.util.Date提供了在Java中比较两个日期的经典方法compareTo(). 如果两个日期相等,则返回值为0. 如果Date在date参数之后, ...
随机推荐
- The tag handler class for "c:forEach" (org.apache.taglibs.standard.tag.rt.core.ForEachTag) was not found on the Java Build Path
.tag出现如上错误 <%@ page language="java" contentType="text/html; charset=GB18030" ...
- HihoCoder1182 欧拉路(Fleury算法)
描述 小Hi和小Ho破解了一道又一道难题,终于来到了最后一关.只要打开眼前的宝箱就可以通关这个游戏了. 宝箱被一种奇怪的机关锁住: 这个机关是一个圆环,一共有2^N个区域,每个区域都可以改变颜色,在黑 ...
- Cash Machine(多重背包二进制转换)
个人心得:多重背包,自己根据转换方程写总是TLE,后面去网上看了二进制转换,不太理解: 后面仔细想了下,用自己的思想理解下把,就是将对应number,cash总和用二进制拆分, 然后全部装入到一个数组 ...
- 2017.10.4北京清北综合强化班DAY4
财富(treasure) Time Limit:1000ms Memory Limit:128MB 题目描述 LYK有n个小伙伴.每个小伙伴有一个身高hi. 这个游戏是这样的,LYK生活的环境是以 ...
- 类和对象(9)—— new和delete
对象动态建立和释放 new 和delete 在软件开发过程中,常常需要动态地分配和撤销内存空间,例如对动态链表中结点的插入与删除.在C语言中是利用库函数malloc和free来分配和撤销内存空间的.C ...
- [转]200 OK (from cache) 与 304 Not Modified------没有这个规则(ETag是否移除)!!!from cache和304,请查看顶部的流程图!
//========没有这个规则(ETag是否移除) 20160422============// 200 OK (from cache) 与 304 Not Modified 为什么有的缓存是 20 ...
- git之reset
有时候,一直用公司电脑开发的时候,顺顺利利.回到家,用自己笔记本,想改改代码,git pull后,发现一堆的unmerged或者其他冲突等问题. 明明只是git pull 一下,怎么会这么多问题. 这 ...
- 蓝桥杯 算法训练 ALGO-120 学做菜
算法训练 学做菜 时间限制:1.0s 内存限制:256.0MB 问题描述 涛涛立志要做新好青年,他最近在学做菜.由于技术还很生疏,他只会用鸡蛋,西红柿,鸡丁,辣酱这四种原料来做菜,我们给这四种 ...
- STM32 -- 硬件知识
一.网站资源 1.http://www.stmcu.com.cn/ 二.硬件 1.BOOT0 和 BOOT1 1)一般BOOT0和BOOT1跳线都跳到0(地): 只是在ISP下载的情况下,BOO ...
- Spring 自动注册及自动装配
Spring支持三种注册Bean及装配Bean的方式: 显式地在Java代码中注册及装配 显示地在Xml文件中注册及装配 隐式地装配,即自动注册及装配 这三种方式可以混合使用.选择哪种更多地是看个人品 ...