在Lucene4.4中,想要实现搜索结果按照时间倒序的效果:如果两个文档得分相同,那么就按照发布时间倒序排列;否则就按照分数排列。这种效果在Lucene4.6中实现起来极其简单,直接利用search接口的Sort参数即可达成,完全不需要像某些人说的重写Similarity那么麻烦。三两行代码的事情,体现了Make it simple, stupid的精髓。

首先来看看测试例子,这个例子中我建立了四个文档,按照内容-发布日期来表示分别是:

2004年光棍节攻略 , 20041111

2005年光棍节攻略 , 20051111

2006年光棍节攻略 , 20061111

游戏攻略 ,20141111

统一使用“光棍节攻略”来搜索它们,用户希望最新的光棍节攻略排在第一。

如果不做排序处理的话,用户体验非常糟糕:

  1. package com.hankcs.test;
  2. import org.apache.lucene.analysis.Analyzer;
  3. import org.apache.lucene.document.*;
  4. import org.apache.lucene.index.*;
  5. import org.apache.lucene.queries.CustomScoreQuery;
  6. import org.apache.lucene.queries.function.FunctionQuery;
  7. import org.apache.lucene.queryparser.classic.ParseException;
  8. import org.apache.lucene.queryparser.classic.QueryParser;
  9. import org.apache.lucene.search.*;
  10. import org.apache.lucene.store.Directory;
  11. import org.apache.lucene.store.LockObtainFailedException;
  12. import org.apache.lucene.store.RAMDirectory;
  13. import org.apache.lucene.util.Version;
  14. import org.wltea.analyzer.lucene.IKAnalyzer;
  15. import java.io.IOException;
  16. /**
  17. * @author hankcs
  18. */
  19. public class TestSortByTime
  20. {
  21. public static void main(String[] args)
  22. {
  23. // Lucene Document的主要域名
  24. String fieldName = "text";
  25. // 实例化IKAnalyzer分词器
  26. Analyzer analyzer = new IKAnalyzer();
  27. Directory directory = null;
  28. IndexWriter iwriter;
  29. IndexReader ireader = null;
  30. IndexSearcher isearcher;
  31. try
  32. {
  33. //索引过程**********************************
  34. //建立内存索引对象
  35. directory = new RAMDirectory();
  36. //配置IndexWriterConfig
  37. IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_46, analyzer);
  38. iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
  39. iwriter = new IndexWriter(directory, iwConfig);
  40. //写入索引
  41. for (int i = 0; i < 3; ++i)
  42. {
  43. int year = 2004 + i;
  44. Document doc = new Document();
  45. doc.add(new TextField(fieldName, year + "年光棍节攻略", Field.Store.YES));
  46. doc.add(new IntField("date",  year * 10000 + 1111, Field.Store.YES));
  47. iwriter.addDocument(doc);
  48. }
  49. // 加入一个干扰文档
  50. Document doc = new Document();
  51. doc.add(new TextField(fieldName, "游戏攻略", Field.Store.YES));
  52. doc.add(new IntField("date",  20141111, Field.Store.YES));
  53. iwriter.addDocument(doc);
  54. iwriter.close();
  55. //搜索过程**********************************
  56. //实例化搜索器
  57. ireader = DirectoryReader.open(directory);
  58. isearcher = new IndexSearcher(ireader);
  59. String keyword = "光棍节攻略";
  60. //使用QueryParser查询分析器构造Query对象
  61. QueryParser qp = new QueryParser(Version.LUCENE_46, fieldName, analyzer);
  62. Query query = qp.parse(keyword);
  63. System.out.println("Query = " + query);
  64. //搜索相似度最高的5条记录
  65. TopDocs topDocs = isearcher.search(query, 5);
  66. System.out.println("命中:" + topDocs.totalHits);
  67. //输出结果
  68. ScoreDoc[] scoreDocs = topDocs.scoreDocs;
  69. for (int i = 0; i < Math.min(5, scoreDocs.length); i++)
  70. {
  71. Document targetDoc = isearcher.doc(scoreDocs[i].doc);
  72. System.out.print(targetDoc.getField(fieldName).stringValue());
  73. System.out.print(" , " + targetDoc.getField("date").numericValue());
  74. System.out.println(" , " + scoreDocs[i].score);
  75. }
  76. } catch (CorruptIndexException e)
  77. {
  78. e.printStackTrace();
  79. } catch (LockObtainFailedException e)
  80. {
  81. e.printStackTrace();
  82. } catch (IOException e)
  83. {
  84. e.printStackTrace();
  85. } catch (ParseException e)
  86. {
  87. e.printStackTrace();
  88. } finally
  89. {
  90. if (ireader != null)
  91. {
  92. try
  93. {
  94. ireader.close();
  95. } catch (IOException e)
  96. {
  97. e.printStackTrace();
  98. }
  99. }
  100. if (directory != null)
  101. {
  102. try
  103. {
  104. directory.close();
  105. } catch (IOException e)
  106. {
  107. e.printStackTrace();
  108. }
  109. }
  110. }
  111. }
  112. }

输出:

2004年光棍节攻略 , 20041111 , 0.71185887

