【问题标题】:How to search Lucene.NET without indicating "top n" hits limit?如何在不指示“top n”命中限制的情况下搜索 Lucene.NET?
【发布时间】:2011-06-13 01:01:07
【问题描述】:

在 Lucene 中有几个 IndexSearcher.Search 方法的重载。其中有些需要“top n hits”参数,有些则不需要(这些已过时,将在 Lucene.NET 3.0 中删除)。

需要“top n”参数的那些实际上会导致整个可能结果范围的内存预分配。因此,当您甚至无法估计返回的结果数时,唯一的机会是传递一个随机的大数以确保返回所有查询结果。由于 LOH 碎片,这会导致严重的内存压力和泄漏。

有没有不通过“top n”参数的官方不过时的搜索方式?

提前谢谢各位。

【问题讨论】:

    标签: search memory lucene heap-memory fragmentation


    【解决方案1】:

    我使用 Lucene.NET 2.9.2 作为此答案的参考点。

    您可以构建一个自定义收集器,将其传递给搜索重载之一。

    using System;
    using System.Collections.Generic;
    using Lucene.Net.Index;
    using Lucene.Net.Search;
    
    public class AwesomeCollector : Collector {
        private readonly List<Int32> _docIds = new List<Int32>();
        private Scorer _scorer;
        private Int32 _docBase;
    
        public IEnumerable<Int32> DocumentIds {
            get { return _docIds; }
        }
    
        public override void SetScorer(Scorer scorer) {
            _scorer = scorer;
        }
    
        public override void Collect(Int32 doc) {
            var score = _scorer.Score();
            if (_lowerInclusiveScore <= score)
                _docIds.Add(_docBase + doc);
        }
    
        public override void SetNextReader(IndexReader reader, Int32 docBase) {
            _docBase = docBase;
        }
    
        public override bool AcceptsDocsOutOfOrder() {
            return true;
        }
    }
    

    【讨论】:

    • 感谢您的建议。实际上,我们一直在以几乎相同的方式使用 Collector,唯一的区别是使用 LinkedList 而不是 List 来防止内存在增长时重新分配。当不需要进行排序时,这种方法非常有用。没有接收 Collector 和 Sort 对象的 Search() 重载。在使用 Sort 时,我们强制 Lucene 使用默认的 TopHitsCollector,它以描述的方式预分配内存。也许使用自定义收集器是个好主意,它会在 Coolect 调用上进行自己的排序。你怎么看?
    • 我会将其更改为在列表中存储文档 ID 和排序值,并在收集到所有结果后进行排序。如果您有一个关键字字段作为排序字段,则可以使用 FieldCache,它将加载(并缓存)每个段的字段值。您必须使用内部读取器(在 SetNextReader 中传递给您的读取器)以使缓存正常工作。
    • 是的,我想这将是最好的方法,但使用字段缓存除外。这个东西在大索引中占用了大量内存,并且由于我们必须经常重新打开它,我不希望仅仅为了对几百行进行排序而将索引中的所有字段数据加载到内存中。所以看来你的建议基本上是对我问题的回答。谢谢西蒙 :)
    • 在 IndexReader 上调用 .Reopen 将重用已打开的段,因此,已打开的 SegmentReader。这意味着 FieldCache 将已经在内存中拥有这些项目,并且不需要访问磁盘来检索排序值。但它确实会消耗大量内存,是的。
    • 嗯。 Reopen() 将从用于创建原始阅读器的源返回一个新的 IndexReader(包括用于近实时更新的 IndexWriter)。如果没有发生更改,它将返回相同的实例,并重用任何未更改的段。请记住,您从 IndexReader.Open 获得的 IndexReader 实际上是一个由多个 SegmentReader 组成的 DirectoryReader。只要段仍然存在,SegmentReader 就会被重用(段只会创建和删除,永远不会更新)。
    猜你喜欢
    • 2016-09-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多