【问题标题】:Consistent Lucene.NET runtime exception on certain queries某些查询的一致 Lucene.NET 运行时异常
【发布时间】:2016-03-09 15:37:18
【问题描述】:

我正在使用 Lucene.NET 在我们的应用程序中进行全文搜索的概念验证。有些查询工作正常,有些似乎返回的结果与 Luke 工具返回的结果不匹配。更有问题的是,这个查询:

(Description:tasty) (Gtin:00018389732061)

总是产生这个异常:

“System.IndexOutOfRangeException”类型的未处理异常 在 Lucene.Net.Search.TermScorer.Score() 处发生在 Lucene.Net.dll 在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\TermScorer.cs:136 行 在 Lucene.Net.Search.BooleanScorer.BooleanScorerCollector.Collect(Int32 文档)在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\BooleanScorer.cs:第 88 行 在 Lucene.Net.Search.TermScorer.Score(收集器 c,Int32 结束,Int32 firstDocID) 在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\TermScorer.cs:第 80 行
在 Lucene.Net.Search.BooleanScorer.Score(收集器收集器,Int32 最大值,Int32 firstDocID)在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\BooleanScorer.cs:323 行 在 Lucene.Net.Search.BooleanScorer.Score(Collector collector) 中 d:\Lucene.Net\FullRepo\trunk\src\core\Search\BooleanScorer.cs:第 389 行 在 Lucene.Net.Search.IndexSearcher.Search(权重,过滤器 过滤器,收集器收集器)在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\IndexSearcher.cs:line 228 在 Lucene.Net.Search.IndexSearcher.Search(权重,过滤器 过滤器,Int32 nDocs)在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\IndexSearcher.cs:188 行 在 Lucene.Net.Search.Searcher.Search(Query query, Filter filter, Int32 n) 在 d:\Lucene.Net\FullRepo\trunk\src\core\Search\Searcher.cs:line 108 在 Lucene.Net.Search.Searcher.Search(Query query, Int32 n) 中 d:\Lucene.Net\FullRepo\trunk\src\core\Search\Searcher.cs:118行
在...

如果我改用这个查询:

(Description:tasty) (Gtin:000)

我得到了结果。是什么导致顶部查询中的异常? FWIW,这里是相关代码sn-p:

protected virtual IList<Document> GetDocuments(BooleanQuery query, DirectoryInfo indexLocation, string defaultField)
        {
            var docs = new List<Document>();

            using (var dir = new MMapDirectory(indexLocation))
            {
                using (var searcher = new IndexSearcher(dir))
                {                        
                    var queryParser = new QueryParser(Constants.LuceneVersion, defaultField, new StandardAnalyzer(Constants.LuceneVersion));
                    TopDocs result = searcher.Search(query, Constants.MaxHits);

                    if (result == null) return docs;

                    foreach (var scoredoc in result.ScoreDocs.OrderByDescending(d => d.Score))
                    {
                        docs.Add(searcher.Doc(scoredoc.Doc));
                    }
                    return docs;
                }
            }
        }

基于下面的 cmets,这是我当前未编辑但仍然无法工作的代码。

protected virtual IList<Document> GetDocuments(BooleanQuery query, DirectoryInfo indexLocation, string defaultField)
        {
            var docs = new List<Document>();

            using (var dir = new MMapDirectory(indexLocation))
            {
                using (var searcher = new IndexSearcher(dir))
                {
                    using (var analyzer = new StandardAnalyzer(Constants.LuceneVersion))
                    {
                        var queryParser = new QueryParser(Constants.LuceneVersion, defaultField, analyzer);
                        var collector = TopScoreDocCollector.Create(Constants.MaxHits, true);
                        var parsed = queryParser.Parse(query.ToString());
                        searcher.Search(parsed, collector);

                        var docsresult = new List<string>();
                        var matches = collector.TopDocs().ScoreDocs;
                        foreach (var scoredoc in matches.OrderByDescending(d => d.Score))
                        {
                            docs.Add(searcher.Doc(scoredoc.Doc));
                        }
                        return docs;
                    }
                }
            }
        }

