前言

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

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

什么是站内搜索?与一般搜索的区别?

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

为什么不用百度、google的站内搜索?

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

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

功能简介

站内搜索使用的技术

Log4Net  日志记录

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

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

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

具体详解

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

#region 创建单例

//定义一个静态变量来保存类的实例

private staticSearchHelper uniqueInstance;//定义一个标识确保线程同步

private static readonly object locker = new object();//定义私有构造函数,使外界不能创建该类实例

privateSearchHelper()

{ }///

///定义公有方法提供一个全局访问点,同时你也可以定义公有属性来提供全局访问点///

///

public staticSearchHelper GetInstance()

{//当第一个线程运行到这里时,此时会对locker对象 "加锁",//当第二个线程运行该方法时,首先检测到locker对象为"加锁"状态,该线程就会挂起等待第一个线程解锁//lock语句运行完之后(即线程运行完之后)会对该对象"解锁"

lock(locker)

{//如果类的实例不存在则创建,否则直接返回

if (uniqueInstance == null)

{

uniqueInstance= newSearchHelper();

}

}returnuniqueInstance;

}#endregion

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

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

private Queue jobs = new Queue(); //任务队列,保存生产出来的任务和消费者使用,不使用list避免移除时数据混乱问题

///

///任务类,包括任务的Id ,操作的类型///

classIndexJob

{public int Id { get; set; }public JobType JobType { get; set; }

}///

///枚举,操作类型是增加还是删除///

enumJobType { Add, Remove }#region 任务添加

public void AddArticle(intartId)

{

IndexJob job= newIndexJob();

job.Id=artId;

job.JobType=JobType.Add;

logger.Debug(artId+ "加入任务列表");

jobs.Enqueue(job);//把任务加入商品库

}public void RemoveArticle(intartId)

{

IndexJob job= newIndexJob();

job.JobType=JobType.Remove;

job.Id=artId;

logger.Debug(artId+ "加入删除任务列表");

jobs.Enqueue(job);//把任务加入商品库

}#endregion

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

///

///索引任务线程///

private voidIndexOn()

