完整的站内搜索Demo(Lucene.Net+盘古分词)

前言

各位朋友,谢谢大家的支持,由于文件过大,有考虑到版权的问题,故没有提供下载,本人已建立一个搜索技术交流群:77570783,源代码已上传至群共享,需要的朋友,请自行下载!

首先自问自答几个问题,以让各位看官了解写此文的目的

什么是站内搜索?与一般搜索的区别?很多网站都有搜索功能,很多都是用SQL语句的Like实现的,但是Like无法做到模糊匹配(例如我搜索“.net学习”,如果有“.net的学习”,Like就无法搜索到,这明显不符合需求,但是站内搜索就能做到),另外Like会造成全盘扫描,会对数据库造成很大压力,为什么不用数据库全文检索,跟普通SQL一样,很傻瓜,灵活性不行

为什么不用百度、google的站内搜索?毕竟是别人的东西,用起来肯定会受制于人(哪天你的网站火了,它看你不爽了,就可能被K),主要还是索引的不够及时,网站新的内容,需要一定时间才能被索引到,并且用户的体验也不太好

最近改造了《动力起航》的站内搜索的功能,它其实已经有站内搜索的功能,但是是用like来实现的,改造此功能是本着在尽可能少的修改网站的源代码的情况下去改造此功能以及此站内搜索功能可以很好的移植到其他项目的原则来编写!本文有借鉴其他大神及园友的技术,在此谢谢!

功能简介

站内搜索使用的技术Log4Net 日志记录

lucene.Net 全文检索开发包,只能检索文本信息

分词(lucene.Net提供StandardAnalyzer一元分词,按照单个字进行分词,一个汉字一个词)

盘古分词 基于词库的分词,可以维护词库

具体详解

首先我们新增的SearchHelper类需要将其做成一个单例,使用单例是因为:有许多地方需要使用使用,但我们同时又希望只有一个对象去操作,具体代码如下:

#region 创建单例 SearchHelper uniqueInstance;locker = new object(); SearchHelper(){ } 定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点 SearchHelper GetInstance(){// 当第一个线程运行到这里时,此时会对locker对象 "加锁",// 当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,,该线程就会挂起等待第一个线程解锁 (locker){(uniqueInstance == null){uniqueInstance = new SearchHelper();}}return uniqueInstance;}#endregion

其次,使用Lucene.Net需要将被搜索的进行索引,然后保存到索引库以便被搜索,我们引入了“生产者,消费者模式”. 生产者就是当我们新增,修改或删除的时候我们就需要将其在索引库进行相应的操作,我们将此操作交给另一个线程去处理,这个线程就是我们的消费者,使用“生产者,消费者模式”是因为:索引库使用前需解锁操作,使用完成之后必须解锁,所以只能有一个对象对索引库进行操作,避免数据混乱,所以要使用生产者,消费者模式

首先我们来看生产者,代码如下:

任务类,包括任务的Id ,操作的类型 IndexJob{public int Id { get; set; }public JobType JobType { get; set; }} 枚举,操作类型是增加还是删除 JobType { Add, Remove }#region 任务添加public void AddArticle(int artId){IndexJob job = new IndexJob();job.Id = artId;job.JobType = JobType.Add;logger.Debug(artId + );jobs.Enqueue(job);//把任务加入商品库}public void RemoveArticle(int artId){IndexJob job = new IndexJob();job.JobType = JobType.Remove;job.Id = artId;logger.Debug(artId + );jobs.Enqueue(job);//把任务加入商品库}#endregion

下面是消费者,消费者我们单独一个线程来进行任务的处理:

索引任务线程 IndexOn(){logger.Debug();while (true){if (jobs.Count <= 0){Thread.Sleep(5 * 1000);continue;}(!System.IO.Directory.Exists(IndexDic)){System.IO.Directory.CreateDirectory(IndexDic);}FSDirectory directory = FSDirectory.Open(new DirectoryInfo(IndexDic), new NativeFSLockFactory());bool isUpdate = IndexReader.IndexExists(directory);logger.Debug(+ isUpdate);if (isUpdate){ (IndexWriter.IsLocked(directory)){logger.Debug();IndexWriter.Unlock(directory);logger.Debug();}}IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);ProcessJobs(writer);writer.Close();directory.Close();//不要忘了Close,否则索引结果搜不到logger.Debug();}}private void ProcessJobs(IndexWriter writer){while (jobs.Count != 0){IndexJob job = jobs.Dequeue();writer.DeleteDocuments(, job.Id.ToString()));(job.JobType == JobType.Add){BLL.article bll = new BLL.article();Model.article art = bll.GetArticleModel(job.Id);if (art == null)//有可能刚添加就被删除了{continue;}string channel_id = art.channel_id.ToString();string title = art.title;DateTime time = art.add_time;string content = Utils.DropHTML(art.content.ToString()););Document document = new Document();//只有对需要全文检索的字段才ANALYZEDdocument.Add(, job.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));document.Add(, title, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));document.Add(, channel_id, Field.Store.YES, Field.Index.NOT_ANALYZED));document.Add(, Addtime, Field.Store.YES, Field.Index.NOT_ANALYZED));document.Add(, content, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));writer.AddDocument(document);logger.Debug(+ job.Id + );}}}#endregion

心有多大,舞台就有多大。

完整的站内搜索Demo(Lucene.Net+盘古分词)

相关文章:

你感兴趣的文章:

标签云: