MoreLikeThis,相似检索。找出某篇文档的相似文档,常见于“类似新闻”、“相关文章”等,这里完全是基于内容的分析。

1)MoreLikeThis的使用

                FSDirectory directory = SimpleFSDirectory.open(new File("d:/nrtTest2")); IndexReader reader = IndexReader.open(directory);IndexSearcher searcher = new IndexSearcher(reader);//MoreLikeThis mlt = new MoreLikeThis(reader);mlt.setFieldNames(new String[] { "ab" }); //用于计算的字段//int docNum = 1;
//      TermFreqVector vector = reader.getTermFreqVector(docNum, "ab");
//      System.out.println(vector.toString());Query query = mlt.like(docNum);//试图找到与docnum=1相似的documentsSystem.out.println(reader.document(docNum));System.out.println(query.toString());//查看构造的query,后面的就是常规的lucene的检索过程。TopDocs topDocs = searcher.search(query, 10);ScoreDoc[] scoreDocs = topDocs.scoreDocs;for (ScoreDoc sdoc : scoreDocs) {Document doc = reader.document(sdoc.doc);System.out.println(doc.get("ti"));// System.out.println(doc.get("ti"));}

2)MoreLikeThis的源码解读

把MLT运行起来很简单,那么我们进一步看看他是怎么实现的。

关键就是 Query query = mlt.like(docNum); 我们就从他下手。

2.1)like

