1.实现简单的搜索功能
1.1 对特定项的搜索
IndexSearcher类是用于对索引中文档进行搜索的核心类。它有几个重载的搜索方法。可以使用最常用的搜索方法对特定的项进行搜索。一个项由一个字符串类型的域值和对应的域名构成。下面的例子展示了使用TermQuery进行搜索:
public class BasicSearchingTest extends TestCase {
public void testTerm() throws Exception {
Directory dir = TestUtil.getBookIndexDirectory();
IndexSearcher searcher = new IndexSearcher(dir);
Term t = new Term("subject", "ant");
Query query = new TermQuery(t);
TopDocs docs = searcher.search(query, 10);
assertEquals("Ant in Action", 1, docs.totalHits);
t = new Term("subject", "junit");
docs = searcher.search(new TermQuery(t), 10);
assertEquals("Ant in Action, JUinit in Action, Second Edition", 2, docs.totalHits);
searcher.close();
dir.close();
}
}
1.2 解析用户输入的查询表达式:QueryParser
从上面的例子中看出,Lucene的搜索方法需要接受一个query对象作为参数。而QueryParser的作用就是将用户的输入转换成query对象。下面的例子展示了如何将用户输入的"+JUINIT +ANT -MOCK"转换成query对象并执行查询操作
public void testQueryParser() throws Exception {
Directory dir = TestUtil.getBookIndexDirectory();
IndexSearcher searcher = new IndexSearcher(dir);
QueryParser parser = new QueryParser(Version.LUCENE_30, "contents", new SimpleAnalyzer());
Query query = parser.parse("+JUINIT +ANT -MOCK");
TopDocs docs = searcher.search(query, 10);
assertEquals("Ant in Action, JUinit in Action, Second Edition", 2, docs.totalHits);
searcher.close();
dir.close();
}
通过QueryParser,应用可以解析非常复杂的查询表达式,最后生成的query实例可能会非常庞大而复杂。
2.使用IndexSearch类
从上述的例子中可以看出lucene的搜索操作非常简单。首先创建一个IndexSearcher实例,它负责打开所用,然后使用该实例的search方法即可进行搜索。
2.1 创建IndexSearcher类
IndexSearcher实例的创建非常简单。
Directory dir = FSDirectory.open("/path/to/index");
IndexReader reader = IndexReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);
上述的例子第一步创建了一个目录的实例,该实例指向了索引存放的位置,第二步创建一个索引只读的实例,最终创建一个IndexSearcher实例。
打开IndexReader需要较大的系统开销,所以原则上复用IndexReader就尽量复用。上述例子中的IndexReader是手动创建的,当IndexSearcher关闭时,IndexReader并不会自动关闭。另外还可以从索引目录中直接创建IndexSearcher,这种情况下系统会自动创建一个只属于该searcher的IndexReader实例,当searcher被关闭时,reader也会同时被关闭。
需要理解的一点是,IndexReader实例是索引的一个快照,所以如果reader被创建之后,索引继续更新,该reader是无法看到被更新的索引内容的。此时如果想看到最新的更新,必须新打开一个Reader。记住,索引的更新不能立即可见,必须通过重新打开IndexReader。
2.2 实现搜索功能
IndexSearcher实现了很多的search方法。在程序后台,search方法会快速完成大量的工作。它会访问所有候选的搜索匹配文档,并返回符合每个查询约束条件的结构。最后,它会收集最靠前的几个搜索结果并返回给调用程序。
2.3 使用TopDocs类
我们已经调用了search方法,并获取其返回的TopDocs对象,我们可以利用该对象来访问搜索结果。TopDocs有以下几个重要的属性或方法:
totalHits: 匹配搜索条件的文档数量
scoreDocs: 包含搜索结果的ScoreDoc对象数组
getMaxScore(): 如果已完成排序则返回最大评分
2.4 搜索结果分页
如果要将搜索结果呈现给终端用户,通常是只将前10~20个最相关的文档展现出来。通过ScoreDocs进行分页处理是一个常见的需求。
2.5 近实时搜索
Lucene2.9版本发布的新功能之一就是近实时搜索,它使你能够使用一个打开的IndexWriter快速搜索索引的变更内容,而不必首先关闭writer或向该writer提交。上面的例子中的IndexReader实例的创建依赖于Directory,近实时搜索的IndexReader创建如下:
IndexWriter writer = new IndexWriter(dir);
IndexReader reader = writer.getReader();
当有新的索引写入时,使用下面的方法重新打开一个IndexReader
IndexReader newReader = reader.reopen()
reader.close()
searcher = new IndexSearcher(reader)
上述的reopen方法打开的reader可以获取到writer写入但未提交的文档。注意一点IndexReader始终是索引某一时刻的只读快照,如果想获取到更新的索引,必须重新打开一个IndexReader。
3.理解Lucene的评分机制
每当搜索到匹配文档时,该文档会被赋予一定的分值,用以反映匹配程度。评分公式这里就不具体展开的,有兴趣的可以自行了解
4. Lucene的多样化查询
除了上述例子中的单项查询,Lucene还支持其它一些场景的查询,如范围查询,组合查询等等。这里简单介绍几个例子
4.1 在指定的项范围内搜索:TermRangeQuery类
索引中的各个Term对象会按照字典编排顺序进行排序,并允许在Lucene的TermRangeQuery对象提供的范围内进行文本项的直接搜索。
public void testTermRangeQuery() throws Exception {
Directory dir = TestUtil.getBookIndexDirectory();
IndexSearcher searcher = new IndexSearcher(dir);
TermRangeQuery query = new TermRangeQuery("title2", "d", "j", true, true);
TopDocs matches = searcher.search(query, 100);
assertEquals(3, matches.totalHits);
searcher.close();
dir.close();
}
上述代码的功能是搜索起始字母范围从d到j的书籍标题。其中TermRangeQuery初始化方法中的两个Boolean对象参数表示是否包含搜索范围的起点或终点。
4.2在指定的数字范围内搜索:NumericRangeQuery
如果使用NumericField对象来索引域,那么你就能有效地使用NumericRangeQuery类在某个特定范围内搜索该域。下面的例子展示了搜索2006年5月到2006年9月出版的书籍
public void testInclusive() throws Exception {
Directory dir = TestUtil.getBookIndexDirectory();
IndexSearcher searcher = new IndexSearcher(dir);
NumericRangeQuery query = new NumericRangeQuery("pubmonth", 201605, 201609, true, true);
TopDocs matches = searcher.search(query, 10);
assertEqual(1, matches.totalHits);
searcher.close();
dir.close();
}
4.3组合查询:BooleanQuery类
通过使用BooleanQuery类可以将各种查询类型组合成复杂的查询方式,而BooleanQuery本身是一个Boolean子句的容器。
下面的例子展示了使用AND查找我们所关注的主题为search的最新书籍
pulic void testAnd() throws Exception {
TermQuery searchingBooks = new TermQuery(new Term("subject", "search"));
Query books2010 = NumericRangeQuery.newIntRange("pubmonth", 201001, 201012, true, true);
BooleanQuery searchingBooks2010 = new BooleanQuery();
searchingBooks2010.add(searchingBooks, BooleanClause.Occur.MUST);
searchingBooks2010.add(books2010, BooleanClause.Occur.MUST);
Directory dir = TestUtil.getBookIndexDirectory();
IndexSearcher searcher = new IndexSearcher(dir);
TopDocs matches = searcher.search(searchingBooks2010, 10);
assetTrue(TestUtil.hitsIncludeTitle(searcher, matches, "Lucene in Action, Second Edition"));
searcher.close();
dir.close();
}