首页 > 编程 > .NET > 正文

使用Lucene.NET实现站内搜索

2020-01-17 23:14:43
字体:
来源:转载
供稿:网友

导入Lucene.NET 开发包

Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene.Net 是 .NET 版的Lucene。

你可以在这里下载到最新的Lucene.NET

创建索引、更新索引、删除索引

搜索,根据索引查找

IndexHelper 添加、更新、删除索引

using System;using Lucene.Net.Store;using Lucene.Net.Index;using Lucene.Net.Analysis.PanGu;using Lucene.Net.Documents;namespace BLL{  class IndexHelper  {    /// <summary>    /// 日志小助手    /// </summary>    static Common.LogHelper logger = new Common.LogHelper(typeof(SearchBLL));    /// <summary>    /// 索引保存的位置,保存在配置文件中从配置文件读取    /// </summary>    static string indexPath = Common.ConfigurationHelper.AppSettingMapPath("IndexPath");        /// <summary>    /// 创建索引文件或更新索引文件    /// </summary>    /// <param name="item">索引信息</param>    public static void CreateIndex(Model.HelperModel.IndexFileHelper item)    {      try      {        //索引存储库        FSDirectory directory = FSDirectory.Open(new System.IO.DirectoryInfo(indexPath), new NativeFSLockFactory());        //判断索引是否存在        bool isUpdate = IndexReader.IndexExists(directory);        if (isUpdate)        {          //如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁          if (IndexWriter.IsLocked(directory))          {            //解锁索引库            IndexWriter.Unlock(directory);          }        }        //创建IndexWriter对象,添加索引        IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);        //获取新闻 title部分        string title = item.FileTitle;        //获取新闻主内容        string body = item.FileContent;        //为避免重复索引,所以先删除number=i的记录,再重新添加        //尤其是更新的话,更是必须要先删除之前的索引        writer.DeleteDocuments(new Term("id", item.FileName));        //创建索引文件 Document        Document document = new Document();        //只有对需要全文检索的字段才ANALYZED        //添加id字段        document.Add(new Field("id", item.FileName, Field.Store.YES, Field.Index.NOT_ANALYZED));        //添加title字段        document.Add(new Field("title", title, Field.Store.YES, Field.Index.NOT_ANALYZED));        //添加body字段        document.Add(new Field("body", body, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));        //添加url字段        document.Add(new Field("url", item.FilePath, Field.Store.YES, Field.Index.NOT_ANALYZED));        //写入索引库        writer.AddDocument(document);        //关闭资源        writer.Close();        //不要忘了Close,否则索引结果搜不到        directory.Close();        //记录日志        logger.Debug(String.Format("索引{0}创建成功",item.FileName));      }      catch (SystemException ex)      {        //记录错误日志        logger.Error(ex);        throw;      }      catch (Exception ex)      {        //记录错误日志        logger.Error(ex);        throw;      }    }    /// <summary>    /// 根据id删除相应索引    /// </summary>    /// <param name="guid">要删除的索引id</param>    public static void DeleteIndex(string guid)    {      try      {        ////索引存储库        FSDirectory directory = FSDirectory.Open(new System.IO.DirectoryInfo(indexPath), new NativeFSLockFactory());        //判断索引库是否存在索引        bool isUpdate = IndexReader.IndexExists(directory);        if (isUpdate)        {          //如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁          if (IndexWriter.IsLocked(directory))          {            IndexWriter.Unlock(directory);          }        }        IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);        //删除索引文件        writer.DeleteDocuments(new Term("id", guid));        writer.Close();        directory.Close();//不要忘了Close,否则索引结果搜不到        logger.Debug(String.Format("删除索引{0}成功", guid));      }      catch (Exception ex)      {        //记录日志        logger.Error(ex);        //抛出异常        throw;      }    }  }}

Search 通过查找索引实现搜索

using Lucene.Net.Analysis;using Lucene.Net.Analysis.PanGu;using Lucene.Net.Documents;using Lucene.Net.Index;using Lucene.Net.Search;using Lucene.Net.Store;using Model.HelperModel;using System;using System.Collections.Generic;namespace BLL{  public static class SearchBLL  {    //一个类中可能会有多处输出到日志,多处需要记录日志,常将logger做成static 静态变量    /// <summary>    /// 日志助手    /// </summary>    static Common.LogHelper logger = new Common.LogHelper(typeof(SearchBLL));    /// <summary>    /// 索引保存位置    /// </summary>    static string indexPath = Common.ConfigurationHelper.AppSettingMapPath("IndexPath");    /// <summary>    /// 搜索    /// </summary>    /// <param name="keywords">用户搜索的关键词</param>    /// <returns>返回搜索的结果</returns>    public static List<SearchResult> Search(string keywords)    {      try      {        //索引存储库        FSDirectory directory = FSDirectory.Open(new System.IO.DirectoryInfo(indexPath), new NoLockFactory());        //创建IndexReader对象        IndexReader reader = IndexReader.Open(directory, true);        //创建IndexSearcher对象        IndexSearcher searcher = new IndexSearcher(reader);        //新建PhraseQuery 查询对象        PhraseQuery query = new PhraseQuery();        //把用户输入的关键词进行拆词        foreach (string word in SplitWord(keywords))        {          //添加搜索关键词          query.Add(new Term("body", word));        }        //设置分词间距为100字之内        query.SetSlop(100);        TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true);        //根据查询条件查询结果        searcher.Search(query, null, collector);        //搜索到的ScoreDoc结果        ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;        //保存搜索结果的list        List<SearchResult> listResult = new List<SearchResult>();        for (int i = 0; i < docs.Length; i++)        {          //取到文档的编号(主键,这个是Lucene .net分配的)          //检索结果中只有文档的id,如果要取Document,则需要Doc再去取          //降低内容占用          int docId = docs[i].doc;          //根据id找Document          Document doc = searcher.Doc(docId);          string number = doc.Get("id");          string title = doc.Get("title");          string body = doc.Get("body");          string url = doc.Get("url");          //建立一个搜索结果对象          SearchResult result = new SearchResult();          result.Number = number;          result.Title = title;          result.BodyPreview = Preview(body, keywords);          result.Url = url;          //添加到结果列表          listResult.Add(result);        }        if (listResult.Count == 0)        {          return null;        }        else        {          return listResult;        }      }      catch (SystemException ex)      {        logger.Error(ex);        return null;      }      catch (Exception ex)      {        logger.Error(ex);        return null;      }    }    /// <summary>    /// 获取内容预览    /// </summary>    /// <param name="body">内容</param>    /// <param name="keyword">关键词</param>    /// <returns></returns>    private static string Preview(string body, string keyword)    {      //创建HTMLFormatter,参数为高亮单词的前后缀       PanGu.HighLight.SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter("<font color=/"red/">", "</font>");      //创建 Highlighter ,输入HTMLFormatter 和 盘古分词对象Semgent       PanGu.HighLight.Highlighter highlighter = new PanGu.HighLight.Highlighter(simpleHTMLFormatter, new PanGu.Segment());      //设置每个摘要段的字符数       highlighter.FragmentSize = 100;      //获取最匹配的摘要段       string bodyPreview = highlighter.GetBestFragment(keyword, body);      return bodyPreview;    }    /// <summary>    /// 盘古分词,对用户输入的搜索关键词进行分词    /// </summary>    /// <param name="str">用户输入的关键词</param>    /// <returns>分词之后的结果组成的数组</returns>    private static string[] SplitWord(string str)    {      List<string> list = new List<string>();      Analyzer analyzer = new PanGuAnalyzer();      TokenStream tokenStream = analyzer.TokenStream("", new System.IO.StringReader(str));      Lucene.Net.Analysis.Token token = null;      while ((token = tokenStream.Next()) != null)      {        list.Add(token.TermText());      }      return list.ToArray();    }  }}

SearchResult 模型

namespace Model.HelperModel{  public class SearchResult  {    public string Number { get; set; }    public string Title { get; set; }    public string BodyPreview { get; set; }    public string Url { get; set; }  }}

以上所述就是本文的全部内容了,希望大家能够喜欢。

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表