我正在使用Lucene 4.2并且正在实施结果分页。
IndexSearcher.searchAfter
提供了实现“下一页”功能的有效方法,但实现“上一页”甚至“转到页面”功能的最佳方式是什么?例如,没有IndexSearcher.searchBefore
。
我正在考虑确定给定页面大小的页面总数,并保留一个ScoreDoc[]
数组来跟踪每个页面的“后”ScoreDoc
(当数据被分页时,将填充数组)。这样我就可以使用“最近的”ScoreDoc
在IndexSearcher.searchAfter
中使用(或者在最坏的情况下为null)。
这有意义吗?有更好的方法吗?
答案 0 :(得分:12)
我一直在使用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接口接受页码和每页点击次数作为参数。 因此,前进或后退就像提交具有适当页面值的新请求一样简单
答案 1 :(得分:5)
我同意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,或者定义您希望等待结果集的时间量。因此,例如,您可以定义始终要在300ms后停止收集器。这是保护应用程序性能问题的好方法。但是,如果您想确保计算所有相关文件,而不是将MAX_RESULTS的参数或最长等待时间设置为无穷值。
答案 2 :(得分:0)
我正在使用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倍。