借助 Lucene.Net 构建站内搜索引擎(上)

news/2024/5/17 3:39:28/文章来源:https://blog.csdn.net/weixin_34242658/article/details/86267282

前言:最近翻开了之前老杨(杨中科)的Lucene.Net站内搜索项目的教学视频,于是作为老杨脑残粉的我又跟着复习了一遍,学习途中做了一些笔记也就成了接下来您看到的这篇博文,仅仅是我的个人笔记,大神请呵呵一笑而过。相信做过站内搜索的.Net程序员应该对Lucene.Net不陌生,没做过的也许会问:就不是个查询嘛!为什么不能使用Like模糊查找呢?原因很简单:模糊查询的契合度太低,匹配关键字之间不能含有其他内容。最重要的是它会造成数据库全表扫描,效率低下,即使使用视图,也会造成数据库服务器"亚历山大"!因此,有必要了解一下Lucene.Net这个神器(也许现在早已不是)!

一、Lucene.Net简介

  

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

它的功能就是负责将文本数据按照某种分词算法进行切词,分词后的结果存储在索引库中,从索引库检索数据的速度灰常快。

  对以上加粗的词汇稍作下阐述:

  文本数据:Lucene.Net只能对文本信息进行检索,所以非文本信息要么转换成为文本信息,要么你就死了这条心吧!

  分词算法:将一句完整的话分解成若干词汇的算法  常见的一元分词(Lucene.Net内置就是一元分词,效率高,契合度低),二元分词,基于词库的分词算法(契合度高,效率低)...

  切词:将一句完整的话,按分词算法切成若干词语

       比如:"不是所有痞子都叫一毛" 这句话,如果根据一元分词算法则被切成: 不 是 所 有 痞 子 都 叫 一 毛 

     如果二元分词算法则切成: 不是 是所 所有 有痞 痞子 子都 都叫 叫一  一毛

     如果基于词库的算法有可能:不是 所有 痞子 都叫 一毛 具体看词库

  索引库:简单的理解成一个提供了全文检索功能的数据库,见下图所示:

二、几种分词的使用

  毫无疑问,Lucene.Net中最核心的内容就是分词,下面我们来体验一下基本的一元分词、二元分词以及基于词库分词的代表:盘古分词。首先,我们准备一个ASP.Net Web项目(这里使用的是WebForms技术),引入Lucene.Net和PanGu的dll,以及加入CJK分词的两个class(均在附件下载部分可以下载),分词演示Demo的项目结构如下图所示:

