Lucene学习笔记(2)

在完成了文档预处理和索引的建立之后,就可以执行搜索操作了。本文将对Lucene搜索的二三事啰嗦一二。

3. Lucene搜索

3.1 IndexSearcher

Lucene中使用IndexSearcher类进行搜索。此类包含在org.apache.lucene.search包中。

//初始化一个IndexSearcher
IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//构建一个Term对象
Term t = new Term("bookname", "女");
//构建一个Query对象
Query q = new TermQuery(t);

//检索
Hits hits = searcher.search(q);
//显示查询结果
for(int i=0; i<hits.length(); i++){System.out.println(hits.doc(i));}


这段代码显示了IndexSearcher进行搜索的基本步骤,但相信读者读起来一定一脸懵逼。不过总觉得似乎有些地方看起来似曾相识。Query,最初是在Hibernate中所见,也是执行查询操作的类,在此处执行同样的功能,表示一个查找请求。不同的是,此处使用TermQuery构建其实例,且接收的参数为Term对象。

Hits对象表示查找结果,通过它可以访问检索到的Document。


3.2 Hits

Hits类在上文已有介绍,在此处,我们先来看一下它的几个公有接口

//取得当前结果集的数量
public final int length()

//取得当前结果集中第N个Document
public final Document doc(int n) throws IOException

//取得当前结果集中第N个Document的得分
public final float score(int n) throws IOException

//取得当前结果集中第N个Document的索引内部ID值
public final int id(int n) throws IOException

//取得对Hits集合的遍历对象
public Iterator iterator();


3.2.1 Hits内部的缓存

在Hits对象内部,保持了一个缓存。它用于存放一定数量的文档。每次用户要取出文档时,Hits都会先访问这个缓存。若缓存中存在该文档,则返回文档,否则执行查询。


3.3 搜索结果的评分

在前面,我们曾多次提到过评分。什么是评分呢?

实际上,通过Searcher检索出来的每个文档都有一个分值,该分值将作为自然排序的依据,即搜索结果输出的次序。

关于文档的分值是如何计算得出,请参考相关文章,这里不作详述。

3.3.1 激励因子

每个Field都会被设置一种激励因子,默认值为1。它增加了Field的重要性,也即增加了Document的重要性。重要性的增加就是指文档得分的增加。可通过Field类的方法setBoost(......) 人为设置激励因子的值。


3.4 Query

Lucene提供了大量Query的子类,可满足各式各样的查询需求,接下来我们分别介绍这些子类。

3.4.1 TermQuery词条搜索

通过对某个固定词条的指定,实现检索索引中存在该词条的所有文档。

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
Term t = new Term("bookname", "女");
Query q = new TermQuery(t);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
	System.out.println(hits.score(i));
	System.out.println(hits.id(i));
}


3.4.2 BooleanQuery布尔搜索

布尔型查询就是一个由多个子句和子句间的布尔逻辑所组成的查询。此种搜索方式的前提是处理多个查询子句。

BooleanQuery依靠BooleanClause.Occur类处理子句间的逻辑关系。

BooleanClause.Occur有三种表示:MUST、MUST_NOT、SHOULD

顾名思义,即是必须,不准,应当之意。两两组合,公有六种。

1. MUST和MUST

2. MUST和MUST_NOT

3. MUST_NOT和MUST_NOT

并没有实际意义,理解为两个子句的结果都不要,最终结果不会有任何值。

4. SHOULD和MUST

相当于MUST

5. SHOULD和MUST_NOT

相当于2

6. SHOULD和SHOULD

或关系,得并集

3.4.3 RangeQuery范围搜索

查找一定范围内的文档,这种范围可以是时间、日期、数字等。

IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
//不生成复合文件
writer.setUseCompoundFile(false);

Document doc1 = new Document();
Document doc2 = new Document();
Document doc3 = new Document();

Field bookNo1 = new Field("booknumber", "0001", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo2 = new Field("booknumber", "0002", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo3 = new Field("booknumber", "0003", Field.Store.YES, Field.Index.UN_TOKENIZED);

doc1.add(bookNo1);
doc2.add(bookNo2);
doc3.add(bookNo3);

writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);

writer.close();

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//RangeQuery的下界
Term begin = new Term("booknumber", "0002");
//RangeQuery的上界
Term end = new Term("booknumber", "0003");
//检索位于上、下界间的所有文档
RangeQuery q = new RangeQuery(begin, end, false);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}


实例化RangeQuery时所赋布尔型参数表示范围的开区间与闭区间。

3.4.4 PrefixQuery前缀搜索
顾名思义,根据词条的前缀搜索。

IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
//不生成复合文件
writer.setUseCompoundFile(false);

Document doc1 = new Document();
Document doc2 = new Document();
Document doc3 = new Document();

Field bookNo1 = new Field("bookname", "三侠五义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo2 = new Field("bookname", "三国演义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo3 = new Field("bookname", "西游记", Field.Store.YES, Field.Index.UN_TOKENIZED);

doc1.add(bookNo1);
doc2.add(bookNo2);
doc3.add(bookNo3);

writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);