{

logger.Debug("索引任务线程启动");while (true)

{if (jobs.Count <= 0)

{

Thread.Sleep(5 * 1000);continue;

}//创建索引目录

if (!System.IO.Directory.Exists(IndexDic))

{

System.IO.Directory.CreateDirectory(IndexDic);

}

FSDirectory directory= FSDirectory.Open(new DirectoryInfo(IndexDic), newNativeFSLockFactory());bool isUpdate =IndexReader.IndexExists(directory);

logger.Debug("索引库存在状态" +isUpdate);if(isUpdate)

{//如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁

if(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 voidProcessJobs(IndexWriter writer)

{while (jobs.Count != 0)

{

IndexJob job=jobs.Dequeue();

writer.DeleteDocuments(new Term("number", job.Id.ToString()));//如果“添加文章”任务再添加,

if (job.JobType ==JobType.Add)

{

BLL.article bll= newBLL.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());string Addtime = art.add_time.ToString("yyyy-MM-dd");

Document document= newDocument();//只有对需要全文检索的字段才ANALYZED

document.Add(new Field("number", job.Id.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));

document.Add(new Field("title", title, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));

document.Add(new Field("channel_id", channel_id, Field.Store.YES, Field.Index.NOT_ANALYZED));

document.Add(new Field("Addtime", Addtime, Field.Store.YES, Field.Index.NOT_ANALYZED));

document.Add(new Field("content", content, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));

writer.AddDocument(document);

logger.Debug("索引" + job.Id + "完毕");

}

}

}#endregion

以上我们就把索引库建立完毕了,接下来就是进行搜索了,搜索操作里面包括对搜索关键词进行分词,其次是搜索内容搜索词高亮显示,下面就是搜索的代码:

#region 从索引搜索结果

///

///从索引搜索结果///

public List SearchIndex(string Words, int PageSize, int PageIndex, out int_totalcount)

{

_totalcount= 0;

Dictionary dic = new Dictionary();

BooleanQuery bQuery= newBooleanQuery();string title = string.Empty;string content = string.Empty;

title=GetKeyWordsSplitBySpace(Words);

QueryParser parse= new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "title", newPanGuAnalyzer());

Query query=parse.Parse(title);

parse.SetDefaultOperator(QueryParser.Operator.AND);

bQuery.Add(query, BooleanClause.Occur.SHOULD);

dic.Add("title", Words);

content=GetKeyWordsSplitBySpace(Words);

QueryParser parseC= new QueryParser(Lucene.Net.Util.Version.LUCENE_29, "content", newPanGuAnalyzer());

Query queryC=parseC.Parse(content);

parseC.SetDefaultOperator(QueryParser.Operator.AND);

bQuery.Add(queryC, BooleanClause.Occur.SHOULD);

dic.Add("content", Words);if (bQuery != null && bQuery.GetClauses().Length > 0)

{return GetSearchResult(bQuery, dic, PageSize, PageIndex, out_totalcount);

}return null;

}///

///获取///

///

private List GetSearchResult(BooleanQuery bQuery, Dictionary dicKeywords, int PageSize, int PageIndex, out inttotalCount)

{

List list = new List();

FSDirectory directory= FSDirectory.Open(new DirectoryInfo(IndexDic), newNoLockFactory());

IndexReader reader= IndexReader.Open(directory, true);

IndexSearcher searcher= newIndexSearcher(reader);

TopScoreDocCollector collector= TopScoreDocCollector.create(1000, true);

Sort sort= new Sort(new SortField("Addtime", SortField.DOC, true));

searcher.Search(bQuery,null, collector);

totalCount= collector.GetTotalHits();//返回总条数

TopDocs docs = searcher.Search(bQuery, (Filter)null, PageSize *PageIndex, sort);if (docs != null && docs.totalHits > 0)

{for (int i = 0; i < docs.totalHits; i++)

{if (i >= (PageIndex - 1) * PageSize && i < PageIndex *PageSize)

{

Document doc=searcher.Doc(docs.scoreDocs[i].doc);

Model.article model= newModel.article()

{

id= int.Parse(doc.Get("number").ToString()),

title= doc.Get("title").ToString(),

content= doc.Get("content").ToString(),

add_time= DateTime.Parse(doc.Get("Addtime").ToString()),

channel_id= int.Parse(doc.Get("channel_id").ToString())

};

list.Add(SetHighlighter(dicKeywords, model));

}

}

}returnlist;

}///

///设置关键字高亮///

/// 关键字列表

/// 返回的数据模型

///

private Model.article SetHighlighter(DictionarydicKeywords, Model.article model)

{

SimpleHTMLFormatter simpleHTMLFormatter= new PanGu.HighLight.SimpleHTMLFormatter("", "");

Highlighter highlighter= new PanGu.HighLight.Highlighter(simpleHTMLFormatter, newSegment());

highlighter.FragmentSize= 250;string strTitle = string.Empty;string strContent = string.Empty;

dicKeywords.TryGetValue("title", outstrTitle);

dicKeywords.TryGetValue("content", outstrContent);if (!string.IsNullOrEmpty(strTitle))

{string title =model.title;

model.title=highlighter.GetBestFragment(strTitle, model.title);if (string.IsNullOrEmpty(model.title))

{

model.title=title;

}

}if (!string.IsNullOrEmpty(strContent))

{string content =model.content;

model.content=highlighter.GetBestFragment(strContent, model.content);if (string.IsNullOrEmpty(model.content))

{

model.content=content;

}

}returnmodel;

}///

///处理关键字为索引格式///

///

///

private string GetKeyWordsSplitBySpace(stringkeywords)

{

PanGuTokenizer ktTokenizer= newPanGuTokenizer();

StringBuilder result= newStringBuilder();

ICollection words =ktTokenizer.SegmentToWordInfos(keywords);foreach (WordInfo word inwords)

{if (word == null)

{continue;

}

result.AppendFormat("{0}^{1}.0", word.Word, (int)Math.Pow(3, word.Rank));

}returnresult.ToString().Trim();

}#endregion

以上我们的站内搜索的SearchHelper类就建立好了,下面来讲讲如何使用,此类提供以下几个方法对外使用:

在Global里面启动消费者线程:

protected void Application_Start(objectsender, EventArgs e)

{//启动索引库的扫描线程(生产者)

SearchHelper.GetInstance().CustomerStart();

}

在需被搜索的新增或修改处添加下面方法:

SearchHelper.GetInstance().AddArticle(model.id);

在需被搜索的删除处添加下面方法:

SearchHelper.GetInstance().RemoveArticle(model.id);

搜索的时候使用下面的方法即可:

public List SearchIndex(string Words, int PageSize, int PageIndex, out int _totalcount)

结束语

本来想直接提供改造了《动力起航》的源代码,这样就可以直接看到效果了,一方面由于文件过大,另一方面不知道是不是会侵权,所有没有提供下载.如果有需要的朋友可以留下邮箱我将发给你,但仅供学习交流之用,误用做商业用途,以上如果有侵权等问题还请及时告知我,以便我及时更正!

很荣幸此文能上最多推荐,多谢大家的支持,由于索要改造了《动力起航》的源代码的园友很多,一一发给大家有点麻烦,在考虑是否放到网盘提供大家下载是不是更方便一些,但是不知道这样会不会有侵权之嫌啊,求各位给个建议,如果可以我就上传网盘了,不行的话就只能一个一个发给大家了!

最好如果觉得好的话!请给个推荐啊~~~~亲!!!!

java lucene 站内搜索_完整的站内搜索Demo(Lucene.Net+盘古分词)相关推荐

  1. 游戏行业网页整站模板下载_游戏 整站 高光 传奇 黑色

    游戏行业网页整站模板下载_游戏 整站 高光 传奇 黑色不仅仅是首页,二级页面,三级页面,登陆,购物车等,页面齐全 功能齐全  js+css+html (img,字体均有),前端html纯静态页面!无后 ...

  2. java不同工厂生产不同电器_完整案例分析再加知识整合——艾特抽象工厂模式,超详细的...

    抽象工厂模式 模式动机与定义 模式动机 产品等级结构:产品等级结构即产品的继承结构,例如一个抽象类是电视机,其子类有海尔电视机.海信电视机.TCL电视机,则抽象电视机与具体品牌的电视机之间构成了一个产 ...

  3. java语句电脑定时关机_月光软件站 - 编程文档 - Java - windows定时关机程序

    应网友yangxjn需要写的一个windows定时关机程序 . /** * Created by IntelliJ IDEA. * autor:cofbean * Date: 2004-3-10 * ...

  4. java创建多级xml树_月光软件站 - 编程文档 - Java - 用XSL与XML实现多级树形菜单

    XSL语言功能强大而奇妙.从前面的例子中,我们可以初步了解到,一个xsl文档中使用了很多类似"xsl:xxxxx"这样的标签,这些标签就是xsl语言的部分.一个xsl文档至少是由一 ...

  5. mysql查询三个月内的_如何在三个月内自学攻克雅思6.5分?

    新西兰留学移民经历分享-进行时(3) 编者按: 选新西兰留学前,看过很多博主的帖子,让一个从没到访过新西兰的我有了多一些了解和规划.现在也希望能把自己的经历分享给大家,同时作为人生中比较大的一个决定, ...

  6. 华为手机关闭蓝牙开发搜索_大众速腾手机无法搜索车载蓝牙系统维修实例

    故障现象 全新速腾1.4T,行驶才600公里,客户来站反应手机无法搜索车载蓝牙系统. 故障诊断 1. 打开收音机启动蓝牙系统,有蓝牙图标指示,用手机搜索时无法搜到车载蓝牙系统.查看右前座椅下方有蓝牙电 ...

  7. rss源搜索_如何使用Google图像搜索,RSS源和更多自定义墙纸

    rss源搜索 If you're looking for a free but powerful solution to automate your Windows wallpaper switchi ...

  8. 仿站和模板建站的区别_智能建站 自助建站,速成网站 花多少钱合适,适合我们的企业吗?...

    智能建站 自助建站 速成网站 可以说是自助建网站,模板建站,在现有的模板的基础上可以编辑文字拖拽图片模块. 正常都 有上千个模板供你选择使用,随时后台可以自行更换模板. 给装修QQ空间差不多.所以说只 ...

  9. python 内推_用Python实现内推外插法

    用Python实现内推外插法 用 Python 实现内推外插法 Author Z Date 2015 12 3 import numpy as np class node object def ini ...

  10. lucene索引MySQL原因_我如何在数据库中存储Lucene索引?

    这是我的示例代码: MysqlDataSource dataSource = new MysqlDataSource(); dataSource.setUser("root"); ...

最新文章

  1. php 编译mcrypt,centos 6下编译安装php时安装mcrypt支持库
  2. oracle归档原理,增加ORACLE归档位置到NFS(转)
  3. 如何使错误日志更加方便排查问题
  4. 熟悉linux运行环境,实验一 熟悉Ubuntu环境
  5. margin负值的巧妙运用(HTML、CSS)
  6. 通过Javascript Facebook API获取Facebook用户信息,以及当前用户的好
  7. 安卓打开本应用的应用信息界面的代码
  8. IntelliJ IDEA 创建 Vue工程
  9. 网平差中的基线定权(松弛因子)
  10. 生物信息百Jia软件(十三):clustalw
  11. cpc按点击计算怎么算_亚马逊CR怎么算?亚马逊CR及CTR计算公式介绍
  12. UserData使用总结[转载-lanyu]
  13. vss问题:客户端提示Sourcesafe cannot find a default database.Would you like to select one?
  14. Mac键盘锁住了怎么办
  15. SDN有哪些优势呢?
  16. j - 分组(动态规划)
  17. 计算机教师招聘要求专业,教师招聘:专业限制会有这3种要求?专业不对口该怎么办?...
  18. linux命令进u 盘,在Linux下制作一个磁盘文件,在u-boot 阶段对emmc 烧写整个Linux系统方法...
  19. blast mysql 基因序列_转载-网页方式下利用BLAST 程序进行基因/蛋白质序列比对...
  20. python画椭圆turtle_python之turtle画蚊香,python用turtle画圆弧,原理:利用turtl

热门文章

  1. 从多个文档在Word 2010中创建主文档
  2. 幼儿园大班下学期幼小衔接工作计划
  3. jsp房屋出租管理系统带合同
  4. Server-sent events(SSE) EventSource 客户端使用与服务器基础实现(基于Node.js)
  5. R语言系统教程(六):描述统计量
  6. 位操作的应用实例(2)位掩码
  7. C盘空间不够?教你简单扩容C盘空间
  8. css3新单位vw、vh、vmin、vmax的使用详解
  9. 企业微信--扫一扫功能(隐形坑)
  10. docker学习(二)docker入门