文档章节

Lucene5学习之CustomScoreQuery

小致dad
 小致dad
发布于 2017/07/21 21:34
字数 1718
阅读 15
收藏 0
点赞 0
评论 0

CustomScoreQuery,顾名思义,就大不略的猜得到它的干嘛用的。它是用来进行干预查询权重的,从而影响最终评分的,即评分公式中的queryNorm部分。

一个索引文档的评分高低意味着它的价值大小,有价值的索引文档会优先返回并靠前显示,而影响评分的因素有Term在document中的出现频率,以及term在每个document中的出现频率,Term的权重等等,但这些因素都是固定的,并不会因为随着时间的改变而有所变化。比如你希望越是新出版的书籍权重应该越高,即出版日期距离当前时间越近权重越大。再比如你想实现我关注的用户发表的文章优先靠前显示,非关注用户发表的文章靠后显示等等,而CustomScoreQuery提供了这样一个接口来实现类似上述场景中的需求。你要做的就是

继承RecencyBoostCustomScoreQuery提供自己的CustomScoreProvider实现并重写其customScore方法,编写自己的实现逻辑。

下面是使用示例:

import java.io.IOException;  
  
import org.apache.lucene.index.LeafReaderContext;  
import org.apache.lucene.index.NumericDocValues;  
import org.apache.lucene.index.SortedDocValues;  
import org.apache.lucene.queries.CustomScoreProvider;  
  
public class RecencyBoostCustomScoreProvider extends CustomScoreProvider {  
    //权重倍数  
    private double multiplier;  
    // 从1970-01-01至今的总天数  
    private int day;  
    // 最大过期天数  
    private int maxDaysAgo;  
    // 日期域的名称  
    private String dayField;  
    // 域缓存值  
    private NumericDocValues publishDay;  
      
    private SortedDocValues titleValues;  
  
    public RecencyBoostCustomScoreProvider(LeafReaderContext context,double multiplier,int day,int maxDaysAgo,String dayField) {  
        super(context);  
        this.multiplier = multiplier;  
        this.day = day;  
        this.maxDaysAgo = maxDaysAgo;  
        this.dayField = dayField;  
        try {  
            publishDay = context.reader().getNumericDocValues(dayField);  
            titleValues = context.reader().getSortedDocValues("title2");  
        } catch (IOException e) {  
            e.printStackTrace();  
        }  
    }  
  
    /** 
     * subQueryScore:指的是普通Query查询的评分 
     * valSrcScore:指的是FunctionQuery查询的评分 
     */  
    @Override  
    public float customScore(int docId, float subQueryScore, float valSrcScore)  
            throws IOException {  
        String title = titleValues.get(docId).utf8ToString();  
        int daysAgo = (int) (day - publishDay.get(docId));  
        //System.out.println(title + ":" + daysAgo + ":" + maxDaysAgo);  
        //如果在6年之内  
        if (daysAgo < maxDaysAgo) {  
            float boost = (float) (multiplier * (maxDaysAgo - daysAgo) / maxDaysAgo);  
            return (float) (subQueryScore * (1.0 + boost));  
        }  
        return subQueryScore;  
    }  
} 
import java.io.IOException;  
  
import org.apache.lucene.index.LeafReaderContext;  
import org.apache.lucene.queries.CustomScoreProvider;  
import org.apache.lucene.queries.CustomScoreQuery;  
import org.apache.lucene.search.Query;  
  
public class RecencyBoostCustomScoreQuery extends CustomScoreQuery {  
    // 倍数  
    private double multiplier;  
    // 从1970-01-01至今的总天数  
    private int day;  
    // 最大过期天数  
    private int maxDaysAgo;  
    // 日期域的名称  
    private String dayField;  
    public RecencyBoostCustomScoreQuery(Query subQuery,double multiplier,int day,int maxDaysAgo,String dayField) {  
        super(subQuery);  
        this.multiplier = multiplier;  
        this.day = day;  
        this.maxDaysAgo = maxDaysAgo;  
        this.dayField = dayField;  
    }  
  
