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

news/2024/5/9 6:49:41/文章来源:https://blog.csdn.net/weixin_30594001/article/details/97213258

前言:最近翻开了之前老杨(杨中科)的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

 

转载于:https://www.cnblogs.com/edisonchou/p/5348625.html

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

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

相关文章

github可以刷星吗_教你一招即使没有服务器,也可以免费拥有自己的专属网站

现在固定公网IP资源稀缺&#xff0c;虽然说IPv6已经开始使用&#xff0c;但是用的人并不多&#xff0c;大部分用的都是IPv4&#xff0c;想要搞一个自己网站&#xff0c;首先就是需要一个固定的公网IP&#xff0c;但是每年却需要缴纳不少的money&#xff0c;而且服务器的成本&am…

oss批量上传工具_使用阿里云OSS与github actions自动部署个人网站

OSS 即 object storage service&#xff0c;对象存储服务。我们可以通过阿里云的 OSS 来托管自己的前端应用&#xff0c;个人网站或者博客在 [使用 netlify 托管你的前端应用]( "使用 netlify 托管你的前端应用") 中我也介绍到另一种专业的网站托管服务平台 netlify。…

Java旅游网站源码+页面

旅游网站[源码前后端页面] 在线预览&#xff1a;旅游网站TourismPro 账号&#xff1a;朱利尔&#xff0c;密码&#xff1a;123 管理员账号&#xff1a;admin&#xff0c;密码&#xff1a;123456 Github开源地址&#xff1a;https://github.com/jwwam/tourismPro下载 or 部署问…

毕业设计之旅游网站

GitHub地址&#xff1a; https://github.com/jwwam/tourism.git 最新的旅游网站地址&#xff1a;旅游网站(SpringBoot版本) 本文是SpringMVC版本&#xff0c;spring boot版本请点击上面的链接下载查看&#xff01;&#xff01;&#xff01; 框架技术&#xff1a; SpringMVC Spr…

Java招聘网站源码+页面

招聘网站[源码前后端页面] 毕业设计免费开源系列&#xff1a; Java旅游网站源码页面&#xff08;景点查看、评论、酒店查看、预定&#xff0c;旅游线路推荐...&#xff09; Java招聘网站源码页面&#xff08;职位查看、评论、简历发布、投递、结果、企业管理&#xff0c;职位…

composer 完整路径才能访问_谷歌浏览器打算隐藏网站地址路径URL!

谷歌浏览器打击钓鱼网站再出新招。内测地址栏开始隐藏网站全部路径 仅显示网站域名。了解到&#xff0c;域名和URL是一个网站最基本的对外展示信息&#xff0c;可以让用户快速知道网站的内容和信息。但是&#xff0c;有时也会被部分黑产和诈骗分子利用&#xff0c;进行使用带有…

vue seo关键词设置_简单设置SEO关键词

SEO是指在了解搜索引擎自然排名机制的基础之上&#xff0c;对网站进行内部及外部的调整优化&#xff0c;改进网站在搜索引擎中关键词的自然排名&#xff0c;获得更多的展现量&#xff0c;吸引更多目标客户点击访问网站&#xff0c;从而达到互联网营销及品牌建设的目标。搜索引擎…

隔一段时间查找一次 golang_资源盘点 | 表情包查找网站

“哎呀气氛有点尴尬&#xff0c;用个表情包缓解一下氛围”“今天这么高兴&#xff0c;发个表情包应应景”“哎他们在夸我&#xff0c;不知道用语言怎么表达&#xff0c;用表情包说出我的心声吧”......上面的场景相信大家都不陌生&#xff0c;有着活跃气氛、避免尴尬等效果的表…

greenfoot推箱子怎么做_SEO排名做上去后该怎么维护-top推

SEO主要是优化搜索引擎的工作&#xff0c;但是搜索引擎的优化是很复杂的工作&#xff0c;因为当我们做排名上去以后&#xff0c;远远没有结束&#xff0c;我们还要继续的维护&#xff0c;否则一旦掉意轻心&#xff0c;排名就有可能掉下来&#xff0c;那么怎样去进行后期的排名维…

服务器节点代表网站的什么,云服务器的节点是什么意思

云服务器的节点是什么意思 内容精选换一换本章介绍了如何添加运行CCE集群的节点池以及对节点池执行操作。要了解节点池的工作原理&#xff0c;请参阅节点池概述。将节点池添加到现有集群。计费模式&#xff1a;节点池仅支持按需计费的计费模式&#xff0c;该模式将根据实际使用…