public Query like(int docNum) throws IOException {if (fieldNames == null) {// gather list of valid fields from luceneCollection<String> fields = ReaderUtil.getIndexedFields(ir);fieldNames = fields.toArray(new String[fields.size()]);}return createQuery(retrieveTerms(docNum));}

filedNames为参与“more like this”运算的字段,在moreLikeThis对象的setFiledNames方法中进行设置。

2.2)retrieveTerms

public PriorityQueue<Object[]> retrieveTerms(int docNum) throws IOException {Map<String,Int> termFreqMap = new HashMap<String,Int>();for (int i = 0; i < fieldNames.length; i++) {String fieldName = fieldNames[i];TermFreqVector vector = ir.getTermFreqVector(docNum, fieldName);//取出term向量// 如果当前字段没有存储termVector,那么需要重新计算。其实这里就是分词,并计算term词频的过程,注意他默认使用的是StandardAnalyzer分词器!!!if (vector == null) {Document d = ir.document(docNum);String text[] = d.getValues(fieldName);if (text != null) {for (int j = 0; j < text.length; j++) {addTermFrequencies(new StringReader(text[j]), termFreqMap,fieldName);}}} else {//如果之前保存了termVector那么就方便多了。addTermFrequencies(termFreqMap, vector);}}

2.3)addTermFrequencies

由于TermVector中的term和field没有关系,不管是标题还是正文,只要term内容一样就将其频率累加。addTermFrequencies就做这个事情!

把累加的结果存放到termFreqMap中。

private void addTermFrequencies(Map<String,Int> termFreqMap,TermFreqVector vector) {String[] terms = vector.getTerms();int freqs[] = vector.getTermFrequencies();for (int j = 0; j < terms.length; j++) {String term = terms[j];if (isNoiseWord(term)) {continue;}// increment frequencyInt cnt = termFreqMap.get(term);if (cnt == null) {cnt = new Int();termFreqMap.put(term, cnt);cnt.x = freqs[j];} else {cnt.x += freqs[j];}}}

截止,我们将指定的文档(被匹配文档)按照指定的运算字段,将其term和对应的frequency存放到了map中。在这个过程中,我们看到了一个听起来比较牛逼的操作——去噪

那么这里怎么判断一个Term是不是噪音呢?

private boolean isNoiseWord(String term) {int len = term.length();if (minWordLen > 0 && len < minWordLen) {return true;}if (maxWordLen > 0 && len > maxWordLen) {return true;}if (stopWords != null && stopWords.contains(term)) {return true;}return false;}

他判断的标准十分简单,第一:是否是规定的停用词;第二:term长度是否过长或过短,这个范围由minWordLen和maxWordLen控制。

2.4)createQueue

这里的queue应该是一个优先级队列,上一步我们获得了所有<term, frequency>,虽然做了去噪,但是term项目还是太多了,还需要找出相对重要的前N个Term。

private PriorityQueue<Object[]> createQueue(Map<String,Int> words)throws IOException {// 获取当前index的文档总数。int numDocs = ir.numDocs();FreqQ res = new FreqQ(words.size()); // 按照term的得分进行存放Iterator<String> it = words.keySet().iterator();while (it.hasNext()) { // 对所有term进行遍历String word = it.next();int tf = words.get(word).x; // 对应term的tfif (minTermFreq > 0 && tf < minTermFreq) {continue; // 和去噪类似,tf太小的term直接过掉。}// 对于同一个term,找到df最大的那个字段,存放到topField。String topField = fieldNames[0];int docFreq = 0;for (int i = 0; i < fieldNames.length; i++) {int freq = ir.docFreq(new Term(fieldNames[i], word));topField = (freq > docFreq) ? fieldNames[i] : topField;docFreq = (freq > docFreq) ? freq : docFreq;}//df太小的term也要直接过掉if (minDocFreq > 0 && docFreq < minDocFreq) {continue; // filter out words that don't occur in enough docs}//df太大的term也要直接过掉if (docFreq > maxDocFreq) {continue; // filter out words that occur in too many docs}//df==0的term也要直接过掉,怎么会有df的term???这里说是index文件的问题if (docFreq == 0) {continue; // index update problem?}//经典的idf、tf又来了float idf = similarity.idf(docFreq, numDocs);float score = tf * idf;//将结果存放到优先队列中。res.insertWithOverflow(new Object[] {word, // the wordtopField, // the top fieldFloat.valueOf(score), // overall scoreFloat.valueOf(idf), // idfInteger.valueOf(docFreq), // freq in all docsInteger.valueOf(tf)});}return res;}

在这里,我们对每个term进行了打分排序,主要还是通过tf、idf进行计算。

这里他的意思就是:

1.将指定参与运算字段的term的frequency进行累加;(这里对ti、ab字段的tf进行累加)

2.通过df的比较,选取df大的字段作为最终“运算”的字段,但tf为所有字段的累加值。这和我们看普通检索时的打分算法不太一样,普通检索中tf为当前字段的词频。

至于为什么这么做,还得验证!!!

2.5)createQuery

到此我们将term的打分排序拿到了,分值越大的term更能表述整篇document的主要内容!(有没有想到这就类似主题词!!!

private Query createQuery(PriorityQueue<Object[]> q) {BooleanQuery query = new BooleanQuery();Object cur;int qterms = 0;float bestScore = 0;while (((cur = q.pop()) != null)) {Object[] ar = (Object[]) cur;TermQuery tq = new TermQuery(new Term((String) ar[1], (String) ar[0]));//这里还可以对termquery进行boost的设置。默认为falseif (boost) {if (qterms == 0) {bestScore = ((Float) ar[2]).floatValue();}float myScore = ((Float) ar[2]).floatValue();tq.setBoost(boostFactor * myScore / bestScore);}//构建boolean query,should关联。try {query.add(tq, BooleanClause.Occur.SHOULD);} catch (BooleanQuery.TooManyClauses ignore) {break;}qterms++;if (maxQueryTerms > 0 && qterms >= maxQueryTerms) {//限定参与运算的term的数量break;}}return query;}

这样就根据一篇document和指定字段得到了一个query。这个query作为代表着document的灵魂,将寻找和他类似的documents。

3)实例

被匹配的document为:

Document<stored,indexed,tokenized<an:CN00103249.6>

stored,indexed<ad:20000320> stored,indexed,tokenized,termVector<ab:    本发明涉及一种高级毛料服装洗涤剂。使用该洗涤剂,洗衣服可不用到干洗店,自己在家水洗就行,且洗涤后毛料服装笔挺膨松,抗静电,不缩水,洗涤中不刺激皮肤。此剂主要由去污剂、抗静电剂、防缩剂、表面活性剂与其它助剂配制而成。去污率≥90%,缩水率≤1‰。>

stored,indexed,tokenized,termVector<ti:高级毛料服装洗涤剂>>

计算出来的query为:

ab:毛料 ab:服装 ab:洗涤剂 ab:抗静电 ab:高级 ab:剂 ab:洗涤

计算结果为:

高级毛料服装洗涤剂
抗静电防尘污灭菌广谱洗涤剂及制备方法
一种抗紫外线的织物涂层材料
服装绿色干洗及服装翻新技术
洗碟用柔性含蛋白酶的液体或凝胶洗涤组合物
复合洗霉制剂
洗衣机
一种抗静电合成纤维
实验室专用洗涤剂
液晶相结构型液体洗涤剂及制造工艺

貌似结果不是很相似,那么我们可以试着只用ti做运算,这样从标题看起来比较相似。

还可以对MLT的各项参数进行设置,这里就不在实验了!

[ lucene扩展 ] MoreLikeThis 相似检索相关推荐

  1. MoreLikeThis 相似检索

    出处:http://www.cnblogs.com/huangfox/archive/2012/07/05/2578179.html MoreLikeThis,相似检索.找出某篇文档的相似文档,常见于 ...

  2. 基于Lucene的联系人拼音检索(第一部分)

    需求 实现联系人信息(姓名,电话,邮件,地址等信息的快速实时检索) 姓名字段:全拼的任意相邻组合,每个单字拼音首字母的任意相邻组合,举例:沈从文的全拼是shencongwen,每个单字拼音首字母scw ...

  3. [ lucene扩展 ] 自定义Collector实现统计功能

    对于lucene的统计,我基本放弃使用factedSearch了,效率不高,而且两套索引总觉得有点臃肿! 这次我们通过改造Collector,实现简单的统计功能.经过测试,对几十万的统计还是比较快的. ...

  4. lucene全文检索与数据库检索的区别

    1.性能 数据库:like检索(会把表中数据进行一行一行的扫描,)性能慢 Lucene检索:先把数据那过来建立检索,然后在根据建立的索引进行查找,这样的话我们需要多维护一份索引表.多一个创建索引的过程 ...

  5. 【Java】Lucene检索引擎详解

    基于Java的全文索引/检索引擎--Lucene Lucene不是一个完整的全文索引应用,而是是一个用Java写的全文索引引擎工具包,它可以方便的嵌入到各种应用中实现针对应用的全文索引/检索功能. L ...

  6. 53.大数据之旅——java分布式项目14-信息检索技术之Lucene,Solr

    信息检索技术 概念介绍 全文检索是一种将文件中所有文本与检索项匹配的文字资料检索方法.全文检索系统是按照全文检索理论建立起来的用于提供全文检索服务的软件系统. 全文检索主要对非结构化数据的数据检索. ...

  7. 【搜索那些事】细谈lucene(一)初识全文资源检索框架lucene

    一:lucene历史和简介 Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎.它不是一个完整的搜索应 ...

  8. web 信息模糊检索等 Lucene的实现

    2019独角兽企业重金招聘Python工程师标准>>> 简介: Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,即它不 ...

  9. Lucene:基于Java的全文检索引擎简介(转载)

    Lucene是一个基于Java的全文索引工具包. 基于Java的全文索引引擎Lucene简介:关于作者和Lucene的历史 全文检索的实现:Luene全文索引和数据库索引的比较 中文切分词机制简介:基 ...

最新文章

  1. R语言使用pwr包的pwr.r.test函数对相关信息分析进行效用分析(power analysis)、在已知效应量(effect size)、显著性水平、效用值的情况下计算需要的样本量
  2. canvas之特丑时钟
  3. .top域名应注意什么
  4. antd tree 动态添加_一文了解Matlab如何制作动态图像
  5. 电商必备:如何选择第三方快递?
  6. mysql第四章分页显示查询出租房屋信息_MYSQL必知必会读书笔记第四章之检索数据...
  7. java 计算 时间差_Java8 时间差计算
  8. A summary of OpenGL ES 3.1 demos and samples
  9. 深度学习模型的可视化技术总结
  10. 华南理工大学计算机专业研究生分数线,2020华南理工大学研究生分数线汇总(含2016-2020历年复试)...
  11. mysql5.7 主从
  12. 【SpringBoot_ANNOTATIONS】 生命周期 02 实现InitializingBean, DisposableBean接口
  13. 【教程】Android系统手机 菜鸟扫盲汇总
  14. AI 之 OpenCvSharp 安卓手机摄像头识别人脸
  15. ios设备开发教程-利用app申请ios开发证书及描述文件
  16. 计算机毕业设计 SSM健康知识信息平台 健康自检平台 健康体检管理系统Java
  17. php cmyk图片,php – 使用Imagick将图像从RGB转换为CMYK
  18. 你的背包背到现在还没烂
  19. LeetCode 1006.笨阶乘
  20. JAVA正则表达式判断字符串不能为空和空格、回车\n(Enter)

热门文章

  1. 把WinXP系统升级到Win7的方法
  2. linux教程opensuse,OpenSUSE 11软件源
  3. 开源框架Pushlet入门
  4. 用U盘安装FreeBSD
  5. VMware Horizon 8 2303 - 虚拟桌面基础架构 (VDI) 和应用软件
  6. #网页中动态嵌入PDF文件/在线预览PDF内容#
  7. 怎么退还保证金?湖北智禾教育为你解忧
  8. springboot jpa 配置多数据源
  9. 神经网络的代价函数解释
  10. mac shell终端编辑命令行快捷键——行首,行尾