2005年光棍节攻略 , 20051111 , 0.71185887

2006年光棍节攻略 , 20061111 , 0.71185887

游戏攻略 , 20141111 , 0.049675122

可以看到文档是严格按照分数排序的,如果分数相同,则按照索引顺序排序,导致最新的文章反而排在最下面。

使用search接口的Sort参数优化搜索结果:

  1. package com.hankcs.test;
  2. import org.apache.lucene.analysis.Analyzer;
  3. import org.apache.lucene.document.*;
  4. import org.apache.lucene.index.*;
  5. import org.apache.lucene.queries.CustomScoreQuery;
  6. import org.apache.lucene.queries.function.FunctionQuery;
  7. import org.apache.lucene.queryparser.classic.ParseException;
  8. import org.apache.lucene.queryparser.classic.QueryParser;
  9. import org.apache.lucene.search.*;
  10. import org.apache.lucene.store.Directory;
  11. import org.apache.lucene.store.LockObtainFailedException;
  12. import org.apache.lucene.store.RAMDirectory;
  13. import org.apache.lucene.util.Version;
  14. import org.wltea.analyzer.lucene.IKAnalyzer;
  15. import java.io.IOException;
  16. /**
  17. * @author hankcs
  18. */
  19. public class TestSortByTime
  20. {
  21. public static void main(String[] args)
  22. {
  23. // Lucene Document的主要域名
  24. String fieldName = "text";
  25. // 实例化IKAnalyzer分词器
  26. Analyzer analyzer = new IKAnalyzer();
  27. Directory directory = null;
  28. IndexWriter iwriter;
  29. IndexReader ireader = null;
  30. IndexSearcher isearcher;
  31. try
  32. {
  33. //索引过程**********************************
  34. //建立内存索引对象
  35. directory = new RAMDirectory();
  36. //配置IndexWriterConfig
  37. IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_46, analyzer);
  38. iwConfig.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
  39. iwriter = new IndexWriter(directory, iwConfig);
  40. //写入索引
  41. for (int i = 0; i < 3; ++i)
  42. {
  43. int year = 2004 + i;
  44. Document doc = new Document();
  45. doc.add(new TextField(fieldName, year + "年光棍节攻略", Field.Store.YES));
  46. doc.add(new IntField("date",  year * 10000 + 1111, Field.Store.YES));
  47. iwriter.addDocument(doc);
  48. }
  49. // 加入一个干扰文档
  50. Document doc = new Document();
  51. doc.add(new TextField(fieldName, "游戏攻略", Field.Store.YES));
  52. doc.add(new IntField("date",  20141111, Field.Store.YES));
  53. iwriter.addDocument(doc);
  54. iwriter.close();
  55. //搜索过程**********************************
  56. //实例化搜索器
  57. ireader = DirectoryReader.open(directory);
  58. isearcher = new IndexSearcher(ireader);
  59. String keyword = "光棍节攻略";
  60. //使用QueryParser查询分析器构造Query对象
  61. QueryParser qp = new QueryParser(Version.LUCENE_46, fieldName, analyzer);
  62. Query query = qp.parse(keyword);
  63. System.out.println("Query = " + query);
  64. //搜索相似度最高的5条记录
  65. Sort sort = new Sort(new SortField("text", SortField.Type.SCORE), new SortField("date", SortField.Type.INT, true));
  66. TopDocs topDocs = isearcher.search(query, 5, sort);
  67. System.out.println("命中:" + topDocs.totalHits);
  68. //输出结果
  69. ScoreDoc[] scoreDocs = topDocs.scoreDocs;
  70. for (int i = 0; i < Math.min(5, scoreDocs.length); i++)
  71. {
  72. Document targetDoc = isearcher.doc(scoreDocs[i].doc);
  73. System.out.print(targetDoc.getField(fieldName).stringValue());
  74. System.out.print(" , " + targetDoc.getField("date").numericValue());
  75. System.out.println(" , " + scoreDocs[i].score);
  76. }
  77. } catch (CorruptIndexException e)
  78. {
  79. e.printStackTrace();
  80. } catch (LockObtainFailedException e)
  81. {
  82. e.printStackTrace();
  83. } catch (IOException e)
  84. {
  85. e.printStackTrace();
  86. } catch (ParseException e)
  87. {
  88. e.printStackTrace();
  89. } finally
  90. {
  91. if (ireader != null)
  92. {
  93. try
  94. {
  95. ireader.close();
  96. } catch (IOException e)
  97. {
  98. e.printStackTrace();
  99. }
  100. }
  101. if (directory != null)
  102. {
  103. try
  104. {
  105. directory.close();
  106. } catch (IOException e)
  107. {
  108. e.printStackTrace();
  109. }
  110. }
  111. }
  112. }
  113. }

输出结果:

命中:4

2006年光棍节攻略 , 20061111 , NaN

2005年光棍节攻略 , 20051111 , NaN

2004年光棍节攻略 , 20041111 , NaN

游戏攻略 , 20141111 , NaN