【问题讨论】:

  • 此外,这个查询:+(Description:tasty) +Gtin:000* 在我的 Lucene.NET impl 上没有返回任何命中,而 Luke(正确地)返回 11 个匹配的文档。
  • “Gtin”索引为字符串还是数字字段?
  • Luke 通常会返回“不同”的结果,因为分析器通常与字段的索引方式不同
  • 您的示例实际上并未解析查询。它使用传入的 BooleanQuery。
  • 任何多部分查询最终都会作为布尔查询。我可以假设您所指的查询是“查询”参数的“ToString()”吗?这个“流利的查询生成器”是什么?我已经使用 Lucene.net 很多年了。除了简单之外,我从来没有与 3rd 方建设者有过很好的经验。我总是最终生成一个查询字符串并解析它,或者很少直接构建查询对象图。除非你能提供一个更完整的例子,有点像我下面的答案,否则很难走得更远。

标签: c# .net lucene full-text-search lucene.net


【解决方案1】:

严格来说不是一个答案,因为它“在我的机器上工作”。发布作为答案,以便我可以共享“有效”的单元测试代码。希望 OP 可以展示他们的版本有什么不同。

这个版本假定“Gtin”字段是一个字符串字段并且没有被分析(因为它似乎是一个代码)。

[TestClass]
public class UnitTest4
{
    [TestMethod]
    public void TestLucene()
    {
        var writer = CreateIndex();
        Add(writer, "tasty", "00018389732061");
        writer.Flush(true, true, true);

        var searcher = new IndexSearcher(writer.GetReader());
        Test(searcher, "(Description:tasty) (Gtin:00018389732061)");
        Test(searcher, "Description:tasty Gtin:00018389732061");
        Test(searcher, "+Description:tasty +Gtin:00018389732061");
        Test(searcher, "+Description:tasty +Gtin:000*");

        writer.Dispose();
    }

    private void Test(IndexSearcher searcher, string query)
    {
        var result = Search(searcher, query);
        Console.WriteLine(string.Join(", ", result));
        Assert.AreEqual(1, result.Count);
        Assert.AreEqual("00018389732061", result[0]);
    }

    private List<string> Search(IndexSearcher searcher, string expr)
    {
        using (var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30))
        {
            var queryParser = new QueryParser(Lucene.Net.Util.Version.LUCENE_30, "Description", analyzer);
            var collector = TopScoreDocCollector.Create(1000, true);
            var query = queryParser.Parse(expr);
            searcher.Search(query, collector);

            var result = new List<string>();
            var matches = collector.TopDocs().ScoreDocs;
            foreach (var item in matches)
            {
                var id = item.Doc;
                var doc = searcher.Doc(id);
                result.Add(doc.GetField("Gtin").StringValue);
            }
            return result;
        }
    }

    IndexWriter CreateIndex()
    {
        var directory = new RAMDirectory();

        var analyzer = new StandardAnalyzer(Lucene.Net.Util.Version.LUCENE_30);
        var writer = new IndexWriter(directory, analyzer, new IndexWriter.MaxFieldLength(1000));

        return writer;
    }
    void Add(IndexWriter writer, string desc, string id)
    {
        var document = new Document();
        document.Add(new Field("Description", desc, Field.Store.YES, Field.Index.ANALYZED));
        document.Add(new Field("Gtin", id, Field.Store.YES, Field.Index.NOT_ANALYZED));

        writer.AddDocument(document);
    }
}

【讨论】:

  • 我显然对 GTIN 感兴趣。你说的对。那是一个代码字段。没有意识到分析设置应该设置为false。现在就试试...
  • 好的,我尝试了你的一些细微的变化。我将 GTIN 转换为不被分析。我还将查询转换为文本。没有喜悦。同样的错误从堆栈中冒出来。请注意,我有一个物理索引,而不是 in-mem,所以代码有点不同...ack,代码太长,我会尝试编辑我的原始帖子
  • 任何包含 code/key/id 类型事物的字段都应该是 NOT_ANALYZED。示例代码中使用的目录类型不会对行为产生任何影响。但是,在 lucene.net 中,我认为 MMAPDirectory 实际上没有任何好处(甚至可能存在错误),您可以尝试使用简单的 FSDirectory。 Windows 在缓存文件内容方面做得很好。由于段文件只写入一次然后只读,因此无论如何您都会获得非常好的性能特征。
  • 显然就是这样!看起来 FSDirectory 是一个基类,所以我使用了派生类之一 - SimpleFSDirectory。神奇的是它开始工作了。
  • 哈!这将教我做出假设!顺便说一句,您不应该直接创建 SimpleFSDirectory。使用“FSDirectory.Open”(静态方法)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-03
相关资源
最近更新 更多