2.1 一元分词

  核心代码

    protected void btnGetSegmentation_Click(object sender, EventArgs e){string words = txtWords.Text;if (string.IsNullOrEmpty(words)){return;}Analyzer analyzer = new StandardAnalyzer(); // 标准分词 → 一元分词TokenStream tokenStream = analyzer.TokenStream("", new StringReader(words));Token token = null;while ((token = tokenStream.Next()) != null) // 只要还有词,就不返回null
        {string word = token.TermText(); // token.TermText() 取得当前分词Response.Write(word + "   |  ");}}
View Code

  效果演示

  可以看到一元分词将这句话的每个字都作为一个词组。前面提到,Lucene.Net维护着一个索引库,如果每个字都作为一个词组,那么索引库会变得尤为巨大,当然,分词的算法很简单,因此分词效率上会很高。

2.2 二元分词

  核心代码

    protected void btnGetSegmentation_Click(object sender, EventArgs e){string words = txtWords.Text;if (string.IsNullOrEmpty(words)){return;}Analyzer analyzer = new CJKAnalyzer(); // CJK分词 → 二元分词TokenStream tokenStream = analyzer.TokenStream("", new StringReader(words));Token token = null;while ((token = tokenStream.Next()) != null) // 只要还有词,就不返回null
        {string word = token.TermText(); // token.TermText() 取得当前分词Response.Write(word + "   |  ");}}
View Code

  效果演示

  可以看到二元分词通过将两个字作为一个词组,在词组的数量上较一元分词有了一定减少,但是分词的效果仍然不佳,比如:个来 这个分词结果就不符合语义,加入索引库也会是没什么机会会被用到。

2.3 盘古分词

  使用步骤

  (1)从PanGu开发包中取得PanGu.dll 与 PanGu.Lucenet.Analyzer.dll并加入到项目中

  (2)从PanGu开发包中取得Dict文件,并在Bin目录下创建一个Dict文件夹将Dict文件一起copy进去

  效果演示

  可以看到,使用基于词库的盘古分词进行分词后的效果较前两种好得太多,不过中间的“就跑不脱”这个词组优点不符合语义。刚刚提到盘古分词是基于词库的分词,因此我们可以到词库里边去为跑不脱(四川方言)添加一个词组到词库当中。

  分词扩展

  词库就是我们刚刚加入到Bin/Dict目录下的Dict文件,借助PanGu开发包中的DictManage.exe打开Dict文件,为跑不脱添加一个词组吧!

  (1)找到DictManage词库管理工具

  (2)打开我们的Dict文件并添加一个词组

  (3)在DictManage.exe中查找词组,然后保存,设置新版本号

  (4)重新打开页面查看分词结果

  修改词库之后的分词结果是不是更加符合我们得常规思维习惯了呢?

三、一个最简单的搜索引擎

3.1 搭建项目

  这个Demo需要模拟的场景是一个BBS论坛,每天BBS论坛都会新增很多新的帖子,每篇帖子都会存入数据库。从前面介绍可知,数据库中的内容也会转换为文本信息存入索引库,用户在前端搜索时会直接从索引库中获取查询结果。整个流程如下图所示:

  我们仍然在之前分词Demo的基础上实现这个小Demo,整个项目的结构如下图所示:

  好了,准备一个Web页面来展示吧:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="SearchEngineV1.aspx.cs" Inherits="Manulife.SearchEngine.LuceneNet.Views.SearchEngineV1" %><!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server"><meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><title>最简单的搜索引擎</title>
</head>
<body><form id="mainForm" runat="server"><div align="center"><asp:Button ID="btnCreateIndex" runat="server" Text="Create Index" OnClick="btnCreateIndex_Click" /><asp:Label ID="lblIndexStatus" runat="server" Visible="false" /><hr /><asp:TextBox ID="txtKeyWords" runat="server" Text="" Width="250"></asp:TextBox><asp:Button ID="btnGetSearchResult" runat="server" Text="Search" OnClick="btnGetSearchResult_Click" /><hr /></div><div><ul><asp:Repeater ID="rptSearchResult" runat="server"><ItemTemplate><li>Id:<%#Eval("Id") %><br /><%#Eval("Msg") %></li></ItemTemplate></asp:Repeater></ul></div></form>
</body>
</html>
View Code

  页面的结构如下图所示:

  页面很简单,只有两个button,一个textbox,以及一个repeater列表。其中:

  (1)Create Index : 点击该按钮会遍历文章/帖子的文本文件夹,对每个帖子进行分词,并将分词后的结果存入索引库;

  (2)Search :点击该按钮会将用户输入的关键词与索引库中的内容进行匹配,并将匹配后的结果显示在repeater列表中;

3.2 创建索引

  核心代码:

    /// <summary>/// 创建索引/// </summary>protected void btnCreateIndex_Click(object sender, EventArgs e){string indexPath = Context.Server.MapPath("~/Index"); // 索引文档保存位置FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory());bool isUpdate = IndexReader.IndexExists(directory); //判断索引库是否存在if (isUpdate){//  如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁//  Lucene.Net在写索引库之前会自动加锁,在close的时候会自动解锁//  不能多线程执行,只能处理意外被永远锁定的情况if (IndexWriter.IsLocked(directory)){IndexWriter.Unlock(directory);  //unlock:强制解锁,待优化
            }}//  创建向索引库写操作对象  IndexWriter(索引目录,指定使用盘古分词进行切词,最大写入长度限制)//  补充:使用IndexWriter打开directory时会自动对索引库文件上锁IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate,IndexWriter.MaxFieldLength.UNLIMITED);for (int i = 1000; i < 1100; i++){string txt = File.ReadAllText(Context.Server.MapPath("~/Upload/Articles/") + i + ".txt");//  一条Document相当于一条记录Document document = new Document();//  每个Document可以有自己的属性(字段),所有字段名都是自定义的,值都是string类型//  Field.Store.YES不仅要对文章进行分词记录,也要保存原文,就不用去数据库里查一次了document.Add(new Field("id", i.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED));//  需要进行全文检索的字段加 Field.Index. ANALYZED//  Field.Index.ANALYZED:指定文章内容按照分词后结果保存,否则无法实现后续的模糊查询 //  WITH_POSITIONS_OFFSETS:指示不仅保存分割后的词,还保存词之间的距离document.Add(new Field("msg", txt, Field.Store.YES, Field.Index.ANALYZED,Field.TermVector.WITH_POSITIONS_OFFSETS));//  防止重复索引,如果不存在则删除0条writer.DeleteDocuments(new Term("id", i.ToString()));// 防止已存在的数据 => delete from t where id=i//  把文档写入索引库
            writer.AddDocument(document);Console.WriteLine("索引{0}创建完毕", i.ToString());}writer.Close(); // Close后自动对索引库文件解锁directory.Close();  //  不要忘了Close,否则索引结果搜不到
lblIndexStatus.Text = "索引文件创建成功!";lblIndexStatus.Visible = true;btnCreateIndex.Enabled = false;}
View Code

  效果展示:

  应用场景:

  在BBS论坛新发布一个帖子的事件时,添加到数据库之后,再进行创建索引的操作,保存到索引库,这样帖子内容就存了两份,一份在数据库,一份在索引库。

