利用Lucene.NET怎么实现一个站内搜索功能
创始人
2024-11-28 21:03:51
0次

导入Lucene.NET 开发包

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

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

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

利用Lucene.NET怎么实现一个站内搜索功能

搜索,根据索引查找

利用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
  {
    /// 
    /// 日志小助手
    /// 
    static Common.LogHelper logger = new Common.LogHelper(typeof(SearchBLL));
    /// 
    /// 索引保存的位置,保存在配置文件中从配置文件读取
    /// 
    static string indexPath = Common.ConfigurationHelper.AppSettingMapPath("IndexPath");
    
    /// 
    /// 创建索引文件或更新索引文件
    /// 
    /// 索引信息
    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;
      }
    }

    /// 
    /// 根据id删除相应索引
    /// 
    /// 要删除的索引id
    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 静态变量
    /// 
    /// 日志助手
    /// 
    static Common.LogHelper logger = new Common.LogHelper(typeof(SearchBLL));
    /// 
    /// 索引保存位置
    /// 
    static string indexPath = Common.ConfigurationHelper.AppSettingMapPath("IndexPath");
    /// 
    /// 搜索
    /// 
    /// 用户搜索的关键词
    /// 返回搜索的结果
    public static List 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 listResult = new List();
        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;
      }
    }

    /// 
    /// 获取内容预览
    /// 
    /// 内容
    /// 关键词
    /// 
    private static string Preview(string body, string keyword)
    {
      //创建HTMLFormatter,参数为高亮单词的前后缀 
      PanGu.HighLight.SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter("", "");
      //创建 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;
    }

    /// 
    /// 盘古分词,对用户输入的搜索关键词进行分词
    /// 
    /// 用户输入的关键词
    /// 分词之后的结果组成的数组
    private static string[] SplitWord(string str)
    {
      List list = new List();
      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; }
  }
}

相关内容

热门资讯

空调连续工作多久需要停机休息 空调连续工作的时间取决于多个因素,包括空调的型号、功率、使用环境和维护情况等。一般来说,空调可以连续...
空调哪个符号是除湿的 空调除湿功能通常用一个水滴形状的符号表示。不同品牌和型号的空调遥控器可能会有所差异,但一般来说,除湿...
格力空调多长时间加一次氟利昂 1、一般情况下,空调对氟利昂的使用量是非常小的,所以在空调使用了四年左右的时候再考虑加也没问题。2、...
空调用多长时间需要加氟一次 一般情况下是五年。当出现以下这几种情况时,空调才需要加氟。1、空调的室内机和室外机各有两个接口,当接...
格调生活·C标思享荟:卡萨帝在... 9月12日的郑州龙湖金融岛,夜风轻拂,灯光璀璨。“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑...
品牌价值破千亿、高端份额持续领... 9月12日,“C标时刻共传奇——卡萨帝思享荟暨20周年品牌盛典郑州站”在郑州龙湖金融岛举办。2026...
空调主板坏了修800还是换新的 空调主板坏了是修还是换新,需要考虑多个因素,以下是一些建议:1. 维修成本:如果维修费用较低,比如 ...
空调开多久需要休息多长时间 问题:空调开多久需要休息多长时间回答:1、在使用空调的过程中,一般空调开7~8个小时左右需要休息2~...
中央空调多长时间清洗一次 1、一般空调开机前清洗一次即可,空调开机中间时段再清洗一次,空调关机时清洗一次,这样比较合理。2、空...
中央空调氟利昂多久加一次 看空调制热和制冷程度,很多人的观点认为氟利昂必须一年放一次,其实这个观点是错误的,理解上面有点偏差,...