writer.close();

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//构建一个Term,用于表示要查找的前缀
Term prefix = new Term("bookname", "三");
//构建前缀查询
PrefixQuery q = new PrefixQuery(prefix);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}


3.4.5 PhraseQuery短语搜索

根据短语搜索

IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
//不生成复合文件
writer.setUseCompoundFile(false);

Document doc1 = new Document();
Document doc2 = new Document();
Document doc3 = new Document();

Field bookNo1 = new Field("bookname", "三侠五义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo2 = new Field("bookname", "三国演义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo3 = new Field("bookname", "西游记", Field.Store.YES, Field.Index.UN_TOKENIZED);

doc1.add(bookNo1);
doc2.add(bookNo2);
doc3.add(bookNo3);

writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);

writer.close();

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//构建一个Term,用于表示要查找的前缀
Term t1 = new Term("bookname", "三");
Term t2 = new Term("bookname", "国");
//构建短语查询
PhraseQuery q = new PhraseQuery();
query.add(t1);
query.add(t2);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}


在实际中,我们常常输入不完全,比如三国演义四字输成三国义,那么此时该怎么办呢?

PhraseQuery提供了一种称为“坡度”的参数,用于表示词组的两个字间可以插入无关字数的个数。默认值为0.

通过public void setSlop(int s)方法即可设置坡度。

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//构建一个Term,用于表示要查找的前缀
Term t1 = new Term("bookname", "三");
Term t2 = new Term("bookname", "义");
//构建短语查询
PhraseQuery q = new PhraseQuery();
query.add(t1);
query.add(t2);
query.setSlop(2);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}
此处表示三和义之间可插入两个字。


3.4.6 MultiPhraseQuery多短语搜索

对多个短语同时进行检索

IndexWriter writer = new IndexWriter(INDEX_STORE_PATH, new StandardAnalyzer(), true);
//不生成复合文件
writer.setUseCompoundFile(false);

Document doc1 = new Document();
Document doc2 = new Document();
Document doc3 = new Document();

Field bookNo1 = new Field("bookname", "三侠五义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo2 = new Field("bookname", "三国演义", Field.Store.YES, Field.Index.UN_TOKENIZED);
Field bookNo3 = new Field("bookname", "西游记", Field.Store.YES, Field.Index.UN_TOKENIZED);

doc1.add(bookNo1);
doc2.add(bookNo2);
doc3.add(bookNo3);

writer.addDocument(doc1);
writer.addDocument(doc2);
writer.addDocument(doc3);

writer.close();

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
//构建短语查询
MultiPhraseQuery q = new MultiPhraseQuery();
//加入要查找的短语的前缀
query.add(new Term("bookname", "三"));
//构建两个Term,作为短语的后缀
Term t1 = new Term("bookname", "国");
Term t2 = new Term("bookname", "侠");
query.add(new Term[]{t1,t2});
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}


3.4.7 FuzzyQuery模糊搜索

使用levenshtein作为匹配算法。这种算法在比较两个字符串时,将动作分为三种:

1. 加一个字母

2. 删一个字母

3. 改变一个字母

两个字符串进行比较时,实质就是执行将其中一个字符串转变为另一个。没执行上面一种动作,将扣除一定分数。比较完毕后,最终得分成为两者之间的距离,也叫模糊度。

分数越高,匹配度越高,默认最小相似度为0.5。该值可在实例化FuzzyQuery时作为参数加入。另一个参数prefixLength表示在进行模糊匹配时要有多少个前缀字母要完全匹配。

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
Term t = new Term("bookname", "三");
FuzzyQuery q = new FuzzyQuery(t, 0.1f, 1);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}

3.4.8 WildcardQuery通配符搜索
通配符不用多做解释了,重新啰嗦几句它的基本符号

1. *:表示0到多个字符

2. ?:表示一个单一字符

IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
Term t = new Term("bookname", "?国*");
WildcardQuery q = new WildcardQuery(t);
Hits hits = searcher.search(q);
for(int i=0; i<hits.length(); i++){
	System.out.println(hits.doc(i));
}


3.4.9 多Field搜索与多索引搜索

3.4.9.1 多域搜索MultiFieldQueryParser

该类可完成三种搜索

1. 在不同的Field上进行不同的查找

2. 在不同的Field上进行同一个查找,指定它们之间的布尔关系

3. 在不同的Field上进行不同的查找,指定它们之间的布尔关系


3.4.9.2 MultiSearcher在多个索引上搜索

在不同的索引上执行不同的搜索(当然在同一索引执行多种搜索然后Boolean也是可以的)。MultiSearcher的原理是对一个IndexSearcher的数组进行循环遍历。分明进行查找,然后将结果进行合并,使用一个HitCollector收集后返回。


3.4.9.3 ParalellMultiSearcher多线程搜索

多线程版本的MultiSearcher

但是效率不一定比MultiSearcher高,和计算机性能有关

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值