你的分享就是我们的动力 ---﹥

Lucene.Net与盘古分词实现站内搜索

时间:2013-03-21 11:14来源:www.chengxuyuans.com 点击:

Q:站内搜索为什么不能使用Like模糊查找

A:模糊契合度太低,匹配关键字之间不能含有其他内容。最重要的是它会造成全表扫描,效率底下,即使使用视图,也会造成数据库服务器"亚历山大"

Lucene简介

Lucene.Net只是一个全文检索开发包,不是一个成型的搜索引擎

它提供了类似SQLServer数据库正式版中的全文检索功能的索引库

你把数据扔给Lucene.Net,【Lucene.Net只针对文本信息建立索引,所以他只接收文本信息,如果不是文本信息,则要转换为文本信息】它会将文本内容分词后保存在索引库中,当用户输入关键字提交查询时,Lucene.Net从索引库中检索关键字数据,所以搜索速度非常快,适合于用户开发自己站内的搜索引擎

Q:分词

A:即将"不是所有痞子都叫一毛"文本内容通过分词算法 分割成为“不是” “所有” “痞子” “都” “叫” "一毛" 。 但是Lucene.Net内置分词算法对中文支持不是很好,以下会使用国内较为流行的分词算法 -- 盘古分词

以下是运行图解:

下面以用户查询数据库Book表 内容描述【对应字段名:ContentDescription】中包含其输入的关键字key的数据 演示Lucene.Net使用

运行界面效果如下:

本人使用的盘古分词版本为V2.3.1.0 Lucene.Net2.9

在项目下新建Dict文件以及dll文件包含Lucene与盘古分词所需的组件及文件信息如图示

1、项目添加对dll文件夹中四个程序集的引用

2、添加图书搜索页面SearchBlogs.aspx代码如下:

<%@PageLanguage="C#"AutoEventWireup="true"CodeBehind="SearchBook.aspx.cs"Inherits="BookShop.Web.SearchBook"%><!DOCTYPEhtmlPUBLIC"-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><htmlxmlns="http://www.w3.org/1999/xhtml"><headrunat="server"><title></title></head><body><formid="form1"runat="server"><asp:TextBoxID="txtSerach"runat="server"Width="494px"></asp:TextBox><asp:ButtonID="btnSearch"runat="server"onclick="btnSearch_Click"Text="搜索"/><asp:ButtonID="CreateView"runat="server"onclick="CreateView_Click"Text="创建索引"/><div><asp:RepeaterID="Repeater1"runat="server"><ItemTemplate><table><tbody><tr><tdstyle="font-size: small;color: red"width="650"><aid="link_prd_name"href='<%#Eval("Id","/book.aspx?id={0}")%>'target="_blank"name="link_prd_name"><%#Eval("Title")%></a></td></tr><tr><tdalign="left"><spanstyle="font-size: 12px;line-height: 20px"><%#Eval("ContentDescription")%></span></td></tr></tbody></table></ItemTemplate></asp:Repeater></div></form></body></html>

3、后台调用