网络不稳定的原因_网站优化分析企业网站排名不稳定的原因!

发现现在很多企业都在做SEO优化&#xff0c;在这过程中&#xff0c;网站排名有波动&#xff0c;不稳定&#xff0c;这是我们经常会遇到的问题&#xff0c;排名的不稳定就会给网站流量带来影响&#xff0c;那么接下来世纪兴小编为大家详细介绍一下网站排名不稳定的原因。一、企业…

lisp图库不显示缩略图_从免费图库、影片到字体,这个网站全包了!

距离上一次介绍 The Stocks 已经超过五年&#xff0c;前段时间无意间浏览到这个网站&#xff0c;才想起我以前好像也写过文章&#xff0c;不过网站现在变得不太一样而且内容又更完整了&#xff0c;非常推荐加入收藏&#xff0c;因为真的很方便。如果你还不知道 The Stocks&…

和php页面_WordPress网站搬家之后访问首页和后台php页面变下载怎么办?

因为服务器到期的缘故老古购买了一台新的服务器来运行测试站&#xff0c;并且使用宝塔面板的搬家工具实现站点迁移&#xff0c;详见『宝塔 Linux 面板使用一键迁移插件实现网站快速搬家图文教程』。不过成功将域名解析为新服务器 IP 地址后&#xff0c;访问 WordPress 站点首页…

2020-06-29-----1.2网站与网页

1.2网站与网页 1.概念&#xff08;1&#xff09;网站&#xff08;web site&#xff09;&#xff1a;互联网上用户展示特定内容的相关网页的集合。 &#xff08;2&#xff09;网页&#xff08;web page&#xff09;&#xff1a;网站中的一页&#xff0c;一个网站中的网页通过“…

[js] mouseover mouseout和mouseenter mouseleave的区别

mouseover mouseout&#xff1a;在鼠标进入或者离开作用元素或者其子元素时&#xff0c;都会触发 在进入son的时候&#xff0c;因为离开了father&#xff0c;所以会触发一次mouseout&#xff0c;同理&#xff0c;在再次进入father的时候&#xff0c;也因为离开了son&#xff0c…

空间后方交会c++程序_全站仪后方交会建站

先看全站仪后方交会建站常见的操作步骤&#xff1a;首先仪器随便架在一个方便的地方。选择你所测量需要的那个坐标系&#xff0c;再进入新点功能。用后方交会法。就可以采点了。先照准你已知的第一个点&#xff0c;再照准已知第2个点。坐标系就建好了。然后就可以碎部测量或放样…

天气预报 源码 实战_渭源县路园镇农特产品登上了天气预报和天气网站

终于等到你还好没放弃 | 渭源广播电视渭源县路园镇辣椒登上了中国天气网“一朵金丝皇菊价格能卖到10元&#xff0c;一亩辣椒的产值能达到1万元&#xff0c;还有娃娃菜&#xff0c;这些农特产品去年已经出省&#xff0c;卖到了山东、上海、广东等地。”6月3日下午&#xff0c;…

建站用什么cms_哪个CMS建站系统更利于seo优化

提到网站建设&#xff0c;最先想到的就是下载CMS建站系统来建站&#xff0c;当然不乏还有会外包定制的方式&#xff0c;那成本就另说了&#xff0c;采用CMS系统建站优势便是开发省时且价格给力&#xff0c;而且功能完整&#xff0c;比定制开发的体验性&#xff0c;功能性方面都…

后台管理系统静态页面_建站指南丨选择动态网站还是静态网站好?

无论是个人网站还是企业网站&#xff0c;在网站建设时&#xff0c;需要有一个决策&#xff0c;就是这个网站到底是做动态网站&#xff0c;还是做静态网站好&#xff1f;它们分别有什么特点&#xff0c;如何去选择&#xff1f;下面我们一起来聊聊。关于 静态网站静态网站&#…

Chrome 无法从该网站添加应用、扩展程序和用户脚本问题解决

1、报错场景 我所使用的是公司电脑&#xff0c;chrome版本是94.0.4606.54&#xff08;正式版本&#xff09;&#xff0c;今天添加插件时&#xff0c;出现了上图所述的提示&#xff0c;经过翻阅资料得以解决&#xff0c;在此记录~ 2、解决方案 在页面URL输入网址&#xff1a;c…