【问题标题】:Lucene.Net (4.8) AutoComplete / AutoSuggestionLucene.Net (4.8) 自动完成/自动建议
【发布时间】:2020-02-26 15:53:23
【问题描述】:

我想使用 Lucene.Net 4.8 实现可搜索索引,为用户提供单个单词和短语的建议/自动完成。

索引创建成功;这些建议是我停滞不前的地方。

4.8 版似乎引入了大量的重大更改,但我发现没有可用的示例有效。

我的立场

供参考,LuceneVersion 是这样的:

private readonly LuceneVersion LuceneVersion = LuceneVersion.LUCENE_48;

解决方案 1

I've tried this,但无法通过reader.Terms

    public void TryAutoComplete()
    {
        var analyzer = new EnglishAnalyzer(LuceneVersion);
        var config = new IndexWriterConfig(LuceneVersion, analyzer);
        RAMDirectory dir = new RAMDirectory();
        using (IndexWriter iw = new IndexWriter(dir, config))
        {
            Document d = new Document();
            TextField f = new TextField("text","",Field.Store.YES);
            d.Add(f);
            f.SetStringValue("abc");
            iw.AddDocument(d);
            f.SetStringValue("colorado");
            iw.AddDocument(d);
            f.SetStringValue("coloring book");
            iw.AddDocument(d);
            iw.Commit();
            using (IndexReader reader = iw.GetReader(false))
            {
                TermEnum terms = reader.Terms(new Term("text", "co"));
                int maxSuggestsCpt = 0;
                // will print:
                // colorado
                // coloring book
                do
                {
                    Console.WriteLine(terms.Term.Text);
                    maxSuggestsCpt++;
                    if (maxSuggestsCpt >= 5)
                        break;
                }
                while (terms.Next() && terms.Term.Text.StartsWith("co"));
            }
        }
    }

reader.Termsno longer exists。作为 Lucene 的新手,目前还不清楚如何重构它。

解决方案 2

尝试this,报错:

    public void TryAutoComplete2()
    {
        using(var analyzer = new EnglishAnalyzer(LuceneVersion))
        {
            IndexWriterConfig config = new IndexWriterConfig(LuceneVersion, analyzer);
            RAMDirectory dir = new RAMDirectory();
            using(var iw = new IndexWriter(dir,config))
            {
                Document d = new Document()
                {
                    new TextField("text", "this is a document with a some words",Field.Store.YES),
                    new Int32Field("id", 42, Field.Store.YES)
                };

                iw.AddDocument(d);
                iw.Commit();

                using (IndexReader reader = iw.GetReader(false))
                using (SpellChecker speller = new SpellChecker(new RAMDirectory()))
                {
                    //ERROR HERE!!!
                    speller.IndexDictionary(new LuceneDictionary(reader, "text"), config, false);
                    string[] suggestions = speller.SuggestSimilar("dcument", 5);
                    IndexSearcher searcher = new IndexSearcher(reader);
                    foreach (string suggestion in suggestions)
                    {
                        TopDocs docs = searcher.Search(new TermQuery(new Term("text", suggestion)), null, Int32.MaxValue);
                        foreach (var doc in docs.ScoreDocs)
                        {
                            System.Diagnostics.Debug.WriteLine(searcher.Doc(doc.Doc).Get("id"));
                        }
                    }
                }
            }
        }
    }

调试时,speller.IndexDictionary(new LuceneDictionary(reader, "text"), config, false); 抛出了The object cannot be set twice! 错误,我无法解释。

欢迎提出任何想法。

澄清

我想返回给定输入的建议术语列表,而不是文档或其完整内容。

例如,如果文档包含“您好,我的名字是 Clark。我来自亚特兰大”,并且我提交了“Atl”,那么“Atlanta”应该作为建议返回。