3.2 获取结果

  核心代码:

    /// <summary>/// 获取搜索结果/// </summary>protected void btnGetSearchResult_Click(object sender, EventArgs e){string keyword = txtKeyWords.Text;string indexPath = Context.Server.MapPath("~/Index"); // 索引文档保存位置FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory());IndexReader reader = IndexReader.Open(directory, true);IndexSearcher searcher = new IndexSearcher(reader);// 查询条件PhraseQuery query = new PhraseQuery();// 等同于 where contains("msg",kw)query.Add(new Term("msg", keyword));// 两个词的距离大于100(经验值)就不放入搜索结果,因为距离太远相关度就不高了query.SetSlop(100);// TopScoreDocCollector:盛放查询结果的容器TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true);// 使用query这个查询条件进行搜索,搜索结果放入collectorsearcher.Search(query, null, collector);// 从查询结果中取出第m条到第n条的数据// collector.GetTotalHits()表示总的结果条数ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs;// 遍历查询结果IList<SearchResult> resultList = new List<SearchResult>();for (int i = 0; i < docs.Length; i++){// 拿到文档的id,因为Document可能非常占内存(DataSet和DataReader的区别)int docId = docs[i].doc;// 所以查询结果中只有id,具体内容需要二次查询// 根据id查询内容:放进去的是Document,查出来的还是DocumentDocument doc = searcher.Doc(docId);SearchResult result = new SearchResult();result.Id = Convert.ToInt32(doc.Get("id"));result.Msg = HighlightHelper.HighLight(keyword, doc.Get("msg"));resultList.Add(result);}// 绑定到RepeaterrptSearchResult.DataSource = resultList;rptSearchResult.DataBind();}
View Code

  效果展示:

附件下载

  Lucene.Net开发包 : 点我下载

  PanGu盘古分词开发包:点我下载

  简单搜索引擎Demo:点我下载

参考资料

(1)杨中科,《Lucene.Net站内搜索公开课》

(2)痞子一毛,《Lucene.Net》

(3)MeteorSeed,《使用Lucene.Net实现全文检索》

