7

我正在使用 Lucene 4.2 并正在实现结果分页。

IndexSearcher.searchAfter提供了一种实现“下一页”功能的有效方法,但是实现“上一页”甚至“转到页面”功能的最佳方法是什么?没有IndexSearcher.searchBefore例子。

我正在考虑确定给定页面大小的总页数,并保留一个ScoreDoc[]数组来跟踪每个页面的“之后” ScoreDoc(该数组将在结果被分页时填充)。这将允许我使用“最接近的”ScoreDoc用于IndexSearcher.searchAfter(或在最坏的情况下为 null)。

这有意义吗?有更好的方法吗?

4

3 回答 3

14

我一直在使用 Lucene 4.8 并且一直在研究一个包含分页的 REST 接口。我的解决方案是使用 TopScoreDocCollector 并调用 topDocs(int startIndex, int numberOfhits) 方法。起始索引是通过将基于零的页码乘以命中数来计算的。

...
DirectoryReader reader = DirectoryReader.open(MMapDirectory.open( java.io.File(indexFile) );
IndexSearcher searcher = new IndexSearcher(reader);
TopScoreDocCollector collector = TopScoreDocCollector.create(MAX_RESULTS, true);  // MAX_RESULTS is just an int limiting the total number of hits 
int startIndex = (page -1) * hitsPerPage;  // our page is 1 based - so we need to convert to zero based
Query query = new QueryParser(Version.LUCENE_48, "All", analyzer).parse(searchQuery);
searcher.search(query, collector);
TopDocs hits = collector.topDocs(startIndex, hitsPerPage);
...

所以我的 REST 接口接受页码和每页点击数作为参数。所以前进或后退就像为页面提交具有适当值的新请求一样简单

于 2014-07-02T14:11:17.457 回答
6

我同意 Jaimie 解释的解决方案。但我想指出您必须注意的另一个方面,它有助于理解搜索引擎的一般机制。

使用 TopDocCollector,您可以定义在按分数或其他排序标准对结果进行排序之前,您希望收集多少匹配您的搜索查询的命中。

请参见以下示例:

collector = TopScoreDocCollector.create(9999, true);
searcher.search(parser.parse("Clone Warrior"), collector);
// get first page
topDocs = collector.topDocs(0, 10);
int resultSize=topDocs.scoreDocs.length; // 10 or less
int totalHits=topDocs.totalHits; // 9999 or less

我们在这里告诉 Lucene 最多收集 9999 个包含搜索短语“Clone Warrior”的文档。这意味着,如果索引包含超过 9999 个包含此搜索短语的文档,则收集器将在 9999 个命中填满后停止!

这意味着,您选择的 MAX_RESULTS 越大,您的搜索结果就越好。但这仅在您期望有大量点击时才有意义。另一方面,如果您搜索“luke skywalker”并且您预计只有一次命中,那么 MAX_RESULTS 也可以设置为 1。

因此,更改 MAX_RESULTS 会影响返回的 scoreDocs,因为将对收集的命中执行排序。实际上,将 MAX_RESULTS 设置为足够大的大小,以便人类用户不会争辩错过特定文档。这个概念与 SQL 数据库的行为完全相反,它总是考虑完整的数据池。

但是lucene也支持另一种机制。您可以不为收集器定义 MAX_RESULTS,而是定义您希望等待结果集的时间量。因此,例如,您可以定义始终希望在 300 毫秒后停止收集器。这是保护您的应用程序的性能问题的好方法。但是,如果要确保计算所有相关文档,则必须将 MAX_RESULTS 参数或最大等待时间设置为无限值。

于 2016-08-26T09:04:34.187 回答
2

我正在使用 lucene 8.2.0。我已经使用 indexSearcher.searchAfter() 实现了分页,如下所示。searchAfter() 将 ScoreDoc 作为第一个参数,所以我需要创建 ScoreDoc 的对象。要创建 ScoreDoc 对象,您需要维护之前 ScoreDoc 结果中的三件事 1.doc、2.score、3.shardIndex,这将有助于创建 ScoreDoc 对象

 ScoreDoc scoreDoc = new ScoreDoc(53, 2.4933066f,0);
 TopDocs hits3 =  indexSearcher.searchAfter(scoreDoc,query3,10);

我也使用了上面提到的答案,并且使用 TopScoreDocCollector 可以正常工作,但是 indexSearcher.searchAfter 的性能比 TopScoreDocCollector 方法好 3 到 4 倍。

于 2019-09-24T07:24:44.263 回答