三、排序过滤和分页
仅仅把东西搜出来是不够的,好的检索工具还应当能够对检索的结果进行排序,优先将最相关的内容送出
或是按照某种规则,将检索结果送出。
1.文档得分规则
文档得分主要是由4部分内容来决定,即tf(词条频率)、idf(反转文档频率)、boost(Field的激励因子)
和lengthNorm(长度因子)
tf:某个关键字在某文档中出现次数的平方根
idf:Math.log(numDocs/docFreq+1)+1.0 (numDocs:表示索引中总共的文档数量,docFreq:当前检索
的关键字的文档总数)
lengthNorm:在lucene的底层实现中,lengthNorm是一种固定值,它无需在索引时指定,只需要跟随创建
索引过程,被写入索引中。
boost:文档的boost值一般情况下默认为1.0,在建立索引时可通过预先人为的指定Document或Field的
boost值,来改变搜索时文档的得分,从而改变文档在搜索结果中的排序位置。
2.使用Sort排序
该类有6种构造函数
public Sort()
public Sort(String field) 默认为降序
public Sort(String field,boolean reverse)
public Sort(String[] fields)
public Sort(SortField field)
public Sort(SortField[] fields)
Sort可以对单一的Field进行排序,但是对多个Field的排序就必须要借助SortField类了。SortField类
是个包装类,它能描述Field和reverse(排序)信息,在搜索结果中有很多记录时,应该正确的指定Field
的类型,这样对排序过程的效率会有很大的提高。
排序有多种
a.按文档得分进行排序:这是lucene默认的排序方式
如:Hits hist=searcher.search(q,Sort.RELEVANCE); Sort.RELEVANCE表示当前的排序法则
是按照文档的得分进行降序排列
b.按文档的内部ID号来排序:在建立索引的时候,lucene会为每个文档建立一个内部的ID号
如:Hits hits=searcher.search(q,Sort.INDEXORDER) Sort.INDEXORDER表示排序法则是按
照文档的内部ID号来排序
c.按一个或多个Field来排序:把每个Field封装成SortField,然后以SortField[]数组的形式传入Sort类
注意:比较字符串时,Locale信息的不同,很有可能会影响到比较的结果,因此SortField类有这样的构
造函数,可以帮助指定Locale信息
public SortField(String field,Locale locale)
public SortField(String field,Locale locale,boolean reverse)
当指定Locale后,lucene才可以正确的对字符串进行比较,从而正确的排序。不过大多数情况下,使用Sort
进行排序时,排序的内容多为int型或是日期型。即便需要进行字符串的比较,也最好使用仅包含ASCII的
字符串Field进行,这样可以确保排序的正确,同时,尽量提高排序的效率。
3.搜索时使用过滤器
简单示例:
- public class MySecurityFilter extends Filter{
- public static final int SECURITY=0;
- public BitSet bits(IndexReader reader) throws IOException{
- //先初始化一个BitSet对象
- final BitSet bits=new BitSet(reader.maxDoc());
- //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
- bits.set(0,bits.size()-1);
- //构造一个Term对象,代表最高安全级别
- Term term=new Term("securitylevel",SECURITY+"");
- //从索引中取出具有最高安全级别的文档
- TermDocs termDocs=reader.termDocs(term);
- //遍历每个文档
- while(termDocs.next()){
- bits.set(termDocs.doc(),false);
- }
- retrun bits;
- }
- }
public class MySecurityFilter extends Filter{ public static final int SECURITY=0; public BitSet bits(IndexReader reader) throws IOException{ //先初始化一个BitSet对象 final BitSet bits=new BitSet(reader.maxDoc()); //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的 bits.set(0,bits.size()-1); //构造一个Term对象,代表最高安全级别 Term term=new Term("securitylevel",SECURITY+""); //从索引中取出具有最高安全级别的文档 TermDocs termDocs=reader.termDocs(term); //遍历每个文档 while(termDocs.next()){ bits.set(termDocs.doc(),false); } retrun bits; } }
或者:
- public class MySecurityFilter extends Filter{
- public static final int SECURITY=0;
- public BitSet bits(IndexReader reader) throws IOException{
- //先初始化一个BitSet对象
- final BitSet bits=new BitSet(reader.maxDoc());
- //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的
- bits.set(0,bits.size()-1);
- //构造一个Term对象,代表最高安全级别
- Term term=new Term("securitylevel",SECURITY+"");
- //初始化一个IndexSearcher对象
- //查找securitylevel这个Field的值为SECURITY的文档
- IndexSearcher searcher=new IndexSearcher(term);
- Hits hits=searcher.search(new TermQuery(term));
- for(int i=0;i<hits.length();i++){
- bits.set(hits.id(i),false);
- }
- retrun bits;
- }
- }
public class MySecurityFilter extends Filter{ public static final int SECURITY=0; public BitSet bits(IndexReader reader) throws IOException{ //先初始化一个BitSet对象 final BitSet bits=new BitSet(reader.maxDoc()); //将整个集合置为true,表示当前集合内的所有文档都是可以被检索到的 bits.set(0,bits.size()-1); //构造一个Term对象,代表最高安全级别 Term term=new Term("securitylevel",SECURITY+""); //初始化一个IndexSearcher对象 //查找securitylevel这个Field的值为SECURITY的文档 IndexSearcher searcher=new IndexSearcher(term); Hits hits=searcher.search(new TermQuery(term)); for(int i=0;i<hits.length();i++){ bits.set(hits.id(i),false); } retrun bits; } }
注意:Filter实际上在进行真正的查询之前,已经遍历过一次一遍索引了,因此无
论使用何种Filter,都不应该忽视它对查询效率的影响。
4.在结果中查询(QueryFilter)
在QueryFilter的bits方法中,调用IndexSearcher方法,对注入的Query进行一次
检索,然后在要被返回的BitSet中,将检索结果所对应的文档标记为true,即过滤掉了
所有不再检索结果范围内的文档。