usingSystem;usingSystem.Collections.Generic;usingSystem.IO;usingBookShop.BLL;usingLucene.Net.Analysis.PanGu;usingLucene.Net.Documents;usingLucene.Net.Index;usingLucene.Net.Search;usingLucene.Net.Store;namespaceBookShop.Web {publicpartialclassSearchBook: System.Web.UI.Page{protectedvoidPage_Load(objectsender,EventArgse) { }   //创建索引事件 可以防止在用户点击查询事件前执行 去掉页面中的"创建索引"按钮protectedvoidCreateView_Click(objectsender,EventArgse) {stringindexPath =@"C:\luceneTest";//索引文档保存位置FSDirectorydirectory =FSDirectory.Open(newDirectoryInfo(indexPath),newNativeFSLockFactory());boolisUpdate =IndexReader.IndexExists(directory);//是否存在索引库文件夹以及索引库特征文件if(isUpdate) {//如果索引目录被锁定(比如索引过程中程序异常退出或另一进程在操作),则解锁if(IndexWriter.IsLocked(directory)) {IndexWriter.Unlock(directory); } }//创建索引库对象 new PanGuAnalyzer()指定使用盘古分词进行切词IndexWriterwriter =newIndexWriter(directory,newPanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED);BooksManagerbookManager =newBooksManager();List<Model.Books> bookList = bookManager.GetModelList("");foreach(varbookinbookList) {Documentdocument =newDocument();//new 一篇文档 对象//所有字段的值都将以字符串类型保存//Field.Store表示是否保存字段原值。指定Field.Store.YE的字段在检索时才能用document.Get取出来值 NOT_ANALYZED指定不按照分词后的结果保存 document.Add(newField("id", book.Id.ToString(),Field.Store.YES,Field.Index.NOT_ANALYZED)); document.Add(newField("title", book.Title,Field.Store.YES,Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS));//Field.Index. ANALYZED指定文章内容按照分词后结果保存 否则无法实现后续的模糊查找 WITH_POSITIONS_OFFSETS指示不仅保存分割后的词 还保存词之间的距离 document.Add(newField("content", book.ContentDescription,Field.Store.YES,Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); writer.AddDocument(document);//文档写入索引库 } writer.Close(); directory.Close();//不要忘了Close,否则索引结果搜不到 }protectedvoidbtnSearch_Click(objectsender,EventArgse) {stringindexPath =@"C:\luceneTest";FSDirectorydirectory =FSDirectory.Open(newDirectoryInfo(indexPath),newNoLockFactory());IndexReaderreader =IndexReader.Open(directory,true);IndexSearchersearcher =newIndexSearcher(reader);//Index:索引//搜索条件PhraseQueryquery =newPhraseQuery();//把用户输入的关键字进行分词foreach(stringwordinCommon.SplitContent.SplitWords(txtSerach.Text.ToLower())) { query.Add(newTerm("content", word)); } query.SetSlop(100);//指定关键词相隔最大距离//TopScoreDocCollector盛放查询结果的容器TopScoreDocCollectorcollector =TopScoreDocCollector.create(1000,true); searcher.Search(query,null, collector);//根据query查询条件进行查询,查询结果放入collector容器//TopDocs 指定0到GetTotalHits() 即所有查询结果中的文档ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;//展示数据实体对象集合List<Model.Books> bookResult =newList<Model.Books>();for(inti = 0; i < docs.Length; i++) {//需要获得文档的详细内容的时候通过searcher.Doc来根据文档id来获得文档的详细内容对象DocumentintdocId = docs[i].doc;//得到查询结果文档的id(Lucene内部分配的id)Documentdoc = searcher.Doc(docId);//找到文档id对应的文档详细信息 Model.Booksbook =newModel.Books(); book.Title = doc.Get("title");//book.ContentDescription = doc.Get("content");//未使用高亮//搜索关键字高亮显示 book.ContentDescription = Common.SplitContent.HightLight(txtSerach.Text, doc.Get("content")); book.Id =Convert.ToInt32(doc.Get("id")); bookResult.Add(book); }//设置Repeater1 绑定查询结果集合 Repeater1.DataSource = bookResult; Repeater1.DataBind(); } }}

4、使用到的公共类 Common下的SplitContent

usingSystem.Collections.Generic;usingSystem.IO;usingLucene.Net.Analysis;usingLucene.Net.Analysis.PanGu;usingPanGu;namespaceBookShop.Web.Common {publicclassSplitContent{publicstaticstring[] SplitWords(stringcontent) {List<string> strList =newList<string>();Analyzeranalyzer =newPanGuAnalyzer();TokenStreamtokenStream = analyzer.TokenStream("",newStringReader(content)); Lucene.Net.Analysis.Tokentoken =null;while((token = tokenStream.Next()) !=null) {//Next继续分词 直至返回null strList.Add(token.TermText());//得到分词后结果 }returnstrList.ToArray(); }//需要添加PanGu.HighLight.dll的引用///<summary>///搜索结果高亮显示///</summary>///<param name="keyword">关键字</param>///<param name="content">搜索结果</param>///<returns>高亮后结果</returns>publicstaticstringHightLight(stringkeyword,stringcontent) {//创建HTMLFormatter,参数为高亮单词的前后缀 PanGu.HighLight.SimpleHTMLFormattersimpleHTMLFormatter =newPanGu.HighLight.SimpleHTMLFormatter("<font color=\"red\"><b>","</b></font>");//创建 Highlighter ,输入HTMLFormatter 和 盘古分词对象Semgent PanGu.HighLight.Highlighterhighlighter =newPanGu.HighLight.Highlighter(simpleHTMLFormatter,newSegment());//设置每个摘要段的字符数 highlighter.FragmentSize = 50;//获取最匹配的摘要段returnhighlighter.GetBestFragment(keyword, content); } }}

5、使用DictDll文件夹以及盘古分词的开发文档点击下载 实际应用中当然不会存在上图中的创建索引按钮 而且索引需随数据库数据同步更新 本章暂不介绍

本文地址http://www.chengxuyuans.com/asp.net/54071.html