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.Net为2.9
在项目下新建Dict文件以及dll文件包含Lucene与盘古分词所需的组件及文件信息如图示
1、项目添加对dll文件夹中四个程序集的引用
2、添加图书搜索页面SearchBlogs.aspx代码如下:
<% @ Page Language="C#" AutoEventWireup="true" CodeBehind="SearchBook.aspx.cs" Inherits ="BookShop.Web.SearchBook" %> <! DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> < html xmlns ="http://www.w3.org/1999/xhtml"> < head runat ="server"> <title ></ title> </ head> < body> <form id="form1" runat="server"> <asp : TextBox ID ="txtSerach" runat ="server" Width ="494px"></asp : TextBox> <asp : Button ID ="btnSearch" runat ="server" onclick ="btnSearch_Click" Text ="搜索" /> <asp : Button ID ="CreateView" runat ="server" onclick ="CreateView_Click" Text ="创建索引" /> <div > < asp: Repeater ID ="Repeater1" runat ="server"> < ItemTemplate> < table> < tbody> < tr> < td style ="font-size : small; color: red" width="650"> < a id ="link_prd_name" href ='<% # Eval("Id","/book.aspx?id={0}") %> ' target ="_blank" name ="link_prd_name"> <%# Eval( "Title") %> </ a> </ td> </ tr> < tr> < td align ="left"> < span style ="font-size : 12px; line-height: 20px"> <%# Eval( "ContentDescription") %></ span > </ td> </ tr> </ tbody> </ table> </ ItemTemplate> </ asp: Repeater > </div > </form > </ body> </ html>
3、后台调用
using System; using System.Collections.Generic; using System.IO; using BookShop.BLL; using Lucene.Net.Analysis.PanGu; using Lucene.Net.Documents; using Lucene.Net.Index; using Lucene.Net.Search; using Lucene.Net.Store; namespace BookShop.Web { public partial class SearchBook : System.Web.UI. Page { protected void Page_Load( object sender, EventArgs e) { } //创建索引事件 可以防止在用户点击查询事件前执行 去掉页面中的"创建索引"按钮 protected void CreateView_Click( object sender, EventArgs e) { string indexPath = @"C:\luceneTest" ; //索引文档保存位置 FSDirectory directory = FSDirectory .Open( new DirectoryInfo (indexPath), new NativeFSLockFactory ()); bool isUpdate = IndexReader .IndexExists(directory); //是否存在索引库文件夹以及索引库特征文件 if (isUpdate) { //如果索引目录被锁定(比如索引过程中程序异常退出或另一进程在操作),则解锁 if (IndexWriter .IsLocked(directory)) { IndexWriter .Unlock(directory); } } //创建索引库对象 new PanGuAnalyzer()指定使用盘古分词进行切词 IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer (), !isUpdate, Lucene.Net.Index.IndexWriter . MaxFieldLength.UNLIMITED); BooksManager bookManager = new BooksManager(); List <Model.Books > bookList = bookManager.GetModelList( "" ); foreach (var book in bookList) { Document document = new Document(); //new 一篇文档 对象 //所有字段的值都将以字符串类型保存 //Field.Store表示是否保存字段原值。指定Field.Store.YE的字段在检索时才能用document.Get取出来值 NOT_ANALYZED指定不按照分词后的结果保存 document.Add( new Field ( "id", book.Id.ToString(), Field .Store .YES, Field .Index .NOT_ANALYZED)); document.Add( new Field ( "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( new Field ( "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,否则索引结果搜不到 } protected void btnSearch_Click( object sender, EventArgs e) { string indexPath = @"C:\luceneTest" ; FSDirectory directory = FSDirectory .Open( new DirectoryInfo (indexPath), new NoLockFactory ()); IndexReader reader = IndexReader .Open(directory, true); IndexSearcher searcher = new IndexSearcher(reader); //Index:索引 //搜索条件 PhraseQuery query = new PhraseQuery(); //把用户输入的关键字进行分词 foreach (string word in Common.SplitContent .SplitWords(txtSerach.Text.ToLower())) { query.Add( new Term ( "content", word)); } query.SetSlop(100); //指定关键词相隔最大距离 //TopScoreDocCollector盛放查询结果的容器 TopScoreDocCollector collector = 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 = new List <Model.Books >(); for (int i = 0; i < docs.Length; i++) { //需要获得文档的详细内容的时候通过searcher.Doc来根据文档id来获得文档的详细内容对象Document int docId = docs[i].doc;//得到查询结果文档的id(Lucene内部分配的id) Document doc = searcher.Doc(docId);//找到文档id对应的文档详细信息 Model. Books book = new Model. 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类
using System.Collections.Generic; using System.IO; using Lucene.Net.Analysis; using Lucene.Net.Analysis.PanGu; using PanGu; namespace BookShop.Web.Common { public class SplitContent { public static string[] SplitWords( string content) { List <string > strList = new List <string >(); Analyzer analyzer = new PanGuAnalyzer(); TokenStream tokenStream = analyzer.TokenStream("" , new StringReader (content)); Lucene.Net.Analysis. Token token = null ; while ((token = tokenStream.Next()) != null ) { //Next继续分词 直至返回null strList.Add(token.TermText()); //得到分词后结果 } return strList.ToArray(); } //需要添加PanGu.HighLight.dll的引用 /// <summary> /// 搜索结果高亮显示 /// </summary> /// <param name="keyword"> 关键字 </param> /// <param name="content"> 搜索结果 </param> /// <returns> 高亮后结果 </returns> public static string HightLight( string keyword, string content) { //创建HTMLFormatter,参数为高亮单词的前后缀 PanGu.HighLight. SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter ( "<font color=\"red\"><b>", "</b></font>" ); //创建 Highlighter ,输入HTMLFormatter 和 盘古分词对象Semgent PanGu.HighLight. Highlighter highlighter = new PanGu.HighLight.Highlighter (simpleHTMLFormatter, new Segment ()); //设置每个摘要段的字符数 highlighter.FragmentSize = 50; //获取最匹配的摘要段 return highlighter.GetBestFragment(keyword, content); } } }
5、使用Dict、Dll文件夹以及盘古分词的开发文档点击下载 实际应用中当然不会存在上图中的创建索引按钮 而且索引需随数据库数据同步更新 本章暂不介绍