自定义排序思路:继承FieldComparatorSource类并在此类的newComparator方法中返回一个FieldComparator类的子类。
备注:可以参考Lucene的org.apache.lucene.search.FieldComparator.StringValComparator类的源码,自己做做实验 就搞定啦!
背景:初始化自定义排序类的时候,其构造方法有两个参数,分别为numHits和field。
参数说明:
numHits为总行数,用来初始化values数组。numHits是我们自己设定的,我们在TopDocs topDocs = indexSearcher.search(query, filter, 1000, sort);这句话中的第三个参数就是了。如果结果集总数小于我们设定的总个数,则按照结果集总数来确定。
field为排序字段,初始化自定义排序类里面的field属性,这个属性lucene用来拿到相关的数据集。
1、setNextReader: Lucene首先会调用此方法进行数据的初始化,我们这里用一个String数组currentReaderValues来接收。
2、copy:第一次将currentReaderValues里面的两个值拿出来,以后都是一个,然后放到values(注意初始时此数组的值都是null)中的指定位置上。
3、compare:自定义排序,我们可以将我们的代码写到这里,看API描述" Compare hit at sort1 with hit at sort2." ,解释一下sort2和sort1是索引文件中的索引号,注意sort1的值大于sort2的值,也就是说val1为后一个的值,val2为前一个的值,看API描述 "Compare hit at sort1 with hit at sort2."
4、compareBottom:看API描述"Compare the bottom of the queue with doc." 这个方法Lucene时候调用,从描述来看是doc序列的尾部,呵呵,自定义排序列中的bottom属性值就是,我们要拿出来的序列中最后面的那个。
流程是: 如果优先队列满了,则先和最低层的比较,如果大于最底层的则先替代最底层的,然后对优先队列重新排序 ,队列的最大值是通过ndexSearcher.search方法中可以设置的,当然Lucene的队列的长度最大值我看了应该是2亿多。所以不用担心了。那么对列什么时候满了呢?队列的最大值是什么呢?答案是: 队列的最大值 由用户自己设定,TopDocs topDocs = indexSearcher.search(query, filter, 1000, sort);这句话中 的第三个参数就是了。
代码:
1、DateValComparator类
package com.meiya.comparator; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.Date; import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.FieldComparator; /** * 实现自定义日期排序 * * @author 林计钦 * @version 1.0 2013-6-9 上午08:57:54 */ @SuppressWarnings("unchecked") public class DateValComparator extends FieldComparator { /** 存储排序的数据集 */ private String[] values; /** 接收从IndexReader读出来的字符串数组,即排序前结果集 */ private String[] currentReaderValues; /** 排序字段 */ private final String field; /** doc的尾部序列 */ private String bottom; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); /** * 默认构造函数 * * @param numHits 总行数 * @param field 排序字段 */ DateValComparator(int numHits, String field) { this.values = new String[numHits]; this.field = field; } /** * 第三步:自定义日期排序,如果查询结果没有超过我们设定的总行数那么会调用这个方法进行比较 <br/><br/> * * 如果sort1小于sort2,返回一个负数;<br/> 如果sort1大于sort2,返回一个正数;<br/> 如果他们相等,则返回0;<br/><br/> * * 注意sort1的值大于sort2的值 */ @Override public int compare(int sort1, int sort2) { try { Date after = format.parse(this.values[sort1]); Date before = format.parse(this.values[sort2]); if (null == after) { if (null == before) { return 0; } return -1; } if (null == before) { return 1; } if (after.after(before)) { return 1; } if (before.after(after)) { return -1; } } catch (Exception e) { e.printStackTrace(); } return 0; } /** * Lucene的查询结果是放在优先队列里面的,优先对象是通过compare进行比较,如果查询结果超过了我们设定的总行数那么会第二步调用这个方法 */ @Override public int compareBottom(int doc) throws IOException { try { Date before = format.parse(this.currentReaderValues[doc]); Date tempBottom = format.parse(this.bottom); if (tempBottom == null) { if (before == null) { return 0; } return -1; } if (before == null) { return 1; } if (before.after(tempBottom)) { return 1; } if (tempBottom.after(before)) { return -1; } } catch (Exception e) { e.printStackTrace(); } return 0; } /** * 第二步:将currentReaderValues数组中的第一个值copy到values中对应的位置中,注意此方法第一次调用了两次。 */ @Override public void copy(int sort, int doc) throws IOException { this.values[sort] = this.currentReaderValues[doc]; } /** * 如果查询结果超过了我们设定的总行数,那么会第一步调用这个方法 */ @Override public void setBottom(int bottom) { this.bottom = this.values[bottom]; } /** * 第一步:Lucene首先调用此方法进行数据的初始化,这里用currentReaderValues数组参数来接收。 */ @Override public void setNextReader(IndexReader reader, int docBase) throws IOException { this.currentReaderValues = FieldCache.DEFAULT.getStrings(reader, this.field); } /** * 输出排序后的结果集 */ @Override public String value(int sort) { return this.values[sort]; } }
2、DateValComparatorSource类
package com.meiya.comparator; import java.io.IOException; import org.apache.log4j.Logger; import org.apache.lucene.search.FieldComparator; import org.apache.lucene.search.FieldComparatorSource; /** * 自定义日期排序调用入口类 * * @author 林计钦 * @version 1.0 2013-6-9 上午10:07:58 */ @SuppressWarnings("serial") public class DateValComparatorSource extends FieldComparatorSource{ private static final Logger LOG=Logger.getLogger(DateValComparatorSource.class); @SuppressWarnings("unchecked") @Override public FieldComparator newComparator(String field, int numHits, int newCount, boolean reversed) throws IOException { LOG.info(String.format("总行数:%s,排序字段:%s,排序方式:%s。", numHits, field, reversed==true?"降序":"升序")); return new DateValComparator(numHits, field); } }
3、单元测试代码
/** * 自定义日期排序单元测试 * * @throws Exception */ @Test public void sort() throws Exception { IndexReader reader = IndexReader.open(FSDirectory.open(new File(XMLPropertyConfig.getConfigXML().getString("meiya_movice_index"))), true); IndexSearcher searcher = new IndexSearcher(reader); TermRangeQuery timeQuery=new TermRangeQuery(MyMovieSource.UPDATE_TIME, "1988-03-09", "2014-01-07", true, true); //true倒序,false升序 //Sort sort = new Sort(new SortField("update_time", new DateValComparatorSource(), false)); //升序 Sort sort = new Sort(new SortField("update_time", new DateValComparatorSource(), true)); //降序 TopDocs topDocs=searcher.search(timeQuery, 1000, sort); System.out.println("共检索出 " + topDocs.totalHits + " 条记录"); System.out.println(); ScoreDoc[] scoreDocs = topDocs.scoreDocs; for (ScoreDoc scDoc : scoreDocs) { Document document = searcher.doc(scDoc.doc); System.out.println("更新时间:"+document.get(MyMovieSource.UPDATE_TIME)); } searcher.close(); reader.close(); }
4、测试结果如下
[INFO ] [2013-06-09 11:46:19 187]-com.meiya.util.XMLPropertyConfig.61[main] - 准备加载配置文件【AppConfig.xml】。 [INFO ] [2013-06-09 11:46:19 265]-com.meiya.util.XMLPropertyConfig.63[main] - 配置文件【AppConfig.xml】加载完成。 [INFO ] [2013-06-09 11:46:19 406]-com.meiya.comparator.DateValComparatorSource.25[main] - 总行数:1000,排序字段:update_time,排序方式:升序。 共检索出 11997 条记录 更新时间:2009-12-27 23:43:11 更新时间:2009-12-28 10:22:09 更新时间:2009-12-28 17:35:45 更新时间:2009-12-31 00:24:32 更新时间:2009-12-31 17:52:09 更新时间:2010-01-01 01:29:00 更新时间:2010-01-01 18:51:37 更新时间:2010-01-01 22:26:40
整理自http://javastudyeye.iteye.com/blog/893921