我们看到“2006年光棍节攻略”因为时间比较新,并且相关性高,就排在了第一。“2005年光棍节攻略”相关度相同,因为时间旧就排在后面一点,而干扰文档“游戏攻略”即使时间最新,因为不相关的原因排在最后面。这种效果正好是我想要的,极大提升了用户体验。

Lucene 时间排序的更多相关文章

  1. lucene之排序、设置权重、优化、分布式搜索(转)

    lucene之排序.设置权重.优化.分布式搜索(转) 1. 基本应用 using System;using System.Collections.Generic;using System.Text;u ...

  2. 如何对sharepoint图片库的文件夹的图片按照时间排序并分页显示

    /// <summary> /// 获取图片库第一层文件夹--根据文件夹名称排序 /// </summary> /// <param name="siteUrl ...

  3. ls按时间排序输出文件列表

    文件转自:http://www.2cto.com/os/201303/197829.html ls按时间排序输出文件列表   首先,ls --help查看ls相关的与时间排序相关的参数:   > ...

  4. C#实现对指定文件夹中文件按修改时间排序

    string path = "~/Document/Introduction/团队管理制度/";            DirectoryInfo dirinfo = new Di ...

  5. PHP读取文件夹目录,按时间排序,大小排序,名字排序

    工作中有时候会遇到文件存储数据,但是在前台显示的时候又因为没有数据库,无法使用上传或最后一次修改日期字段排序,所以有了如下代码: <?php $dir = "./";//目录 ...

  6. lucene查询排序结果原理总结

    参考文章 Lucene3.0结果排序原理+操作+示例 Lucene的排序算法 一句话总结lucene排序算法是什么样的 关键几个概念 参考文档: http://lucene.apache.org/co ...

  7. 几种能在O(n*log(n))时间排序

    线性时间排序   各种排序算法总结已经介绍了几种能在O(n*log(n))时间内培训n个数的算法.归并排序和堆排序达到了最坏情况下的上界:快速排序在平均情况下达到该上界.这些算法都有一个有趣的性质:在 ...

  8. linux_常用命令_(ls, lsof,nslookup)_查看文件按照时间排序

    平时收集些用到的命令 方便使用 1:  ls -lrt 按时间排序  展示 2:nslookup  查看dns解析 3:lsof -p 进程号 lsof `which httpd` //那个进程在使用 ...

  9. Linux中ls对文件进行按大小排序和按时间排序,设置ls时间格式

    1 按文件大小排序 使用 ll -S | grep '^[^d]' // 格式化文件大小形式 ll -Sh | grep '^[^d]' 2 按文件修改时间排序显示 使用 ll -rt 3 设置ls ...

随机推荐

  1. 让你的Mac支持NTFS

    前段时间换成Mac电脑之后,发现有一点不爽,不能在Mac下写入NTFS格式的磁盘,所以就去研究了一下. 解决方法有如下三种. 第一种,直接使用第三方软件,如Paragon NTFS for MAC,T ...

  2. 6-tips-for-managing-property-files-with-spring--转

    原文地址:http://www.summa.com/blog/2009/04/20/6-tips-for-managing-property-files-with-spring What could ...

  3. 1Z0-053 争议题目解析134

    1Z0-053 争议题目解析134 考试科目:1Z0-053 题库版本:V13.02 题库中原题为: 134.You are managing an Oracle Database 11g datab ...

  4. JS代码实现的聊天框

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  5. C#解决Linq OrderBy() 失效的小技巧

    前言 前几天的一个数据列表中我用了Linq GroupBy 和OrderBy. 排序在本机正常使用,发到测试后排序死活不对,很是郁闷,总以为是程序问题.于是请教了另外一个同事.有了以下的答案. 问题原 ...

  6. 魔方渗透系统安装VMtools教程

    虚拟机魔方渗透系统安装VMtools教程 1.开机登陆后,如图点击安装VMtools. 2.进入media文件夹: cd /media   查看mdia文件夹内容: ls   3.打开VMware T ...

  7. 【UWP开发】一个简单的Toast实现

    Toast简介 在安卓里Toast是内置原生支持,它是Android中用来显示显示信息的一种机制.它主要用于向用户显示提示消息,没有焦点,显示的时间有限,过一定的时间就会自动消失.在UWP中虽然没有原 ...

  8. 在 .NET 中远程请求 https 内容时,发生错误:根据验证过程,远程证书无效。

    当访问 https 内容的时候,有时候经常会看到证书错误(不在操作系统的证书信任链中?)的提示,在浏览器中我们可以忽略错误的证书,继续访问网页内容. 但是在 .NET 程序中,需要由代码来判断是否忽略 ...

  9. 第二篇:Entity Framework CodeFirst & Model 映射

    前一篇 第一篇:Entity Framework 简介 我有讲到,ORM 最关键的 Mapping,也提到了最早实现Mapping的技术,就是 特性 + 反射,那Entity Framework 实现 ...

  10. C#不对称加密

    对称加密的缺点是双方使用相同的密钥和IV进行加密.解密.由于接收方必须知道密钥和IV才能解密数据,因此发送方需要先将密钥和IV传递给接收方.这就 有一个问题,如果攻击者截获了密钥和IV,也就等于知道了 ...