    @Override  
    protected CustomScoreProvider getCustomScoreProvider(  
            LeafReaderContext context) throws IOException {  
        return new RecencyBoostCustomScoreProvider(context,multiplier,day,maxDaysAgo,dayField);  
    }  
}  
import java.io.IOException;  
import java.nio.file.Paths;  
import java.util.Date;  
  
import org.apache.lucene.analysis.standard.StandardAnalyzer;  
import org.apache.lucene.document.Document;  
import org.apache.lucene.index.DirectoryReader;  
import org.apache.lucene.index.IndexReader;  
import org.apache.lucene.queryparser.classic.ParseException;  
import org.apache.lucene.queryparser.classic.QueryParser;  
import org.apache.lucene.search.IndexSearcher;  
import org.apache.lucene.search.Query;  
import org.apache.lucene.search.Sort;  
import org.apache.lucene.search.SortField;  
import org.apache.lucene.search.TopDocs;  
import org.apache.lucene.store.Directory;  
import org.apache.lucene.store.FSDirectory;  
  
import com.yida.framework.lucene5.util.Constans;  
/** 
 * CustomScoreQuery测试 
 * @author Lanxiaowei 
 * 
 */  
public class CustomScoreQueryTest {  
      
    public static void main(String[] args) throws IOException, ParseException {  
        String indexDir = "C:/lucenedir";  
        Directory directory = FSDirectory.open(Paths.get(indexDir));  
        IndexReader reader = DirectoryReader.open(directory);  
        IndexSearcher searcher = new IndexSearcher(reader);  
          
        int day = (int) (new Date().getTime() / Constans.PRE_DAY_MILLISECOND);  
        QueryParser parser = new QueryParser("contents",new StandardAnalyzer());  
        Query query = parser.parse("java in action");         
        Query customScoreQuery = new RecencyBoostCustomScoreQuery(query,2.0,day, 6*365,"pubmonthAsDay");  
        Sort sort = new Sort(new SortField[] {SortField.FIELD_SCORE,  
            new SortField("title2", SortField.Type.STRING)});  
        TopDocs hits = searcher.search(customScoreQuery, null, Integer.MAX_VALUE, sort,true,false);  
  
        for (int i = 0; i < hits.scoreDocs.length; i++) {  
            //两种方式取Document都行,其实searcher.doc内部本质还是调用reader.document  
          //Document doc = reader.document(hits.scoreDocs[i].doc);  
            Document doc = searcher.doc(hits.scoreDocs[i].doc);  
          System.out.println((1+i) + ": " +  
                             doc.get("title") +  
                             ": pubmonth=" +  
                             doc.get("pubmonth") +  
                             " score=" + hits.scoreDocs[i].score);  
        }  
        reader.close();  
        directory.close();  
    }  
}
import java.io.File;  
import java.io.FileInputStream;  
import java.io.IOException;  
import java.nio.file.Paths;  
import java.text.ParseException;  
import java.util.ArrayList;  
import java.util.Date;  
import java.util.List;  
import java.util.Properties;  
  
import org.apache.lucene.analysis.Analyzer;  
import org.apache.lucene.analysis.standard.StandardAnalyzer;  
import org.apache.lucene.document.BinaryDocValuesField;  
import org.apache.lucene.document.DateTools;  
import org.apache.lucene.document.Document;  
import org.apache.lucene.document.Field;  
import org.apache.lucene.document.IntField;  
import org.apache.lucene.document.NumericDocValuesField;  
import org.apache.lucene.document.SortedDocValuesField;  
import org.apache.lucene.document.SortedNumericDocValuesField;  
import org.apache.lucene.document.StringField;  
import org.apache.lucene.document.TextField;  
import org.apache.lucene.index.IndexWriter;  
import org.apache.lucene.index.IndexWriterConfig;  
import org.apache.lucene.index.IndexWriterConfig.OpenMode;  
import org.apache.lucene.store.Directory;  
import org.apache.lucene.store.FSDirectory;  
import org.apache.lucene.util.BytesRef;  
/** 
 * 创建测试索引 
 * @author Lanxiaowei 
 * 
 */  
