zoukankan      html  css  js  c++  java
  • jieba.NET与Lucene.Net的集成

    首先声明:我对Lucene.Net并不熟悉,但搜索确实是分词的一个重要应用,所以这里还是尝试将两者集成起来,也许对你有一参考。

    看到了两个中文分词与Lucene.Net的集成项目:Lucene.Net.Analysis.PanGuLucene.Net.Analysis.MMSeg,参考其中的代码实现了最简单的集成:jiebaForLuceneNet。下面给出简单的介绍。

    1、JiebaTokenizer

    主要的集成点是自定义一个Tokenizer的子类,此时必须要实现它的抽象方法IncrementToken,该方法用于对文本流中的文本生成的token进行遍历,这正是分词组件发挥作用的地方。

     
    public override bool IncrementToken()
    {
        ClearAttributes();
        position++;
        if (position < tokens.Count)
        {
            var token = tokens[position];
            termAtt.SetTermBuffer(token.Word);
            offsetAtt.SetOffset(token.StartIndex, token.EndIndex);
            typeAtt.Type = "Jieba";
            return true;
        }
    
        End();
        return false;
    }
     

    termAtt和offsetAtt所在的两行代码需要用到每一个token的词本身、起始索引和终止索引,而这三个值恰好是JiebaSegmenter.Tokenize方法所实现的,所以只要在初始化JiebaTokenizer时使用:

    tokens = segmenter.Tokenize(text, TokenizerMode.Search).ToList();

    就可以得到所有分词所得的token,另外TokenizerMode.Search参数使得Tokenize方法的结果中包含更全面的分词结果,比如“语言学家”会得到四个token,即“[语言, (0, 2)], [学家, (2, 4)], [语言学, (0, 3)], [语言学家, (0, 4)]”,这在创建索引和搜索时都很有帮助。

    2、JiebaAnalyzer

    Tokenizer类实现分词,而添加索引和搜索需要的是Analyzer,JiebaAnalyzer只要调用JiebaTokenizer即可。

     
    public override TokenStream TokenStream(string fieldName, TextReader reader)
    {
        var seg = new JiebaSegmenter();
        TokenStream result = new JiebaTokenizer(seg, reader);
        // This filter is necessary, because the parser converts the queries to lower case.
        result = new LowerCaseFilter(result);
        result = new StopFilter(true, result, StopWords);
        return result;
    }
     

    除了JiebaTokenizer,JiebaAnalyzer还会用到LowerCaseFilterStopFilter。前者可将索引和搜索的内容正则化,忽略大小写,后者则过滤掉停用词。这里使用的停用词列表合并了NLTK的英文停用词和哈工大的中文停用词。

    3、创建索引和搜索

    创建索引时,IndexWriter要使用JiebaAnalyzer的实例:

     
    var analyzer = new JiebaAnalyzer();
    
    using (var writer = new IndexWriter(Directory, analyzer, IndexWriter.MaxFieldLength.UNLIMITED))
    {
        // replaces older entry if any
        foreach (var sd in data)
        {
            AddToLuceneIndex(sd, writer);
        }
    
        analyzer.Close();
    }
     

    搜索的时候,先将用户的输入分词:

     
    private static string GetKeyWordsSplitBySpace(string keywords, JiebaTokenizer tokenizer)
    {
        var result = new StringBuilder();
    
        var words = tokenizer.Tokenize(keywords);
    
        foreach (var word in words)
        {
            if (string.IsNullOrWhiteSpace(word.Word))
            {
                continue;
            }
    
            result.AppendFormat("{0} ", word.Word);
        }
    
        return result.ToString().Trim();
    }
     

    比如如果用户输入的是“语言学家”,那么该函数的返回值是“语言 学家 语言学 语言学家”,为后面的搜索做好准备(另外,我们还可以为每个词加上一个*,这样只要部分匹配就可以搜到结果)。最后的搜索实现是:

     
    private static IEnumerable<News> SearchQuery(string searchQuery, string searchField = "")
    {
        if (string.IsNullOrEmpty(searchQuery.Replace("*", "").Replace("?", "")))
        {
            return new List<News>();
        }
    
        using (var searcher = new IndexSearcher(Directory, false))
        {
            var hitsLimit = 1000;
            //var analyzer = new StandardAnalyzer(Version.LUCENE_30);
            var analyzer = GetAnalyzer();
    
            if (!string.IsNullOrEmpty(searchField))
            {
                var parser = new QueryParser(Version.LUCENE_30, searchField, analyzer);
                var query = ParseQuery(searchQuery, parser);
                var hits = searcher.Search(query, hitsLimit).ScoreDocs;
                var results = MapLuceneToDataList(hits, searcher);
    
                analyzer.Dispose();
                return results;
            }
            else
            {
                var parser = new MultiFieldQueryParser(Version.LUCENE_30, new[] { "Id", "Title", "Content" }, analyzer);
                var query = ParseQuery(searchQuery, parser);
                var hits = searcher.Search(query, null, hitsLimit, Sort.RELEVANCE).ScoreDocs;
                var results = MapLuceneToDataList(hits, searcher);
    
                analyzer.Close();
                return results;
            }
        }
    }
     

    这里的searchField参数可以指定特定字段进行搜索,如果为空,则对所有字段进行搜索。至此实现了最基本的集成。

    JiebaTokenizer、JiebaAnalyzer的实现和示例代码都可在jiebaForLuceneNet找到。

    4、Luke.Net

    Luke.Net可以查看Lucene.Net生成的索引内容,这在开发和调试Lucene的时候会特别有帮助。

    原网址: https://www.cnblogs.com/anderslly/p/jiebanet-lucenenet.html

  • 相关阅读:
    UVa 12174 (滑动窗口) Shuffle
    UVa 1607 (二分) Gates
    CodeForces ZeptoLab Code Rush 2015
    HDU 1525 (博弈) Euclid's Game
    HDU 2147 (博弈) kiki's game
    UVa 11093 Just Finish it up
    UVa 10954 (Huffman 优先队列) Add All
    CodeForces Round #298 Div.2
    UVa 12627 (递归 计数 找规律) Erratic Expansion
    UVa 714 (二分) Copying Books
  • 原文地址:https://www.cnblogs.com/xubao/p/10684964.html
Copyright © 2011-2022 走看看