zoukankan      html  css  js  c++  java
  • 利用Lucene.net搭建站内搜索(4)数据检索

    前面的文章,我们已经对要检索的数据创建了索引,现在要做的就是为用户提供全文搜索的功能。通过Lucene我们还可以简单而高效地对搜索结果进行访问。此文和大家简单的说说利用Lucene.net进行数据的搜索。

    当我们查询Lucene的一个索引时,Lucene会返回一个有序的Hits对象集合(collection)Lucene使用默认的评分方式对该集合内的对象按照其得分高低进行排序。对于一个给定的查询,Lucene为每个文档计算一个评分(即一个表示相关性的数值)。Hits本身不是实际的匹配文档集,只是指向这些匹配文档的引用(reference)。

    Lucene.net中处理检索的核心API:

       

    IndexSearcher

    搜索操作的入口。所有的搜索操作都是通过IndexSercher实例使用一个重载的search方法来实现

    Query (及其子类)

    具体的Query子类为每一种特定类型的查询进行了逻辑上的封装。Query实例被传递到IndexSeracherserach方法中

    QueryParser

    将用户输入的(并且可读的)查询表达式处理为一个具体的Query对象

    Hits

    提供对搜索结果的访问。Hits对象由IndexSearchersearch方法返回

    IndexSearcher
    IndexSercher简单易用。搜索操作是通过一个IndexSearcher的实例完成的。IndexSearcher有三个构造函数,目的是告诉IndexSearcher索引存放的路径。构造好IndexSearcher对象后,我们紧接着调用它的一个search方法进行搜索。Search方法提供了很多重载,这里主要方法有:

    IndexSearcher.search的函数声明

    使用场合

    Hits search(Query query)

    直接进行搜索,无需过滤

    Hits search(Query query,Filter filter)

    在过滤标准的约束下,把搜索限制在有效的文档子集之内

    void search(Query query,HitCollector results)

    只有当我们需要使用搜索返回的所有文档时才调用这个方法。一般来说,只有搜索结果中前几个文档才是我们所需的,所以使用此方法会降低程序的性能

    IndexSearcher实例只搜索在它被实例化时已存在的索引。如果制作索引和搜索并发进行,那么最新索引的文档对搜索来说是不可见的。为了能在搜索时找到这些新的文档,必须构造一个新的IndexSearcher对象。

    QueryParser
    QueryParser是Query的构造器。通过使用由自然语言表示的查询表达式,也可以创建某一个Query子类的对象。Lucene具有一个引人注目的特性,那就是我们可以通过QueryParser类对查询表达式进行解析。QueryParser的主要作用就是用来处理用户输入的查询条件。

    Query

    Lucene.net的查询操作最终要调用IndexSearcher中的search方法,在该方法中需要一个Query实例作为其参数。Query的子类能够通过其构造函数直接实例化。 

    如果想将搜索的文档限制在某一种类别的范围之内。根据搜索应用程序的用户界面,你可以通过日期采集器选择一个搜索的日期范围,也可以通过下拉菜单选择一个搜索的类别,或者还可以选择不受限制的搜索方式。这些功能都能够通过对QueryParser,BooleanQuery(组合查询),RangeQuery(在指定范围内搜索),TermQuery(通过项进行搜索),PhraseQuery(通过短语搜索),PrefixQuery (通过字符串进行搜索)类的整合来实现。

    Hits
    通过调用search(Query)方法,可以对返回的Hits对象进行处理。我们是通过Hits来访问搜索结果。
    Hits类中用于有效地对搜索结果进行访问的方法:

    Hits方法

      

    length()

    Hits对象集合中所包含的文档的数量

    doc(n)

    排名第nDocument实例

    id(n)

    排名第nDocument ID

    score(n)

    排名第n的标准分值(以得分最高的文档的评分为标准,将分值标准化),这个分值大于0,小于等于1


    Hits类对少数文档进行了缓存并且保留了最近使用过的文档。Lucene会自动检索出前100个文档并将其置入缓存。Lucene返回的Hits集合只向用户提供少数评分较高的文档,因为可能这些文档才是用户所需要的。doc(n)、id(n)和score(n)方法需要在文档还没有被放入缓存之前,就将其从索引中读取出来。因此我们建议你,若非真正需要显示或者访问这些文档,就不要调用这些方法。

    通过实例实现数据的检索:
    上篇我们已经为数据创建了索引,接着上篇的实例来实现检索的功能:

    Code
      private void search()
        {
            indexDirectory = Server.MapPath("index")+"\\"; //索引路径;
            wordPath = Server.MapPath("App_Data")+"\\"; //词库路径

            highanalyzer = new Lucene.Net.Analysis.Standard.StandardAnalyzer();
            searcher = new IndexSearcher(indexDirectory);

            Analyzer KTDanalyzer = new KTDictSegAnalyzer(wordPath);
            PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(KTDanalyzer);

            wrapper.AddAnalyzer("News_Title", KTDanalyzer);
            wrapper.AddAnalyzer("News_Body", KTDanalyzer);

            string[] fields = new string[] { "News_Title" };
            mfqp = new MultiFieldQueryParser(fields, wrapper);

            string nowq = reqs(this.Query);
            if (nowq.Length < 2)
            {
                return;
            }
            q = mfqp.Parse(reqs(this.Query));
            BooleanQuery m_BooleanQuery = new BooleanQuery();
            m_BooleanQuery.Add(q, BooleanClause.Occur.MUST);


            Sort sort = new Sort(new SortField("ID", true));
            Hits reshits = searcher.Search(m_BooleanQuery, sort);

            MultiFieldQueryParser parser = new MultiFieldQueryParser(new string[] { "News_Title", "News_Body" }, highanalyzer);
            highquery = parser.Parse(reqs(this.Query));

            total = reshits.Length();
            if (total > 0)
            { 
                ShowHits(reshits);
            }

            Repeater1.DataSource = Results;
            Repeater1.DataBind();
            DataBind();
        }

        public void ShowHits(Hits hits)
        {  
            this.Results.Columns.Add("title", typeof(string));
            this.Results.Columns.Add("sample", typeof(string));
            this.Results.Columns.Add("path", typeof(string));
            this.Results.Columns.Add("time", typeof(string));

            this.startAt = initStartAt();
            int resultsCount = smallerOf(total, this.maxResults + this.startAt);
            string qsid = "";

            for (int i = 0; i < resultsCount; i++)
            {
                Document doc = hits.Doc(i);
                qsid += doc.Get("ID") + ",";
            }
            qsid = qsid.Remove(qsid.Length - 1);
            DataTable dtdb = os.GetTable("select ID,News_Title,News_Body from WEBNEWS where ID in(" + qsid + ")", "WEBNEWS").Tables[0];

            for (int i = startAt; i < resultsCount; i++)
            {
                DataRow[] drs = dtdb.Select(" ID =" + hits.Doc(i).Get("ID"));
                Document doc = hits.Doc(i);

                string plainText, title;
                plainText = parseHtml(drs[0]["News_Body"].ToString());
                title = parseHtml(drs[0]["News_Title"].ToString());

                DataRow row = this.Results.NewRow();
                highlighter = new Highlighter(new SimpleHTMLFormatter("<font color=\"red\"><B>", "</B></font>"), new QueryScorer(highquery));

                Lucene.Net.Analysis.TokenStream tokenStream = highanalyzer.TokenStream("News_Body", new System.IO.StringReader(plainText));
                Lucene.Net.Analysis.TokenStream titletokenStream = highanalyzer.TokenStream("News_Title", new System.IO.StringReader(title));

                string result = highlighter.GetBestFragments(tokenStream, plainText, 0, "");
                string titleresult = highlighter.GetBestFragments(titletokenStream, title, 0, "");
                if (string.IsNullOrEmpty(titleresult))
                {
                    titleresult = title;
                }
                if (string.IsNullOrEmpty(result))
                {
                    if (plainText.Length > 100)
                    {
                        result = plainText.Substring(0, 100);
                    }
                    else
                        result = plainText;
                }

                if (result.Length < plainText.Length)
                    result = result + "";

                row["title"] = titleresult;
                row["path"] = doc.Get("News_Url");
                row["sample"] = result;
                row["time"] = doc.Get("News_Date");
                this.Results.Rows.Add(row);
            }
        }

    运行效果:
     

    小结:通过几篇文章和大家一起学习,复习了下Lucene.net的创建索引分词处理数据检查的内容,并实现了一个简单的站内搜索。当然Lucene.net的知识还有很多很多,如何追加索引,如何更好的分词,如何更高效的检索数据,都可以更深一步的研究。有什么学习心得还会和大家分享。
    最后提前祝大家双节快乐!!O(∩_∩)O

    实例代码:(注:只是个练习的小例子,里面可能还有很多Bug,仅供学习参考)
    /Files/gaoweipeng/LuceneSearch.rar




        文章作者:高维鹏(Brian)
        文章出处:http://www.cnblogs.com/gaoweipeng
        欢迎转载,转载时请注明出处。谢谢合作。

  • 相关阅读:
    老刘 Yii2 源码学习笔记之 Component 类
    zanphp 初探----安装篇
    php SQL 防注入的一些经验
    基于FPGA的CNN算法移植(五)算法优化方向汇总
    基于FPGA的CNN算法移植(四)地平线等公司的 ADAS 产品
    基于FPGA的CNN算法移植(三)软件算法架构分析
    基于FPGA的CNN算法移植(二)硬件架构
    基于FPGA的CNN算法移植(一)概述
    ubuntu下无法启动vivado SDK的解决办法(二)
    HDMI调试经验分享(二)—— 基于zc706的ADV7511的实现
  • 原文地址:https://www.cnblogs.com/zhwl/p/2360089.html
Copyright © 2011-2022 走看看