public class CreateTestIndex {  
    public static void main(String[] args) throws IOException {  
        String dataDir = "C:/data";  
        String indexDir = "C:/lucenedir";  
  
        Directory dir = FSDirectory.open(Paths.get(indexDir));  
        Analyzer analyzer = new StandardAnalyzer();  
        IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);  
        indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);  
        IndexWriter writer = new IndexWriter(dir, indexWriterConfig);  
  
        List<File> results = new ArrayList<File>();  
        findFiles(results, new File(dataDir));  
        System.out.println(results.size() + " books to index");  
  
        for (File file : results) {  
            Document doc = getDocument(dataDir, file);  
            writer.addDocument(doc);  
        }  
        writer.close();  
        dir.close();  
  
    }  
  
    /** 
     * 查找指定目录下的所有properties文件 
     *  
     * @param result 
     * @param dir 
     */  
    private static void findFiles(List<File> result, File dir) {  
        for (File file : dir.listFiles()) {  
            if (file.getName().endsWith(".properties")) {  
                result.add(file);  
            } else if (file.isDirectory()) {  
                findFiles(result, file);  
            }  
        }  
    }  
  
    /** 
     * 读取properties文件生成Document 
     *  
     * @param rootDir 
     * @param file 
     * @return 
     * @throws IOException 
     */  
    public static Document getDocument(String rootDir, File file)  
            throws IOException {  
        Properties props = new Properties();  
        props.load(new FileInputStream(file));  
  
        Document doc = new Document();  
  
        String category = file.getParent().substring(rootDir.length());  
        category = category.replace(File.separatorChar, '/');  
  
        String isbn = props.getProperty("isbn");  
        String title = props.getProperty("title");  
        String author = props.getProperty("author");  
        String url = props.getProperty("url");  
        String subject = props.getProperty("subject");  
  
        String pubmonth = props.getProperty("pubmonth");  
  
        System.out.println("title:" + title + "\n" + "author:" + author + "\n" + "subject:" + subject + "\n"  
                + "pubmonth:" + pubmonth + "\n" + "category:" + category + "\n---------");  
  
        doc.add(new StringField("isbn", isbn, Field.Store.YES));  
        doc.add(new StringField("category", category, Field.Store.YES));  
        doc.add(new SortedDocValuesField("category", new BytesRef(category)));  
        doc.add(new TextField("title", title, Field.Store.YES));  
        doc.add(new Field("title2", title.toLowerCase(), Field.Store.YES,  
                Field.Index.NOT_ANALYZED_NO_NORMS,  
                Field.TermVector.WITH_POSITIONS_OFFSETS));  
        //doc.add(new BinaryDocValuesField("title2", new BytesRef(title.getBytes())));  
          
        doc.add(new SortedDocValuesField("title2", new BytesRef(title.getBytes())));  
        String[] authors = author.split(",");  
        for (String a : authors) {  
            doc.add(new Field("author", a, Field.Store.YES,  
                    Field.Index.NOT_ANALYZED,  
                    Field.TermVector.WITH_POSITIONS_OFFSETS));  
        }  
  
        doc.add(new Field("url", url, Field.Store.YES,  
                Field.Index.NOT_ANALYZED_NO_NORMS));  
        doc.add(new Field("subject", subject, Field.Store.YES,  
                Field.Index.ANALYZED, Field.TermVector.WITH_POSITIONS_OFFSETS));  
  
        doc.add(new IntField("pubmonth", Integer.parseInt(pubmonth),  
                Field.Store.YES));  
        doc.add(new NumericDocValuesField("pubmonth", Integer.parseInt(pubmonth)));  
        Date d = null;  
        try {  
            d = DateTools.stringToDate(pubmonth);  
        } catch (ParseException pe) {  
            throw new RuntimeException(pe);  
        }  
        int day = (int) (d.getTime() / (1000 * 3600 * 24));  
        doc.add(new IntField("pubmonthAsDay",day, Field.Store.YES));  
        doc.add(new NumericDocValuesField("pubmonthAsDay", day));  
        for (String text : new String[] { title, subject, author, category }) {  
            doc.add(new Field("contents", text, Field.Store.NO,  
                    Field.Index.ANALYZED,  
                    Field.TermVector.WITH_POSITIONS_OFFSETS));  
        }  
        return doc;  
    }  
  
}

