当你查询Lucene索引时, 它将会返回一个包含有序的ScoreDoc对象数组的TopDocs对象.
在输入查询后, Lucene会为每个文档计算评分(用以表示相关性的数值)
IndexSearcher | 所有搜索都通过IndexSearcher进行, 它们会调用该类中重载的search方法 |
Query | 封装某种查询类型的具体子类. Query实例将被传递给IndexSearcher的search方法 |
QueryParser | 将用户输入的查询表达式处理成具体的Query对象 |
TopDocs | 保持由IndexSearcher.search()方法返回的具有较高评分的顶部文档 |
ScoreDoc | 提供对TopDocs中没跳搜索结果的访问接口 |
// 3. search
int hitsPerPage = 10;
IndexReader reader = DirectoryReader.open(index);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;
// 4. display results
System.out.println("Found " + hits.length + " hits.");
for(int i=0;i<hits.length;++i) {
int docId = hits[i].doc;
Document d = searcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("isbn") + "\t" + d.get("title"));
}
// reader can only be closed when there
// is no need to access the documents any more.
reader.close();
Lucene的搜索方法需要一个Query对象作为参数.
我们可以通过QueryParser类对查询表达式进行解析, 它把诸如 +A +B -C 或者 A OR B 这样的多个查询条件解析成一个Query类
Query q = new QueryParser("title", analyzer).parse(querystr);
如果表达式解析不成功, Lucene会抛出一个ParseException异常.
A | 默认域包含A的文档 |
A BA OR B | 默认域包含A和B中的一个或两个的文档 |
+A +BA AND B | 默认域中同时包含A和B的文档 |
title:A | title域中包含A项的文档 |
title:A -subject:Btitle:A AND NOT subject:B | title域中包含A 并且 subject域中不包含B的文档 |
(A OR B) AND C | 默认域中包含C 并且包含 A和B中的其中一个或者两个的文档 |
title: “hello world” | title域为hello world的文档 |
title: “hello world” ~5 | title域中hello和world之间距离小于5的文档 |
hello* | 包含由hello开头的文档, 包括hello本身 |
hello~ | 包含与单词hello相近的文档,例如Jello |
lastmodified:[1/1/2017 TO 12/31/2017] | lastmodified域在2017年1月1日和2017年12月31日之间的文档 |
用Lucene进行搜索操作异常简单, 首先建立一个IndexSearcher实例, 它负责打开索引, 然后改实例的search方法即可进行搜索操作.
程序返回TopDocs对象表示顶部搜索结果
要创建IndexSearcher类的实例, 首先需要一个用于存储索引的目录
上一个例子我们使用了内存目录, 这次使用一个真实的磁盘路径
Directory dir = FSDirectory.open(Paths.get(“c:\\myindex”));
通过这个dir可以方便的创建index reader/writer
IndexWriter indexWriter = new IndexWriter(dir, indexWriterConfig);
IndexReader reader = DirectoryReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);
这里需要注意的是, IndexReader完成了诸如打开所有索引文件和提供底层reader API 等复杂的工作, 而 IndexSearcher 则简单的多.
由于打开一个IndexReader需要较大的系统开销, 因此最好在所有搜索期间都重复使用同一个IndexReader实例, 只有在必要的时候才建议打开新的IndexReader
在创建IndexReader时,它会搜索已有的索引快照. 如果你需要搜索索引中的变更信息, 那么必须打开新的reader.
IndexReader.reopen方法是一个获取新IndexReader的有效手段, 重启的IndexReader能在耗费较少系统资源的情况下使用当前reader来获取索引中的所有变更信息.
一旦获取了IndexSearcher实例, 就可以通过调用它的search方法来进行搜索了
search方法会快速完成大量工作. 它会访问所有候选的搜索匹配文档, 并只返回符合每个查询条件的结果.
最后它会收集最靠前的几个搜索结果并返回给调用程序.
TopDocs search(Query query, int n) | 直接进行搜索. int n 参数表示返回的最高评分的文档数量 |
TopDocs search(Query query, Filter filter, int n) | 搜索受文档子集约束, 约束条件基于过滤策略 |
TopFieldDocs search(Query query, Filter filter, int n, Sort sort) | 搜索受文档子集约束, 约束条件基于过滤策略, 结果排序通过自定义的Sort完成 |
void search(Query query, Collector results) | 当使用自定义文档访问策略时使用, 或者不想以默认的前n个搜索结果培训策略收集结果时使用 |
void search(Query query, Filter filter, Collector results) | 同上. 区别在于结果文档只有在传入过滤策略时才能被接收 |
Topdocs.totalHits 属性会返回匹配文档数量. 在默认情况下, 匹配文档是按照评分降序排序的(DESC)
TopDocs.scoreDocs属性是一个数组, 它包含程序所要求的顶部匹配文档数量.
每个scoreDoc实例都有一个浮点类型的评分, 该评分是相关性评分, 该实例还包含一个整型数字用于标示文档ID, 能够用于检索文档中保存的域
ScoreDoc[] hits = collector.topDocs().scoreDocs;
for(int i=0;i<hits.length;++i) {
int docId = hits[i].doc;
Document d = searcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("filename") + "\t" + d.get("filepath"));
}
package lzlucene;
import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopScoreDocCollector;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
public class HelloLucene2 {
public static void main(String[] args) throws IOException, ParseException {
// 1. create the index
Directory dir = FSDirectory.open(Paths.get("c:\\myindex"));
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
IndexWriter indexWriter = new IndexWriter(dir, indexWriterConfig);
indexWriter.deleteAll();
File dFile = new File("C:/");
File[] files = dFile.listFiles();
for (File file : files) {
Document document = new Document();
document.add(new Field("filename", file.getName(), TextField.TYPE_STORED));
document.add(new Field("filepath", file.getAbsolutePath(), TextField.TYPE_STORED));
indexWriter.addDocument(document);
}
indexWriter.commit();
indexWriter.close();
// 2. query
String querystr = args.length > 0 ? args[0] : "out.txt";
Query q = new QueryParser("filename", analyzer).parse(querystr);
// 3. search
int hitsPerPage = 10;
IndexReader reader = DirectoryReader.open(dir);
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(hitsPerPage);
searcher.search(q, collector);
ScoreDoc[] hits = collector.topDocs().scoreDocs;
// 4. display results
System.out.println("Found " + hits.length + " hits.");
for(int i=0;i<hits.length;++i) {
int docId = hits[i].doc;
Document d = searcher.doc(docId);
System.out.println((i + 1) + ". " + d.get("filename") + "\t" + d.get("filepath"));
}
reader.close();
}
}
