Lucene学习笔记(3)

很多读者认为搜索完成后只要输出,整个过程就应该结束了。事实确实如此。不过输出的过程存在着以下几个问题

1. 以何种顺序输出?

2. 搜索结果是否全部输出?

3. 以什么形式输出?

下面,我们来一一解决。


4. 排序

4.1 自然排序

关于自然排序前面已经提到,是根据相关度进行排序。所谓相关度,就是前文提到过的文档得分。

注意:文档的得分在每次执行查找时都是不一样的,需要根据查找的关键字来确定。

4.1.1 explain

public Explanation explain(Query query, int doc) throws IOException

explaination类的toString()方法提供的信息能够将一个文档的得分情况详细地列举出来。这在调试中有着重要意义。

Hits htis = searcher.search(q);
for(int i=0; i<hits.length(); i++){
    Document doc = hits.doc(i);
    System.out.print(doc.get("bookname" + "\t\t"));
    System.out.println(hits.score(i));
    System.out.println(searcher.explain(q, hits.id(i)).toString());
}

文档得分主要由四部分内容决定:

1. tf:词条频率

2. idf:反转文档频率

3. boost:Field的激励因子

4. lengthNorm:长度因子

相关算法请参考其他相关资料


4.2 Sort排序

Sort是Lucene自带的一个排序工具,位于org.apache.lucene.search包中。

4.2.1 构造函数

public Sort();

public Sort(String field);

public Sort(String field, boolean reverse);

public Sort(String[] fields);

public Sort(SortField field);

public Sort(SortField[] fields);

其中boolean reverse表示指定升序还是降序排列。默认值为false降序,可设置为true升序。

下一个问题就是最后两种构造方法里的SortField是什么鬼


4.2.2 SortField

SortField是一个包装类型,使Sort类清楚地了解要进行排序的Field的各种信息

部分构造函数如下:

public SortField(String field)

public SortField(String field, boolean reverse)

public SortField(String field, int type)

public SortField(String field, int type, boolean reverse)

只解释一点,就是type,它表示Field的类型。SortField为此提供了几个常量,比如SortField.STRING,SortField.INT,SortField.FLOAT。如果构造函数中没有指定type时,SortField提供了SortField.AUTO由Lucene来对当前Field的类型进行判断。

在Sort内部,所有Field都会被转成SortField的对象数组进行保存。


4.2.3 三种排序方案

通过指定IndexSearch的search方法中的参数确定不同的排序方案。大体有三种:

1. 按文档得分进行排序

2. 按文档的内部ID好来排序

3. 按一个或多个Field来排序


4.2.3.1 按文档得分排序

Hits hits = searcher.searcher(q, Sort.RELEVANCE);


4.2.3.2 按文档的内部ID号来排序

Hits hits = searcher.search(q, Sort.INDEXORDER);

4.2.3.3 按一个或多个Field来排序

在Sort类的内部有多个重载的setSort方法,它们会将用户提交的String型的Field信息转成SortField的对象,并存放于内部的数组中,或是直接将用户提交的SortField数组作为内部的数组,在排序时使用。

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
TermQuery q = new TermQuery(new Term("bookname", "女"));

//定义一个Sort对象
Sort sort = new Sort();
//定义一个SortField对象
SortField f = new SortField("bookNumber", SortField.INT, false);
//添加到Sort中
sort.setSort(f);

//查找并排序
Hits hits = searcher.search(q, sort);
for(int i=0; i<hits.length(); i++){
	Document doc = hits.doc(i);
	......
}


当需要指定更多的Field排序时,只需要将这些参与排序的SortField变成一个数组,并调用Sort实例的setSort方法就可以实现了。

Sort sort = new Sort();
SortField sf1 = new SortField("publishdate", SortField.STRING, true);
SortField sf2 = new SortField("bookNumber", SortField.INT, false );
sort.setSort(new SortField[]{sf1, sf2});


注:在大多数情况下,当使用Sort排序时,排序的内容多为int型或日期型。即便需要进行字符串比较,也最好使用仅包含ASCII码的字符串Field进行。这样可以确保排序的正确性,同时,尽量提高排序的效率。