重点在创建索引域那里,由于我们需要在CustomScoreQuery里获取指定域的所有值,随后根据文档ID去获取特定域的值,这里Lucene使用了FieldCache即域缓存,如果不用域缓存,我们需要根据docId通过IndexReader对象去索引目录读取每个段文件从而获取某个域的值,一个文档意味着一次磁盘IO,如果你索引文档数据量大的话,那后果将会很严重,你懂的,为了减少磁盘IO次数,Lucene引入了域缓存概念,其实内部就是用一个Map<String,Object> 来存储的,map的key就是域的名称,看源码:

IndexReader.getNumericDocValues

@Override  
 public final NumericDocValues getNumericDocValues(String field) throws IOException {  
   ensureOpen();  
   Map<String,Object> dvFields = docValuesLocal.get();  
  
   Object previous = dvFields.get(field);  
   if (previous != null && previous instanceof NumericDocValues) {  
     return (NumericDocValues) previous;  
   } else {  
     FieldInfo fi = getDVField(field, DocValuesType.NUMERIC);  
     if (fi == null) {  
       return null;  
     }  
     NumericDocValues dv = getDocValuesReader().getNumeric(fi);  
     dvFields.put(field, dv);  
     return dv;  
   }  
 }  

还有一点需要注意的是域缓存只对DocValuesField有效,这也是为什么创建索引代码那里需要add SortedDocValuesField,因为我们还需要根据该域进行排序,所以使用了SortedDocValuesField,

字符串类型可以用BinaryDocValuesField,数字类型可以使用NumericDocValuesField.域缓存是Lucene内部的一个高级API,对于用户来说,它是透明的,你只需要知道,使用DocValuesField可以利用域缓存来提升查询性能,但缓存也意味着需要有更多的内存消耗,所以在使用之前请进行性能测试,至于到底使不使用域缓存根据测试结果做好权衡。当你需要在Query查询内部去获取每个索引的某个域的值的时候,你就应该考虑使用域缓存。对于给定的IndexReader和指定的域,在首次访问域缓存的时候,会加载所有索引的该域的values放入缓存中(其实就是内存),是根据indexReader和域名两者联合起来确定唯一性,换句话说,你应该在多次查询中维持同一个IndexReader对象,因为每一个IndexReader都会有一套域缓存,如果你每次都new一个新的IndexReader,你会在内存中N个域缓存,这无疑是在内存中埋了N颗定时乍弹,而且这些你也无法利用域缓存。

本文转载自:http://iamyida.iteye.com/blog/2200742

共有 人打赏支持
小致dad
粉丝 116
博文 518
码字总数 569093
作品 0
济南
技术主管
IKAnalyzer分词

一、IKAnalyzer介绍 IK Analyzer 是一个开源的,基于java语言开发的轻量级的中文分词工具包。从2006年12月推出1.0版开始,IKAnalyzer已经推出了4个大版本。最初,它是以开源项目Luence为应用...

白志华
2015/12/30
495
0
apache开源项目lucene5.0如何导入到Eclipse中而不报错?

apache的lucene5开源项目是写的版本是jdk1.7开发的,我用的jdk1.8,我的ant也是1.9的高于开发版本,但是还是报错

njss
2015/03/23
725
1
Lucene2.9.2和Lucene3.0.1同时发布

这两个版本都是bug修正版本 重要改进内容: An increased maximum number of unique terms in each index segment. Fixed experimental CustomScoreQuery to respect per-segment search. Th......

zhuzhangsuo
2010/02/26
906
0
lucene 自定义评分 影响排序

前记 这段时间需要修改一个别人写的一个搜索有关的项目,恰好底层使用的是lucene搜索框架。 为什么要去修改呢,当然是搜索结果不太令人满意啦,于是去研读了项目中关于搜索的代码。。。。。。...

leemo0530
2015/07/19
0
0
TngouDB 0.2 beta 发布,中文搜索引擎数据库

TngouDB 中文索引数据库 0.2 beta 版本 主要改进: 1、数据存储引擎Lucene4更新到Lucene5。 2、增加了并发增、删、改的功能。 3、添加了返回状态码 4、重构了回收链接已经关闭链接功能。 需要...

tngou
2015/07/01
1K
13
lucene5 排序