(4)Lucene.Net官方网站:http://lucenenet.apache.org/download.html

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_752892.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

php特级课---4、网站服务监控(常用网站服务监控软件有哪些)

php特级课---4、网站服务监控&#xff08;常用网站服务监控软件有哪些&#xff09; 一、总结 一句话总结&#xff1a;这些是架构师的知识 网络流量监控&#xff1a;cacti&#xff0c;mrtg 邮件报警系统&#xff1a;postfix 压力测试工具&#xff1a;Apache压力测试软件-ab&…

用户访问网站的过程

一.用户访问网站的过程 1.当用户访问一个网站时&#xff0c;都发生了什么&#xff1f; 概括如下&#xff1a; (1).利用DNS服务&#xff0c;将输入的域名解析为相应的IP地址 a.本地主机在输入域名后&#xff0c;会查询本地缓存信息和本地hosts b.本地主机会向远程LDNS服务器&…

go语言的服务发现、存储引擎、静态网站

go语言的服务发现、存储引擎、静态网站

03JavaScript程序设计修炼之道_2019-06-18_20-39-14_事件onloadonmouseoverout

02onload.html 03onmouseover&onmouseout.html 转载于:https://www.cnblogs.com/HiJackykun/p/11054778.html

修改chrome浏览器user-agent;在电脑上也能打开pad 或者 iphone 的专用网站

今天找了一个方法&#xff0c;可以修改chrome浏览器的user-agent &#xff1b;这样我们就 可以在电脑上访问 pad &#xff08; phone &#xff09;版的 gmail 或者 outlook 等。 1、 把chrome发送到桌面快捷方式&#xff0c;可以针对PAD 和PHONE 多建一些快捷方式。 2、 在快…

更好的网站 - 你知道网站被屏蔽了吗?

网站所有者应该知道的 - 你的网站被屏蔽了&#xff1f;

php网站点击特效代码,【seo优化】鼠标点击网站特效生成原理(附代码)

当前位置&#xff1a;首页 > SEO干货 正文2019年11月19日admin有朋友经常问&#xff0c;网站上点击出现爱国字眼是怎么做出来的&#xff0c;鼠标点击就显示“富强、民主、和谐”等24个词语&#xff0c;这样鼠标点击特效。下面来分享一下如何实现的。1&#xff0c;效果如下&…

树状结构搜索功能_事竟成科技,科普seo搜索引擎优化的做法

随着互联网的不断发展与普及&#xff0c;SEO成为一个企业宣传&#xff0c;选择推广的必要渠道&#xff0c;网络不同于其他大众传媒&#xff0c;网络具有广泛性&#xff0c;没有地域、行业、职务、生活背景、经济条件等等的限制。所以SEO排名直接影响了企业被展示的机会。要做优…

seo技术_基础知识_网站pr值的意义_SEO网站优化的通用策略有哪些!

seo不仅一种提高网站的权重的手段&#xff0c;更是网站运营的策略&#xff0c;seo策略直接关系到网站发展&#xff0c;不同的网站在执行上也略有差异&#xff0c;但是在还是有优化的程序都大同小异&#xff0c;下面为你介绍下网站优化通用的策略。第一步:分析对手1.确定你的竞争…

seo 伪原创_英文SEO采集伪原创软件Kontent Machine注册使用实战教程!

做跨境电商的童鞋们&#xff0c;总会遇到这样的问题&#xff1a;我们英文很差或者一般&#xff0c;无法写出精彩的原创英文文章进行SEO推广。这确实是很难的&#xff0c;即使许多商务英语过硬的都难以写出高质量英文原创文章&#xff0c;因为你不熟悉他们的生活环境&#xff0c…

php mysql 网站性能分析工具_MySQL profiling性能分析工具

MySQL 的 Query Profiler 是一个使用非常方便的 Query 诊断分析工具&#xff0c;通过该工具可以获取一条Query 在整个执行过程中 多种资源的简述&#xff1a;MySQL 的 Query Profiler 是一个使用非常方便的 Query 诊断分析工具&#xff0c;通过该工具可以获取一条Query 在整个执…

