有了上一篇建立的索引,就可以进行检索了。
数据库查询使用SQL,lucene检索使用Query。
lucene提供了一个IndexSearcher类,检索的功能通过这个类完成,其构造方法需要一个IndexReader对象。IndexReader用于读取索引库Directory。
IndexSearcher有许多重构的方法,其中返回值为TopDocs类型的为最简单的。本文使用这个方法进行演示。TopDocs保存检索结果,其中的scoreDocs属性保存了记录的docId及评分,根据docId就可以取得对应的记录。
上一篇中已经知道初始化Directory对象需要索引库的路径,我们提供一个Searcher类,简化索引的操作。
伪代码如下:
public class Searcher {
/**
* 检索
*
* @param indexDir
* 索引存放目录
* @param query
* 检索条件
* @param n
* 返回结果数量
* @return
* @throws Exception
*/
public List<Document> search(String indexDir, Query query, int n) throws Exception {
创建Directory对象;
创建IndexReader对象;
创建IndexSearcher对象;
使用IndexSearcher对象进行检索;
}
}
实现代码如下:
package cn.lym.lucene.quickstart.search;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
/**
* 提供检索的类
*
* @author liuyimin
*
*/
public class Searcher {
/**
* logger
*/
private static final Logger logger = LogManager.getLogger(Searcher.class);
/**
* 检索
*
* @param indexDir
* 索引存放目录
* @param query
* 检索条件
* @param n
* 返回结果数量
* @return
* @throws Exception
*/
public List<Document> search(String indexDir, Query query, int n) throws Exception {
if (logger.isDebugEnabled()) {
logger.debug("Search " + indexDir + " for " + n + " documents, with query: " + query);
}
Directory directory = FSDirectory.open(new File(indexDir));
IndexReader reader = DirectoryReader.open(directory);
IndexSearcher searcher = new IndexSearcher(reader);
TopDocs topDocs = searcher.search(query, n);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
if (logger.isDebugEnabled()) {
logger.debug("Totally " + scoreDocs.length + " documents hit.");
}
List<Document> documents = new ArrayList<>(scoreDocs.length);
for (ScoreDoc scoreDoc : scoreDocs) {
documents.add(searcher.doc(scoreDoc.doc));
}
return documents;
}
}
按照上一篇的需求,编写单元测试。
package cn.lym.lucene.quickstart.search;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import java.util.Date;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.Term;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
import org.junit.Before;
import org.junit.Test;
public class SearcherTest {
/**
* 索引存放目录
*/
private static final String indexDir = "E:\\Documents\\lucene-quickstart\\";
private Searcher searcher;
@Before
public void init() {
this.searcher = new Searcher();
}
/**
* 按文件名搜索文件
*/
@Test
public void testSearchWithFileName() throws Exception {
// 搜索文件名为jdk-8u60-windows-x64.exe
Query query = new TermQuery(new Term("filename", "jdk-8u60-windows-x64.exe"));
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertEquals(1, documents.size());
}
/**
* 按文件类型搜索文件
*/
@Test
public void testSearchWithFileType() throws Exception {
// 搜索文件类型为exe的文件
Query query = new TermQuery(new Term("type", "exe"));
int n = Integer.MAX_VALUE;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
assertTrue(documents.size() > 0);
}
/**
* 按文件类型搜索文件
*/
@Test
public void testSearchWithFileType2() throws Exception {
// 搜索文件类型为exe的文件
Query query = new TermQuery(new Term("type", "txt"));
int n = Integer.MAX_VALUE;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document.get("pathname"));
}
assertTrue(documents.size() > 0);
}
/**
* 按文件大小搜索文件
*/
@Test
public void testSearchWithFileSize() throws Exception {
// 搜索文件大小为195,200,088字节的文件(jdk-8u60-windows-x64.exe)
long size = 195_200_088L;
Query query = NumericRangeQuery.newLongRange("size", size, size, true, true);
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertEquals(1, documents.size());
}
/**
* 按文件大小搜索文件
*/
@Test
public void testSearchWithFileSize2() throws Exception {
// 搜索文件大小在1024~2048字节之间的文件
Long min = 1024L;
Long max = 2048L;
Query query = NumericRangeQuery.newLongRange("size", min, max, true, true);
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertTrue(documents.size() > 0);
}
/**
* 按文件大小搜索文件
*/
@Test
public void testSearchWithFileSize3() throws Exception {
// 搜索文件大小小于1024字节的文件
Long min = null;
Long max = 1024L;
Query query = NumericRangeQuery.newLongRange("size", min, max, true, true);
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertTrue(documents.size() > 0);
}
/**
* 按文件大小搜索文件
*/
@Test
public void testSearchWithFileSize4() throws Exception {
// 搜索文件大小大于1024 * 1024 * 1024字节的文件
Long min = 1024 * 1024 * 1024L;
Long max = null;
Query query = NumericRangeQuery.newLongRange("size", min, max, true, true);
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertTrue(documents.size() > 0);
}
/**
* 按文件修改日期搜索文件
*/
@Test
public void testSearchWithModifiedTime() throws Exception {
// 搜索最近一周修改的文件
Long max = new Date().getTime();
Long min = max - 7 * 24 * 3600 * 1000L;
Query query = NumericRangeQuery.newLongRange("lastmodified", min, max, true, true);
int n = 10;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document);
}
assertTrue(documents.size() > 0);
}
/**
* 按文件内容搜索文件
*/
@Test
public void testSearchWithContent() throws Exception {
// 搜索内容中包含success的文件
Query query = new TermQuery(new Term("content", "success"));
int n = Integer.MAX_VALUE;
List<Document> documents = this.searcher.search(indexDir, query, n);
System.out.println(documents.size() + " documents hit.");
for (Document document : documents) {
System.out.println(document.get("pathname"));
}
assertTrue(documents.size() > 0);
}
}
需要说明的几点:
关于Query。Query有几种子类:
等值查询:TermQuery。文件名、文件路径、文件类型、文件内容都属于这种。
范围查询:NumericRangeQuery。文件大小、修改时间属于这种。NumericRangeQuery通过静态工厂方法创建,几个参数分别为:字段名,最小值,最大值,是否包含最小值,是否包含最大值。最大值、最小值某个为null时,表示无穷大、无穷小。
关于检索结果Document对象。我们可以从Document对象中,通过get(String name)方法,获得建立索引时,存储选项设置为存储的字段。当试图获得非存储字段的值时,返回null。
本文代码可以从 https://git.oschina.net/coding4j/lucene-quickstart 获取。
来源:oschina
链接:https://my.oschina.net/u/1466185/blog/501918