5. 过滤

Lucene中所有的过滤器都来自于一个抽象的基类org.apache.lucene.search.Filter,它定义了过滤器的基本行为,其中声明的方法:

public abstract BitSet bits(IndexReader reader) throws IOException
读者读到这里怕是会想当然地认为BitSet必然是Lucene定义的一个类,然而事实是BitSet出自java.util,是一种位集合队列,每个元素只能取布尔值(详见Java SE API)。

Lucene以这两种取值来代表文档是否被过滤。

当Lucene返回结果时,会首先遍历BitSet,仅将那些对应值为true的文档返回。BitSet集合中,将其索引号看做是文档的内部ID号。


5.1 Filter的安全级别

Filter共设置了三种安全级别:

public static final int SECURITY_ADVANCED = 0;

public static final int SECURITY_MIDDLE = 1;

public static final int SECURITY_NORMAL = 2;

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
TermQuery q = new TermQuery(new Term("bookname", "女"));

//查找并排序
Hits hits = searcher.search(q, new AdvancedSecurityFilter());
for(int i=0; i<hits.length(); i++){
	Document doc = hits.doc(i);
	......
	int level = Integer.parseInt(doc.get("securitylevel"));
	switch(level){
	case SECURITY_ADVANCED:
		System.out.println("高级");
		break;
	case SECURITY_MIDDLE:
		System.out.println("中级");
		break;
	case SECURITY_NORMAL:
		System.out.println("一般");
		break;
	}
}

5.2 按范围过滤RangeFilter

顾名思义,不解释

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
TermQuery q = new TermQuery(new Term("bookname", "女"));

RangeFilter filter = new RangeFilter("publishdate", "2017-01-01", "2017-01-10", true, true);

//查找并排序
Hits hits = searcher.search(q, filter);
for(int i=0; i<hits.length(); i++){
	Document doc = hits.doc(i);
	......
	int level = Integer.parseInt(doc.get("securitylevel"));
	switch(level){
	case SECURITY_ADVANCED:
		System.out.println("高级");
		break;
	case SECURITY_MIDDLE:
		System.out.println("中级");
		break;
	case SECURITY_NORMAL:
		System.out.println("一般");
		break;
	}
}

5.3 在结果中查询QueryFilter

相信大家在很多搜索引擎中都看到过在结果中搜索这么一栏。什么意思呢?就是在一次检索完毕后希望从上一次检索的结果集合中进行二次检索。Lucene提供QueryFilter来帮助完成这项功能。

QueryFilter的使用只需要使其构造函数接收一个Query对象,该对象可以看做是前一次查询,只要在本次查询时将所构造的QueryFilter对象注入即可。

//进行第一次搜索
Term begin = new Term("publishdate", "2017-01-01");
Term end = new Term("publishdate", "2017-01-10");
RangeQuery q = new RangeQuery(begin, end, true);

//构建QueryFilter,将RangeQuery赋给它
QueryFilter filter = new QueryFilter(q);

//构造TermQuery,查询所有securitylevel为NORMAL的文档
Term normal = new Term("securitylevel", SECURITY_NORMAL + "");
TermQuery query = new TermQuery(normal);

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);

//查找并排序
Hits hits = searcher.search(q, filter);
for(int i=0; i<hits.length(); i++){
	Document doc = hits.doc(i);
	......
	int level = Integer.parseInt(doc.get("securitylevel"));
	switch(level){
	case SECURITY_ADVANCED:
		System.out.println("高级");
		break;
	case SECURITY_MIDDLE:
		System.out.println("中级");
		break;
	case SECURITY_NORMAL:
		System.out.println("一般");
		break;
	}
}


在结果中查询基本原理?

在QueryFilter的bits方法中,调用IndexSearcher方法,对注入的Query进行一次检索,然后在要被返回的BitSet中,将检索结果所对应的文档标记为true,也就是说,过滤掉了所有不在检索范围内的文档。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值