找网络高手联系方式_上海网络营销之SEO推广:原来SEO这样做才最有效

网络营销中&#xff0c;SEO因为成本低、效率高、获取的客户质量有保障&#xff0c;一直是各大企业最信赖、也最认可的一种营销方式。可是大多数的企业&#xff0c;却不懂如何运用SEO推广营销&#xff0c;更有甚者&#xff0c;贪图一时的利益&#xff0c;被某些黑心的互联网公司…

购买实体网站服务器,实体服务器购买

实体服务器购买 内容精选换一换已购买弹性云服务器ECS&#xff0c;具体操作请参见《弹性云服务器快速入门》。已购买弹性公网IP&#xff0c;并绑定到购买的弹性云服务器ECS上&#xff0c;具体操作请参见《弹性公网IP快速入门》登录AOM控制台&#xff0c;在左侧导航栏中选择“配…

辰光php客服系统源码_角点科技:购买的商城网站源码靠谱吗?

购买的商城网站源码靠谱吗?当下&#xff0c;商城网站开发越来越火&#xff0c;很多企业都想要开发一款商城网站&#xff0c;而不少企业在选择源码时&#xff0c;忽略了商城网站源码的重要性&#xff0c;从而造成损失&#xff0c;下面角点科技来为你讲解商城源码。1、商城网站源…

seo日常工作表_SEO日常工作内容主要有哪些

L氪迹在这里详细讲解SEO日常工作的主要内容以及各类工作内容重要性、频率和作用&#xff0c;下面进入主题:第一项:查看网站基本数据工作内容简述:快照、首页及其他页面重点词排名、整站关键词排名、权重、收录、友链稳定性&#xff1b;重要性:重要&#xff1b;频率:每天作用:检…

filestream写文件导致无法打开_网站配置文件删除导致网站无法正常访问

今天视频在线企业名录网站升级&#xff0c;升级后访问提示信息如下&#xff1a;500代码是内部服务器错误&#xff0c;同一个服务器的其他网站可以正常打开访问&#xff0c;这个网站https://www.0086freecall.com ,是专门做视频在线企业名录的一个网站&#xff0c;因为网站改版升…

404。服务器中未发现请求的 url。_SEO优化案例:服务器连接错误怎样解决?

降低动态网页加载太多的要求。如果网站提供的多个URL相同内容的&#xff0c;将被视为提供动态内容。动态网页的响应工作时间我们可能会很长&#xff0c;并会因此可以导致超时问题。或者&#xff0c;服务器可能会返回超载状态&#xff0c;蜘蛛抓取网站要求百度放缓。一般来说&am…

java仿qq_「java qq」仿QQ聊天软件java实现(一) - seo实验室

java qq之前学java通信的时候写过简单的通信程序&#xff0c;但比较简陋&#xff0c;于是重新写了一个仿照QQ的聊天软件&#xff0c;主要在界面上做了优化&#xff0c;增加了一些功能。实现的功能有注册、登录、好友列表、分组、黑名单、添加好友、群列表、创建群、添加群、聊天…

苹果结构体系不匹配_研究:拥有苹果产品的用户在交友网站更具吸引力

IT之家 8 月 28 日消息 根据 Comparemymobile.com 的一项新研究显示&#xff0c;在约会资料照片中明显展示苹果产品的用户比那些使用其它品牌产品的用户能多获得 76% 的匹配率。为了确定这个有趣的事实&#xff0c;Comparemymobile.com 花了不少心思&#xff0c;他们在全球多个…

python开启简易网站服务器

如果在局域网中想把当前主机作为服务器&#xff0c;让其他主机下载文件&#xff0c;可在目标文件目录下使用一下命令 python3 -m http.server然后其他主机在网页登录 ip:8000&#xff0c;ip为服务器的ip地址&#xff0c;即可访问到服务器的目录&#xff0c;进行文件下载