zoukankan      html  css  js  c++  java
  • DotLucene搜索引擎Demo

    DotLucene搜索引擎Demo之:创 建索引

    DotLucene的官方网站这样介绍:DotLucene是一个专门为NET设计的功能强大的搜索引擎!他上面也有一个在线demo,搜索 3.5GB的文本数据所花费的时间在0.1秒左右!大家可以点这里测试。我也记得在我的一个网站99 收藏夹(注1)里面有个在线帮助,他是用StreamReader来读文本数据的,其他的是读数据库,我发现不管是读数据库还是一xml的形式读xml文 档,不管你的数据库如何优化,也不管你的机器培植如何之高,读的速度与读文本数据的速度是不可比的,大家也可到http://www.99scj.com测试下。点在线帮助,一闪就出来了。 

    本文是按照DotLucene官方网站的一个demo 写的,本同点在于, 
    1,本文的demo采用了DotLucene最新稳定版1.4.3 
    2,开发环境是vs2005。 
    3,demo被我划成了两个部分,一个部分是一个console程序,就是本文所讲的,主要就是怎么样创建索引,另 
          一个部分是个web程序,关键说的是搜索这个部分所建立的索引。 
    4,源代码将在下个部分提供下载,因为这两个部分同属一个解决方案。 
    好了,我们现在开始进入怎么用DotLucene来创建索引了。 
    什么是索引呢?我也不太明白,我是这样理解的,索引就是用来加快查询数据的速度的,比如我们小时候读书的时候课本前面有那个第一课:什么什么的。。。。。。第几页,这应该就是索引吧。用DotLucene创建索引也就是说把某些文件内容编入某个目录下的索引。
    首先运行vs2005,选择文件
    --新建项目,在弹出的对话框选:其他项目类型里的Visual Studio解决方案,选右边的空白解决方案,输入名字:SearchDemo,位置选D:\确定。
    再在解决方案SearchDemo右键选添加
    ---再选---新 建解决方案文件夹,输入文件夹名字为Indexer.我再找到d:\SearchDemo,再这个文件夹里面新建个目录,叫wwwroot,大家就知道这 个目录是放weB 程序的,我们在iis管理器里面新建设个虚拟目录,指向d:\SearchDemo\wwwroot目录,名字叫 SearchDemo.
    我们再在vs的解决方案SearchDemo单季右键选添加
    --新建解决方案文件夹,输入文件夹名字为web,其实这两个文件夹名都是vs虚拟的,其实并不存在。我们然后在第一个项目Indexer上右键选:添加==新建项目,再弹出的面板左边选v c#--windows--右边选控制台应用程序,输入名字Indexer,确定,这个时候vs会在d:\searchdemo目录下添加个Indexer目录,然后我们再在新添加的名为web项目上右建--添加---现有网站---选择我们刚刚建立的SearchDemo就是。
    现 在我们建设好了两个项目,一个console控制台项目和一个SearchDemo的web项目,我们这部分只讲第一个项目怎么样建立索引,要建立索引, 我们首先必须明白索引应该建立在什么地方?为了方便我把索引建立在D:\SearchDemo\wwwroot目录下新建一个index目录下;还有我们 必须明白哪些文件将被编入索引?也为了方便,我把要被编入索引的文件放在d:\SearchDemo\wwwroot目录下新建个documents目录 下,也就是说documents目录下的所有文件都将被我编入索引。因为我们这个demo 演示的是搜索DotLucene的帮助文档,文艺我们把所有下 载来的帮助文档文件全部拷入d:\SearchDemo\wwwroot\documents目录下。同时我们还必须赋予index目录写的权利。
    我们现在给Indexer控制台项目添加引用Lucene.Net.dll
    我们现在在Indexer控制台项目里添加个类:IntrnetIndexer.cs;
    先说明下doc.Add(Field.UnStored(
    "text", ParseHtml(html)));
                doc.Add(Field.Keyword(
    "path", relativePath));
                doc.Add(Field.Text(
    "title", GetTitle(html)));
    索引是由Docuemnt对象组成,而Docuemnt对象又是由字段对象组成.
    Field.UnStored方法在其官方网站上的说明是:Constructs a String
    -valued Field that is tokenized and indexed, but that is not stored in the index. Term vector will not be stored for this Field.搜价110的Eunge帮忙翻译下成这样:构造一个String类型的字段,它将被分词和索引,但是它不会被存储在索引中。关于这个字段的词向量不会被存储,我一直都未能够理解关于这个字段的词向量不会被存储的含义,汗。
    代码如下:

    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.IO;
    using System.Text.RegularExpressions;
    using Lucene.Net.Analysis.Standard;
    using Lucene.Net.Documents;
    using Lucene.Net.Index;


    namespace Indexer
    {
        
    public class IntranetIndexer
        
    {
            
    //索引写入器
            private IndexWriter writer;
            
    //要写入索引的文件的根目录
            private string docRootDirectory;
            
    //要匹配的文件格式
            private string pattern;
            
    /// <summary>
            
    /// 初始化一个索引写入器writer,directory为创建索引的目录,true代表如果不存在索引文件将重新创建索引文件,如果已经存在索引文件将覆写索引文件,如果为true将代表打开已经存在的索引文件
            
    /// </summary>
            
    /// <param name="directory">传入的要创建索引的目录,注意是字符串值,如果目录不存在,他将会被自动创建</param>

            public IntranetIndexer(string directory)
            
    {
                writer 
    = new IndexWriter(directory,new StandardAnalyzer(),true);
                writer.SetUseCompoundFile(
    true);
            }

            
    public void AddDirection(DirectoryInfo directory,string pattern)
            
    {
                
    this.pattern = pattern;
                
    this.docRootDirectory = directory.FullName;
                AddSubDirectory(directory);
            }

            
    private void AddSubDirectory(DirectoryInfo directory)
            
    {
                
    foreach (FileInfo fi in directory.GetFiles(pattern))
                

                   
    //遍历要写入索引的目录的所有文件,把他先加入Docuemnt对象,再加入索引,因为索引都是有Document对象组成
                    AddHtmlToDocument(fi.FullName);
                }

                
    foreach (DirectoryInfo di in directory.GetDirectories())
                

                    
    //层层遍历递归,只到把所有的子目录子文件都搞完
                    AddSubDirectory(di);
                }

            }

            
    private void AddHtmlToDocument(string path)
            
    {
                Document doc 
    = new Document();
                
    string html;
                
    using (StreamReader sr = new StreamReader(path, System.Text.Encoding.Default))
                
    {
                    html 
    = sr.ReadToEnd();
                }

                
    int relativePathStartsAt = this.docRootDirectory.EndsWith("\\"? this.docRootDirectory.Length : this.docRootDirectory.Length + 1;
                
    string relativePath = path.Substring(relativePathStartsAt);

                doc.Add(Field.UnStored(
    "text", ParseHtml(html)));
                doc.Add(Field.Keyword(
    "path", relativePath));
                doc.Add(Field.Text(
    "title", GetTitle(html)));
                writer.AddDocument(doc);
            }

            
    /// <summary>
            
    /// 把读取的文件中的所有的html标记去掉,把&nbsp;替换成空格
            
    /// </summary>
            
    /// <param name="html"></param>
            
    /// <returns></returns>

            private string ParseHtml(string html)
            
    {
                
    string temp = Regex.Replace(html, "<[^>]*>""");
                
    return temp.Replace("&nbsp;"," ");
            }

            
    /// <summary>
            
    /// 获得读取的html文挡的标题
            
    /// </summary>
            
    /// <param name="html"></param>
            
    /// <returns></returns>

            private string GetTitle(string html)
            
    {
                Match m 
    = Regex.Match(html,"<title>(.*)</title>");
                
    if (m.Groups.Count == 2)
                    
    return m.Groups[1].Value;
                
    return "此文挡标题未知";
            }


            
    public void Close()
            
    {
                writer.Optimize();
                writer.Close();
            }

        }

    }


    DotLucene搜索引擎Demo之:创 建索引

    在上篇文章我们说了怎么建立索引,现在说的是怎么搜索这个索引,最主要的我们是要理解startAt的含义,理解了他什么问 题都解决了。还有这个例子的分页很经典,我发现google和baidu用的都是这个分页方法。主要就两个方法,一个search()方法,主要是显示当 前页的搜索记录

     1protected void search()
     
    2    {
     
    3        DateTime start = DateTime.Now;//搜索的开始时间
     4        //得到索引所在的目录,我们在上个console程序里把索引放到了index目录下
     5        string indexDirectory = Server.MapPath("index");
     
    6        //创建个索引搜索器
     7        IndexSearcher searcher = new IndexSearcher(indexDirectory);
     
    8        //分词并解析索引的text字段以便搜索
     9        Query thisQuery = QueryParser.Parse(this.Query,"text",new StandardAnalyzer());
    10        //为要绑定输出到页面的results建立几列
    11        this.Results.Columns.Add("path",typeof(string));
    12        this.Results.Columns.Add("sample",typeof(string));
    13        this.Results.Columns.Add("title",typeof(string));
    14        //开始搜索
    15        Hits hits = searcher.Search(thisQuery);
    16        //得到搜索返回的记录总数
    17        this.total = hits.Length();
    18        //创建一个高亮
    19        QueryHighlightExtractor highlighter = new QueryHighlightExtractor(thisQuery, new StandardAnalyzer(), "<B>""</B>");
    20        //初始化startAt,以便得到要显示的结果集
    21        this.startAt = initStartAt();
    22        //得到当前页要显示的记录数量,包括以前所有页的记录数,这样把他与this.startAt结合就能够很好的知道当前页要显示的记录数了
    23        int resultsCount = smallOf(this.total,this.startAt+this.maxResults);
    24        //开始循环得到当前页要显示的记录
    25        for (int i = this.startAt; i < resultsCount; i++)
    26        
    27           //得到每一行Hits的Document,因为Hits的没一行都是个Document对象
    28            Document doc = hits.Doc(i);
    29            //得到doc里面的列path的值
    30            string path = doc.Get("path");
    31            //再得到这个路径在web程序的路径,我们原来把文档放到了web根目录的documents目录下的
    32            string location = Server.MapPath(@"documents\"+path);
    33            //用StreamReader读取文档,因为我们不能够直接从索引中得到text字段的值,因为我们建立索引的时候没有存储他的
    34            string plainText;
    35            using (StreamReader sr = new StreamReader(location, System.Text.Encoding.Default))
    36            {
    37                plainText = ParseHtml(sr.ReadToEnd());
    38            }

    39            //为结果集DataTable,Results添加个新行
    40            DataRow dr = this.Results.NewRow();
    41            dr["title"= doc.Get("title");
    42            dr["path"= @"documents/" + path;
    43            dr["sample"= highlighter.GetBestFragment(plainText,80);
    44            //把行添加进DataTable
    45            this.Results.Rows.Add(dr);
    46        }

    47        //循环完毕,关闭搜索
    48        searcher.Close();
    49        //搜索花费多少时间
    50        this.duration = DateTime.Now - start;
    51        //给fromItem赋值,他总是startAt+1
    52        this.fromItem = this.startAt + 1;
    53        //给toItem赋值
    54        this.toItem = smallOf(this.total,this.startAt+this.maxResults);
    55
    56    }
    还有就是一个Paging属性,他的作用就是分页,输出分页的html这个属性很经典
     
    1/**//// <summary>
     2    /// 开始分页
     3    /// </summary>
     4    /// <returns></returns>
     5    protected DataTable Paging
     
    6    {
     
    7        get
     
    8        
     
    9           //知道了startAt,分页也很容易了,现在根据startAt得到当前是第几页,注意,现在这里的页数也是暂时从0开始的
    10            int pageNumber = (this.startAt + this.maxResults - 1/ this.maxResults;
    11            DataTable dt = new DataTable();
    12            dt.Columns.Add("html");
    13            DataRow dr = dt.NewRow();
    14            //暂时得到当前页的html连接,注意这里当真正显示页数的时候要+1
    15            dr["html"= pagingNumberHtml(startAt,pageNumber+1,false);
    16            dt.Rows.Add(dr);
    17            //前面显示10页,如果有的话
    18            int previousPagesCount = 10;
    19            //循环把前面页的html连接插到前面去 
    20            for (int i = pageNumber - 1; i >= 0 && i >= pageNumber - previousPagesCount; i--)
    21            {
    22                DataRow r = dt.NewRow();
    23                r["html"= pagingNumberHtml(i*this.maxResults,i+1,true);
    24                dt.Rows.InsertAt(r,0);;
    25            }

    26            //后面也显示10页,如果有的话
    27            int nextPagesCount = 10;
    28            for (int i = pageNumber + 1; i <= this.pageCount && i <= pageNumber + nextPagesCount; i++)
    29            {
    30                DataRow r = dt.NewRow();
    31                r["html"= pagingNumberHtml(i*this.maxResults,i+1,true);
    32                dt.Rows.Add(r);
    33            }

    34            //添加下一页的超级连接
    35            DataRow lastRow = dt.NewRow();
    36            lastRow["html"= "<a href='Search.aspx?q="+this.Query+"&start="+(pageNumber+1)*this.maxResults+"'>下一页</a>";
    37            dt.Rows.Add(lastRow);
    38            return dt;
    39
    40        }
    using System;
    using System.Collections;
    using System.ComponentModel;
    using System.Data;
    using System.Data.SqlClient;
    using System.Drawing;
    using System.Threading;
    using System.Web;
    using System.Web.SessionState;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.HtmlControls;
    using Lucene.Net.Index;
    using Lucene.Net.Documents;
    using Lucene.Net.Analysis.Cn;
    using System.IO;
    using Lucene.Net.QueryParsers;
    using Lucene.Net.Search;
    using Lucene.Net.Search.Highlight;
    using System.Text.RegularExpressions;
    using XK_Vote.DAL;
    using System.Net;
    namespace XK_Vote
    {
     /// <summary>
     /// WebForm1 的摘要说明。
     /// </summary>
     public    class WebForm1 : System.Web.UI.Page
     {
      protected System.Web.UI.WebControls.Button Button2;
      protected System.Web.UI.WebControls.Button Button1;
      /// <summary>
      /// 搜索返回的结果集
      /// </summary>
      protected DataTable Results = new DataTable();
      /// <summary>
      /// 搜索返回的结果总数
      /// </summary>
      private int total;
      /// <summary>
      /// 搜索的结果每页显示多少记录条数
      /// </summary>
      private readonly int maxResults = 10;
      /// <summary>
      /// 本次搜索所花费的时间
      /// </summary>
      private TimeSpan duration;
      /// <summary>
      /// 本次搜索是从哪条数据开始的,这个东西很重要,通过他基本上可以得到一切
      /// </summary>
      private int startAt;
      /// <summary>
      /// 本页显示的数据是从哪条开始的,他总是startAt+1
      /// </summary>
      private int fromItem;
      /// <summary>
      /// 本页显示的数据记录到哪条结束
      /// </summary>
      private int toItem;
      protected System.Web.UI.WebControls.TextBox TextBox1;
            protected string pg;
      protected void Page_Load(object sender, System.EventArgs e)
      {
       // 在此处放置用户代码以初始化页面

      }

      #region Web 窗体设计器生成的代码
      override protected void OnInit(EventArgs e)
      {
       //
       // CODEGEN: 该调用是 ASP.NET Web 窗体设计器所必需的。
       //
       InitializeComponent();
       base.OnInit(e);
      }
      
      /// <summary>
      /// 设计器支持所需的方法 - 不要使用代码编辑器修改
      /// 此方法的内容。
      /// </summary>
      private void InitializeComponent()
      {   
       this.Button1.Click += new System.EventHandler(this.Button1_Click);
       this.Button2.Click += new System.EventHandler(this.Button2_Click);
       this.Load += new System.EventHandler(this.Page_Load);

      }
      #endregion
      protected void IndexBook(Result rs,IndexWriter writer)
      {
        Document doc = new Document();                  
        doc.Add(Field.Keyword("Title", rs.Title));
           doc.Add(Field.Keyword("CopyFrom",rs.CopyFrom));
           doc.Add(Field.Keyword("UpdateTime",rs.UpdateTime.ToString()));
        doc.Add(Field.Text("Content", rs.Content));
           doc.Add(Field.Text("DefaultPicUrl",rs.DefaultPicUrl));
           doc.Add(Field.Text("IncludePic",rs.InclucePic.ToString()));
        writer.AddDocument(doc);
      
      }
      private string ParseHtml(string html)
      {
       string temp = Regex.Replace(html,"<[^>]*>|<|>","");
       return temp.Replace("&nbsp;"," ");
      }
      protected void Button1_Click(object sender, System.EventArgs e)
      {
       for(int t=0;t<1;t++)
       {
        System.Threading.Thread thread=new Thread(new ThreadStart(InitIndex));
        thread.Start();
       }

      }
      private IndexWriter GetWriter(string physicalPath)
      {
       IndexWriter indexWriter = null;
       string segmentFile = System.IO.Path.Combine(physicalPath, "segments");
       if ( System.IO.File.Exists(segmentFile) )
        indexWriter = new IndexWriter(physicalPath, new Lucene.Net.Analysis.Cn.ChineseAnalyzer(), false);
       else
        indexWriter = new IndexWriter(physicalPath, new Lucene.Net.Analysis.Cn.ChineseAnalyzer(), true);

       return indexWriter;

      }
            private void InitIndex()
            {
       int index=0;
       int end = 0;
    //   IndexWriter iw=new IndexWriter(@"D:\Index",ca,true); 
       IndexWriter iw=GetWriter(@"D:\study\XK_Vote\Index");
       iw.mergeFactor = 15;
       while(true)
       {
        DB db=new DB();
        System.Data.SqlClient.SqlDataReader sdr=null; 
        System.Data.SqlClient.SqlParameter [] p={db.MakeInParam("@id",SqlDbType.Int,4,index)};
        db.RunProc("GZ_SelectText",p,out sdr);
        int count = 0;
        while(sdr.Read())
        {
         Result rs=new Result();
         rs.Title=sdr["Title"].ToString();
         rs.Content=this.ParseHtml(sdr["Content"].ToString());
         rs.CopyFrom=sdr["CopyFrom"].ToString();
         if(Convert.ToInt32(sdr["IncludePic"])==1)
         {
          rs.InclucePic=Convert.ToInt32(sdr["IncludePic"]);
          rs.DefaultPicUrl=sdr["DefaultPicUrl"].ToString();
         }
         else
         {
          rs.InclucePic=0;
          rs.DefaultPicUrl="";
         }
         rs.UpdateTime=Convert.ToDateTime(sdr["UpdateTime"].ToString());
         IndexBook(rs,iw);
         index=Convert.ToInt32(sdr["ArticleID"]);
         count++;
         
        }
        end++;
        if(count < 100-1)
         break;
        if(end > 200)
         break;
                  

       }
       iw.Optimize();
       iw.Close();

            }
      private void Button2_Click(object sender, System.EventArgs e)
      { 
       this.Query=Convert.ToString(Request.Form["Content"]);
    //   Search();
       DataBind();

      }
      /*protected void Search()
      {
       DateTime start = DateTime.Now;//搜索的开始时间
       Lucene.Net.Search.IndexSearcher search=new Lucene.Net.Search.IndexSearcher(@"D:\LuceneIndex");
       string [] fields={"Content","Title"};
                Query multiquery = MultiFieldQueryParser.Parse(this.Query,fields,new ChineseAnalyzer());
       this.Results.Columns.Add("Title",typeof(string));
       this.Results.Columns.Add("Content",typeof(string));
    //   this.Results.Columns.Add("CopyFrom",typeof(string));
    //   this.Results.Columns.Add("UpdateTime",typeof(string));
       QueryHighlightExtractor highlighter = new QueryHighlightExtractor(multiquery, new ChineseAnalyzer(), "<font color=red>", "</font>");
       Lucene.Net.Search.Hits hit =search.Search(multiquery);
     
       //初始化startAt,以便得到要显示的结果集
       this.total=hit.Length();
       this.startAt = initStartAt();
       //得到当前页要显示的记录数量,包括以前所有页的记录数,这样把他与this.startAt结合就能够很好的知道当前页要显示的记录数了
       int resultsCount = smallOf(this.total,this.startAt+this.maxResults);
       //开始循环得到当前页要显示的记录ex
       for (int i = this.total - this.startAt-1; i >= this.total - resultsCount; i--)
       {  
        DataRow dr    = this.Results.NewRow();
        dr["Title"]   = highlighter.GetBestFragments(hit.Doc(i).GetField("Title").StringValue(),1,2,"...");
        dr["Content"] = highlighter.GetBestFragments(hit.Doc(i).GetField("Content").StringValue(),80,2,"...");
    //                dr["CopyFrom"]= hit.Doc(i).GetField("CopyFrom").StringValue();
    //    dr["UpdateTime"]=hit.Doc(i).GetField("UpdateTime").StringValue();
        //把行添加进DataTable
        this.Results.Rows.Add(dr);

       }
       //循环完毕,关闭搜索
       search.Close();
       //搜索花费多少时间
       this.duration = DateTime.Now - start;
       Response.Write(this.duration);
       //给fromItem赋值,他总是startAt+1
       this.fromItem = this.startAt + 1;
       //给toItem赋值
       this.toItem = smallOf(this.total,this.startAt+this.maxResults);
       
      }*/
      /// <summary>
      /// 开始分页
      /// </summary>
      /// <returns></returns>
      protected DataTable Paging
      {
       get
       {
        //知道了startAt,分页也很容易了,现在根据startAt得到当前是第几页,注意,现在这里的页数也是暂时从0开始的
        int pageNumber = (this.startAt + this.maxResults - 1) / this.maxResults;
        DataTable dt = new DataTable();
        dt.Columns.Add("html");
        DataRow dr = dt.NewRow();
        //暂时得到当前页的html连接,注意这里当真正显示页数的时候要+1
        dr["html"] = pagingNumberHtml(startAt,pageNumber+1,false);
        dt.Rows.Add(dr);
        //前面显示10页,如果有的话
        int previousPagesCount = 10;
        //循环把前面页的html连接插到前面去
        for (int i = pageNumber - 1; i >= 0 && i >= pageNumber - previousPagesCount; i--)
        {
         DataRow r = dt.NewRow();
         r["html"] = pagingNumberHtml(i*this.maxResults,i+1,true);
         dt.Rows.InsertAt(r,0);;
        }
        //后面也显示10页,如果有的话
        int nextPagesCount = 10;
        for (int i = pageNumber + 1; i <= this.pageCount && i <= pageNumber + nextPagesCount; i++)
        {
         DataRow r = dt.NewRow();
         r["html"] = pagingNumberHtml(i*this.maxResults,i+1,true);
         dt.Rows.Add(r);
        }
        //添加下一页的超级连接
        DataRow lastRow = dt.NewRow();
        lastRow["html"] = "<a href='WebForm1.aspx?q=" + HttpUtility.UrlEncode(this.Query) + "&start=" + (pageNumber + 1) * this.maxResults + "'>下一页</a>";
        dt.Rows.Add(lastRow);
        return dt;

       }
      }
      /// <summary>
      /// 得到某一页的html连接字符串
      /// </summary>
      /// <param name="start">页是从那条记录开始搜索的</param>
      /// <param name="number">页数</param>
      /// <param name="active">是不是个超级连接</param>
      /// <returns></returns>
      private string pagingNumberHtml(int start, int number, bool active)
      {
       if (active)
       {
        return "<a href='WebForm1.aspx?q="+HttpUtility.UrlEncode(this.Query)+"&start="+start+"'>"+number+"</a>";
       }
       else
       {
        return "<span style='font-weight:bold;'>'+number+'</span>'";
       }
      }
      private int smallOf(int first, int second)
      {
       return first < second ? first : second;//得到两个数之间的较少数
      }
      private int initStartAt()
      {
       try
       {
        int sa = Convert.ToInt32(this.Request.Params["start"]);
        if (sa < 0)
         return 0;
        if (sa >= this.total - 1)
         return lastPageStartAt;//如果不足以显示三条,从最后页的第0条开始显示
        return sa;
       }
       catch
       {
        return 0;//如果输入非法非数字,那么从第0条开始显示
       }
      }
      private int pageCount
      {
       get
       {
        return (this.total - 1) / this.maxResults;//总页数,记住都是从0开始的
       }
      }
      private int lastPageStartAt
      {
       get
       {
        return pageCount * this.maxResults;//返回最末页的第0条的序号
       }
      }
      private string query="";
      protected string Query
      {
       get
       {
        return query;
       }
       set
       {
        this.query=value;
       }
      }
      protected string Summary
      {
       get
       {
        if (this.total > 0)
         return "共有结果<span style='color:red;'>' + this.total + '</span>,当前从第<span style='color:red;'>' + this.fromItem + '</span>条到第<span style='color:red;'>' + this.toItem + '</span>条,本次搜索耗时<span style='color:red;'>' + this.duration.TotalSeconds + '</span>秒'";
          else
         return "对不起,本次搜索没有找到任何结果";
       }
      }

     


     }

    }

  • 相关阅读:
    leetcode108 Convert Sorted Array to Binary Search Tree
    leetcode98 Validate Binary Search Tree
    leetcode103 Binary Tree Zigzag Level Order Traversal
    leetcode116 Populating Next Right Pointers in Each Node
    Python全栈之路Day15
    Python全栈之路Day11
    集群监控
    Python全栈之路Day10
    自动部署反向代理、web、nfs
    5.Scss的插值
  • 原文地址:https://www.cnblogs.com/chenying99/p/1971373.html
Copyright © 2011-2022 走看看