SortField priceSortField = new SortField("price", SortField.Type.DOUBLE, false); Sort sort = new Sort(priceSortField); //TopDocs topDocs = indexSearcher.search(query,pageSize,pr......

zheng_pat
2016/06/27
22
0
打造自己的搜索引擎

这周项目要做一个搜索引擎系统,于是,我看看了看上下左右,看来只有我来弄了~~ 代码其中参考了@红薯的Lucene 早年分享的代码,与一些朋友的精华博客。算是入门了,这个入门花了我40篇日志。...

linapex
2015/11/26
133
0
Lucene查询语法

译自http://www.lucenetutorial.com/lucene-query-syntax.html Lucene有其惯用的查询语法对其索引进行查询。下面用一些例子来说明其查询语法。 关键字匹配 检索条件为在title字段中包含“foo...

木木在路上
2015/02/05
0
0
许雪里/xxl-search

《搜索应用参考示例XXL-SEARCH》 一、简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic风格的搜索应用参考示例,是索引搜索世界中的主流技术选型,最佳实践的总结...

许雪里
2016/09/26
0
0
搜索应用参考示例XXL-SEARCH

《搜索应用参考示例XXL-SEARCH》 一、简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic风格的搜索应用参考示例,是索引搜索世界中的主流技术选型,最佳实践的总结...

许雪里
2016/09/26
718
2

没有更多内容

加载失败,请刷新页面

加载更多

下一页

对基于深度神经网络的Auto Encoder用于异常检测的一些思考

一、前言 现实中,大部分数据都是无标签的,人和动物多数情况下都是通过无监督学习获取概念,故而无监督学习拥有广阔的业务场景。举几个场景:网络流量是正常流量还是攻击流量、视频中的人的...

冷血狂魔
12分钟前
0
0
并发设计之A系统调用B系统

A-->B A在发送请求之前,用乐观锁,减少对B的重复调用,这样一定程度上是幂等性。 比如A系统支付功能,要调用B系统进行支付操作,但是前端对"支付"按钮不进行控制,即用户会不断多次点击支付...

汉斯-冯-拉特
33分钟前
0
0
HTTP协议通信原理

了解HTTP HTTP(HyperText Transfer Protocol)是一套计算机通过网络进行通信的规则。计算机专家设计出HTTP,使HTTP客户(如Web浏览器)能够从HTTP服务器(Web服务器)请求信息和服务。 HTTP使用...

寰宇01
55分钟前
0
0
【Java动态性】之反射机制

一、Java反射机制简介

谢余峰
56分钟前
1
0
Centos 6.X 部署环境搭建

1.Linux学习笔记CentOS 6.5(一)--CentOS 6.5安装过程

IT追寻者
今天
0
0
博客即同步至腾讯云+社区声明

我的博客即将搬运同步至腾讯云+社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=8vy9bsmadbko...

xiaoge2016
今天
1
0
大数据教程(3.1):Linux系统搭建网络YUM源服务器

博主在前面的2.5章节讲述了linux系统本地YUM服务器的搭建和httpd轻量级静态网站服务器的安装,本节博主将为大家分享内网环境中搭建自己的网络YUM服务器的全过程。如果大家对本地YUM服务器还不...

em_aaron
今天
1
0
蚂蚁技术专家:一篇文章带你学习分布式事务

小蚂蚁说: 分布式事务是企业集成中的一个技术难点,也是每一个分布式系统架构中都会涉及到的一个东西,特别是在这几年越来越火的微服务架构中,几乎可以说是无法避免,本文就围绕分布式事务...

Java大蜗牛
今天
1
0
新的Steam应用将拓展服务项目

导读 未来几周,Steam将推出两个免费的应用程序Steam Link和Steam Video。这两个应用程序都旨在拓展Steam平台的业务和便利性。 即将开放的Steam Link应用程序最先提供了Android测试版,它将允...

问题终结者
今天
0
0
golang 第三方包的使用总结

golang 第三方包的安装的方法: 1. go get 安装 $ go get github.com/gin-gonic/gin 注意:执行go get 命令需要先安装git命令,并配置git全局变量。 2. 源码包安装 由于国内网络问题,很多时...

科陆李明
今天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部