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

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

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

Lucene简介

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

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

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

Q:分词

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

以下是运行图解:

Lucene.Net与盘古分词实现站内搜索_第1张图片

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

运行界面效果如下:

Lucene.Net与盘古分词实现站内搜索_第2张图片

 

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

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

Lucene.Net与盘古分词实现站内搜索_第3张图片

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(1000true );
            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、使用DictDll文件夹以及盘古分词的开发文档点击下载        实际应用中当然不会存在上图中的创建索引按钮  而且索引需随数据库数据同步更新   本章暂不介绍

你可能感兴趣的:(Lucene.Net与盘古分词实现站内搜索)