【问题讨论】:

    标签: c# lucene.net


    【解决方案1】:

    如果我对您的理解正确,您的索引设计可能有点过于复杂。如果您的目标是将 Lucene 用于自动完成,您需要创建一个您认为 complete 的术语的索引。然后只需使用 PrefixQuery 使用部分单词或短语来查询索引。

    using Lucene.Net.Analysis;
    using Lucene.Net.Analysis.En;
    using Lucene.Net.Documents;
    using Lucene.Net.Index;
    using Lucene.Net.Search;
    using Lucene.Net.Store;
    using Lucene.Net.Util;
    using System;
    using System.Linq;
    
    namespace LuceneDemoApp
    {
        class LuceneAutoCompleteIndex : IDisposable
        {
            const LuceneVersion Version = LuceneVersion.LUCENE_48;
            RAMDirectory Directory;
            Analyzer Analyzer;
            IndexWriterConfig WriterConfig;
    
            private void IndexDoc(IndexWriter writer, string term)
            {
                Document doc = new Document();
                doc.Add(new StringField(FieldName, term, Field.Store.YES));
                writer.AddDocument(doc);
            }
    
            public LuceneAutoCompleteIndex(string fieldName, int maxResults)
            {
                FieldName = fieldName;
                MaxResults = maxResults;
                Directory = new RAMDirectory();
                Analyzer = new EnglishAnalyzer(Version);
                WriterConfig = new IndexWriterConfig(Version, Analyzer);
                WriterConfig.OpenMode = OpenMode.CREATE_OR_APPEND;
            }
    
            public string FieldName { get; }
            public int MaxResults { get; set; }
    
            public void Add(string term)
            {
                using (var writer = new IndexWriter(Directory, WriterConfig))
                {
                    IndexDoc(writer, term);
                }
            }
    
            public void AddRange(string[] terms)
            {
                using (var writer = new IndexWriter(Directory, WriterConfig))
                {
                    foreach (string term in terms)
                    {
                        IndexDoc(writer, term);
                    }
                }
            }
    
            public string[] WhereStartsWith(string term)
            {
                using (var reader = DirectoryReader.Open(Directory))
                {
                    IndexSearcher searcher = new IndexSearcher(reader);
                    var query = new PrefixQuery(new Term(FieldName, term));
                    TopDocs foundDocs = searcher.Search(query, MaxResults);
                    var matches = foundDocs.ScoreDocs
                        .Select(scoreDoc => searcher.Doc(scoreDoc.Doc).Get(FieldName))
                        .ToArray();
    
                    return matches;
                }
            }
    
            public void Dispose()
            {
                Directory.Dispose();
                Analyzer.Dispose();
            }
        }
    }
    

    运行这个:

    var indexValues = new string[] { "apple fruit", "appricot", "ape", "avacado", "banana", "pear" };
    var index = new LuceneAutoCompleteIndex("fn", 10);
    index.AddRange(indexValues);
    
    var matches = index.WhereStartsWith("app");
    foreach (var match in matches)
    {
        Console.WriteLine(match);
    }
    

    你明白了:

    apple fruit
    appricot
    

    【讨论】:

    • 我不是在寻找文档本身或它们的内容,而是在以输入字符串开头的索引文档中标记 in。使用您的示例,如果我提交“hap”,我会得到“happy”,因为“happy”将是一个令牌。
    • 这行得通。感谢帮助。现在的问题是让我的基于文档的长建议对人类有用......
    • 太棒了,很高兴这对你有用。您可能已经知道这一点,但您可以将多个字段添加到单个文档中。一个字段可以是完整的文档,另一个可以用作显示名称或其他类似的标签。您可以基于一个字段进行搜索并显示另一个字段。
    • 是的,我已经失望了;)。而是我得到的建议是段落。前任。像“嗨,我的名字是汤姆·史密斯。这是关于我的生活的一段很长的段落”这样的身体,在某些情况下,搜索汤姆可以让我看到整个段落。反正也不是什么大问题,更多的是解析结果。当然,欢迎提出建议。
    • 看起来这会有所帮助:stackoverflow.com/questions/34529261/…
    猜你喜欢
    • 1970-01-01
    • 2011-06-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    • 1970-01-01
    • 2012-07-31
    相关资源
    最近更新 更多