使用Lucene.Net实现全文检索

Lucene.net是Lucene的.net移植版本,是一个开源的全文检索引擎开发包,即它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎。

开发人员可以基于Lucene.net实现全文检索的功能。

Lucene.net是Apache软件基金会赞助的开源项目,基于Apache License协议。

Lucene.net并不是一个爬行搜索引擎,也不会自动地索引内容。我们得先将要索引的文档中的文本抽取出来,然后再将其加到Lucene.net索引中。标准的步骤是先初始化一个Analyzer、打开一个IndexWriter、然后再将文档一个接一个地加进去。一旦完成这些步骤,索引就可以在关闭前得到优化,同时所做的改变也会生效。这个过程可能比开发者习惯的方式更加手工化一些,但却在数据的索引上给予你更多的灵活性,而且其效率也很高。

获取索引目录

    /// <summary>/// 获取索引目录/// </summary>/// <param name="index">索引类型</param>/// <returns>索引目录</returns>private LcStore.Directory GetLuceneDirectory(IndexType index){var indexPath = string.Empty;try{var dirPath = ConfigHelper.GetAppSetting("LuceneIndexPath");var indexName = Enum.EnumHelper.GetEnumDescription(index);indexPath = Path.Combine(dirPath, indexName);return LcStore.FSDirectory.Open(indexPath);}catch (Exception ex){NLogger.Write($"获取索引目录失败" + Environment.NewLine +$"路径:{indexPath}" + Environment.NewLine +$"异常信息:{ex}","Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("获取索引目录异常,详情请查看相关日志");}}#endregion 获取目录

盘古分词

   /// <summary>/// 盘古分词/// </summary>/// <param name="keyword">语句</param>/// <returns>词组集合</returns>public string[] GetSplitKeywords(string keyword){try{string ret = null;var reader = new StringReader(keyword);var ts = PanguAnalyzer.TokenStream(keyword, reader);var hasNext = ts.IncrementToken();Lucene.Net.Analysis.Tokenattributes.ITermAttribute ita;while (hasNext){ita = ts.GetAttribute<Lucene.Net.Analysis.Tokenattributes.ITermAttribute>();ret += ita.Term + "|";hasNext = ts.IncrementToken();}ts.CloneAttributes();reader.Close();PanguAnalyzer.Close();if (string.IsNullOrWhiteSpace(ret)) return null;ret = ret.Substring(0, ret.Length - 1);return ret.Split('|');}catch (Exception ex){NLogger.Write("分词异常" + Environment.NewLine +$"关键词:{keyword}" + Environment.NewLine +$"异常信息:{ex}","Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("分词出现异常,详情请查看相关日志");}}#endregion 分词

创建索引或追加索引

     /// <summary>/// 创建索引或追加索引/// </summary>/// <param name="dataList">数据集合</param>/// <param name="index">索引类型</param>public void CreateOrAppendIndexes(List<Document> dataList, IndexType index){if (dataList == null || dataList.Count == 0)return;IndexWriter writer;var directory = GetLuceneDirectory(index);try{//false表示追加(true表示删除之前的重新写入)writer = new IndexWriter(directory, PanguAnalyzer, false, IndexWriter.MaxFieldLength.LIMITED);}catch{//false表示追加(true表示删除之前的重新写入)writer = new IndexWriter(directory, PanguAnalyzer, true, IndexWriter.MaxFieldLength.LIMITED);}writer.MergeFactor = 1000;//writer.SetMaxBufferedDocs(1000);foreach (var doc in dataList){writer.AddDocument(doc);}writer.Optimize();writer.Dispose();directory.Dispose();}

完整代码

 /// <summary>/// Lucene搜索引擎帮助类/// </summary>public class LuceneHelper{/// <summary>/// 私有构造函数/// </summary>private LuceneHelper(){}#region 属性private static LuceneHelper _instance;/// <summary>/// 单一实例/// </summary>public static LuceneHelper Instance => _instance ?? (_instance = new LuceneHelper());private Analyzer _analyzer;/// <summary>/// 分析器/// </summary>private Analyzer PanguAnalyzer => _analyzer ?? (_analyzer = new PanGuAnalyzer());#endregion 属性#region 获取目录/// <summary>/// 获取索引目录/// </summary>/// <param name="index">索引类型</param>/// <returns>索引目录</returns>private LcStore.Directory GetLuceneDirectory(IndexType index){var indexPath = string.Empty;try{var dirPath = ConfigHelper.GetAppSetting("LuceneIndexPath");var indexName = Enum.EnumHelper.GetEnumDescription(index);indexPath = Path.Combine(dirPath, indexName);return LcStore.FSDirectory.Open(indexPath);}catch (Exception ex){NLogger.Write($"获取索引目录失败" + Environment.NewLine +$"路径:{indexPath}" + Environment.NewLine +$"异常信息:{ex}","Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("获取索引目录异常,详情请查看相关日志");}}#endregion 获取目录#region 分词/// <summary>/// 盘古分词/// </summary>/// <param name="keyword">语句</param>/// <returns>词组集合</returns>public string[] GetSplitKeywords(string keyword){try{string ret = null;var reader = new StringReader(keyword);var ts = PanguAnalyzer.TokenStream(keyword, reader);var hasNext = ts.IncrementToken();Lucene.Net.Analysis.Tokenattributes.ITermAttribute ita;while (hasNext){ita = ts.GetAttribute<Lucene.Net.Analysis.Tokenattributes.ITermAttribute>();ret += ita.Term + "|";hasNext = ts.IncrementToken();}ts.CloneAttributes();reader.Close();PanguAnalyzer.Close();if (string.IsNullOrWhiteSpace(ret)) return null;ret = ret.Substring(0, ret.Length - 1);return ret.Split('|');}catch (Exception ex){NLogger.Write("分词异常" + Environment.NewLine +$"关键词:{keyword}" + Environment.NewLine +$"异常信息:{ex}","Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("分词出现异常,详情请查看相关日志");}}#endregion 分词#region 索引增删改查/// <summary>/// 创建索引或追加索引/// </summary>/// <param name="dataList">数据集合</param>/// <param name="index">索引类型</param>public void CreateOrAppendIndexes(List<Document> dataList, IndexType index){if (dataList == null || dataList.Count == 0)return;IndexWriter writer;var directory = GetLuceneDirectory(index);try{//false表示追加(true表示删除之前的重新写入)writer = new IndexWriter(directory, PanguAnalyzer, false, IndexWriter.MaxFieldLength.LIMITED);}catch{//false表示追加(true表示删除之前的重新写入)writer = new IndexWriter(directory, PanguAnalyzer, true, IndexWriter.MaxFieldLength.LIMITED);}writer.MergeFactor = 1000;//writer.SetMaxBufferedDocs(1000);foreach (var doc in dataList){writer.AddDocument(doc);}writer.Optimize();writer.Dispose();directory.Dispose();}/// <summary>/// 删除索引/// </summary>/// <param name="field">字段名</param>/// <param name="value">字段值</param>/// <param name="index">索引类型</param>public void DeleteIndexes(string field, string value, IndexType index){IndexWriter writer = null;var directory = GetLuceneDirectory(index);try{writer = new IndexWriter(directory, PanguAnalyzer, false, IndexWriter.MaxFieldLength.LIMITED);var term = new Term(field, value);writer.DeleteDocuments(term);//var isSuccess = writer.HasDeletions();writer.Optimize();}catch (Exception ex){NLogger.Write("删除索引异常" + Environment.NewLine +$"异常信息:{ex}", "Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("删除索引异常,详情请查看相关日志");}finally{writer?.Dispose();directory?.Dispose();}}/// <summary>/// 更新索引;这里实际上是先删除原有索引,在创建新索引。/// 所以在更新索引时,一定要确保传入的Document的所有字段都有值/// 否则将会被置为空/// </summary>/// <param name="field">字段名</param>/// <param name="value">字段值</param>/// <param name="doc">文档</param>/// <param name="index">索引类型</param>public void UpdateIndexes(string field, string value, Document doc, IndexType index){IndexWriter writer = null;var directory = GetLuceneDirectory(index);try{writer = new IndexWriter(directory, PanguAnalyzer, false, IndexWriter.MaxFieldLength.LIMITED);var term = new Term(field, value);writer.UpdateDocument(term, doc);}catch (Exception ex){NLogger.Write("更新索引异常" + Environment.NewLine +$"异常信息:{ex}", "Lucene", "x", "x",CustomException.UnknownError, CustomLogLevel.Error);throw new Exception("更新索引异常,详情请查看相关日志");}finally{writer?.Dispose();directory?.Dispose();}}#endregion 索引增删改查#region 查询/// <summary>/// 查询/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="fields">条件字段</param>/// <param name="keywords">关键词组</param>/// <param name="index">索引类型</param>/// <param name="sort">排序,可为空</param>/// <param name="count">读取数量</param>/// <returns>结果集</returns>public List<T> Search<T>(string[] fields,string[] keywords,IndexType index,Sort sort,int count) where T : new(){if (fields == null || fields.Length == 0)return null;if (keywords == null || keywords.Length == 0)return null;//索引目录var directory = GetLuceneDirectory(index);//查询条件var boolQuery = GetQuery(fields, keywords);//索引查询器var searcher = new IndexSearcher(directory, true);TopDocs docs;if (sort != null)docs = searcher.Search(boolQuery, null, count, sort);elsedocs = searcher.Search(boolQuery, count);if (docs == null || docs.TotalHits == 0)return null;//文档集合var docList = docs.ScoreDocs.Select(sd => searcher.Doc(sd.Doc)).ToList();//反射赋值var list = ConvertDocToObj<T>(docList);searcher.Dispose();directory.Dispose();return list;}/// <summary>/// 查询分页数据(指定排序方式)/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="fields">条件字段</param>/// <param name="keywords">关键词组</param>/// <param name="index">索引类型</param>/// <param name="sort">排序,必填</param>/// <param name="pageNumber">页码</param>/// <param name="pageSize">页数</param>/// <returns>结果集</returns>public PagedResult<List<T>> SearchByPaged<T>(string[] fields,string[] keywords,IndexType index,Sort sort,int pageNumber = 1,int pageSize = 20) where T : new(){if (fields == null || fields.Length == 0)return null;if (keywords == null || keywords.Length == 0)return null;//索引目录var directory = GetLuceneDirectory(index);//查询条件var boolQuery = GetQuery(fields, keywords);var collector = TopFieldCollector.Create(sort, pageNumber * pageSize, false, false, false, false);var searcher = new IndexSearcher(directory, true);searcher.Search(boolQuery, collector);if (collector == null || collector.TotalHits == 0)return null;//分页var start = (pageNumber - 1) * pageSize;var limit = pageSize;var hits = collector.TopDocs(start, limit).ScoreDocs;var totalCount = collector.TotalHits;var docList = hits.Select(sd => searcher.Doc(sd.Doc)).ToList();//反射赋值var list = ConvertDocToObj<T>(docList);searcher.Dispose();directory.Dispose();return new PagedResult<List<T>>{Total = totalCount,Result = list};}/// <summary>/// 查询分页数据(默认排序方式)/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="fields">条件字段</param>/// <param name="keywords">关键词组</param>/// <param name="index">索引类型</param>/// <param name="pageNumber">页码</param>/// <param name="pageSize">页数</param>/// <returns>结果集</returns>public PagedResult<List<T>> SearchByPaged<T>(string[] fields,string[] keywords,IndexType index,int pageNumber = 1,int pageSize = 20) where T : new(){if (fields == null || fields.Length == 0)return null;if (keywords == null || keywords.Length == 0)return null;//索引目录var directory = GetLuceneDirectory(index);//查询条件var boolQuery = GetQuery(fields, keywords);var collector = TopScoreDocCollector.Create(pageNumber * pageSize, false);var searcher = new IndexSearcher(directory, true);searcher.Search(boolQuery, collector);if (collector == null || collector.TotalHits == 0)return null;//分页var start = (pageNumber - 1) * pageSize;var limit = pageSize;var hits = collector.TopDocs(start, limit).ScoreDocs;var totalCount = collector.TotalHits;var docList = hits.Select(sd => searcher.Doc(sd.Doc)).ToList();//反射赋值var list = ConvertDocToObj<T>(docList);searcher.Dispose();directory.Dispose();return new PagedResult<List<T>>{Total = totalCount,Result = list};}/// <summary>/// 查询分页数据(默认排序方式)/// </summary>/// <param name="fields">条件字段</param>/// <param name="keywords">关键词组</param>/// <param name="index">索引类型</param>/// <returns>结果集</returns>public int GetTotla(string[] fields, string[] keywords, IndexType index){if (fields == null || fields.Length == 0)return 0;if (keywords == null || keywords.Length == 0)return 0;//索引目录var directory = GetLuceneDirectory(index);//查询条件var boolQuery = GetQuery(fields, keywords);var collector = TopScoreDocCollector.Create(20, false);var searcher = new IndexSearcher(directory, true);searcher.Search(boolQuery, collector);if (collector == null || collector.TotalHits == 0)return 0;searcher.Dispose();directory.Dispose();return collector.TotalHits;}/// <summary>/// 文档转换为对象/// </summary>/// <typeparam name="T">实体类型</typeparam>/// <param name="docList">文档集合</param>/// <returns>对象集合</returns>private List<T> ConvertDocToObj<T>(List<Document> docList) where T : new(){var type = typeof(T);var propertyList = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);var list = new List<T>();var firstDoc = docList.First();var fieldNames = firstDoc.GetFields().Select(x => x.Name).ToList();foreach (var doc in docList){var tObj = new T();foreach (var pInfo in propertyList){var name = pInfo.Name;if (fieldNames.Any(x => x.ToLower() == name.ToLower())){SetValue<T>(pInfo, tObj, doc, name);}}list.Add(tObj);}return list;}/// <summary>/// 获取查询条件/// </summary>/// <param name="fields">条件字段</param>/// <param name="keywords">关键词组</param>/// <returns></returns>private BooleanQuery GetQuery(string[] fields, string[] keywords){var boolQuery = new BooleanQuery();foreach (var field in fields){foreach (var keyword in keywords){var t = new TermQuery(new Term(field, keyword));boolQuery.Add(t, Occur.SHOULD);}}return boolQuery;}#endregion 查询private void SetValue<T>(PropertyInfo pInfo, T tObj, Document doc, string name){var pType = pInfo.PropertyType.Name;switch (pType){case "String":pInfo.SetValue(tObj, doc.Get(name), null);break;case "Int32":pInfo.SetValue(tObj, GetInt(doc.Get(name)), null);break;case "Boolean":pInfo.SetValue(tObj, GetBool(doc.Get(name)), null);break;case "DateTime":pInfo.SetValue(tObj, GetDate(doc.Get(name)), null);break;case "Double":pInfo.SetValue(tObj, GetDouble(doc.Get(name)), null);break;case "Single":pInfo.SetValue(tObj, GetFloat(doc.Get(name)), null);break;case "Decimal":pInfo.SetValue(tObj, GetDecimal(doc.Get(name)), null);break;}}private int GetInt(string value){var result = 0;int.TryParse(value, out result);return result;}private DateTime GetDate(string value){DateTime result;DateTime.TryParse(value, out result);return result;}private bool GetBool(string value){bool result;bool.TryParse(value, out result);return result;}private double GetDouble(string value){double result;double.TryParse(value, out result);return result;}private float GetFloat(string value){float result;float.TryParse(value, out result);return result;}private decimal GetDecimal(string value){decimal result;decimal.TryParse(value, out result);return result;}}

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

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

相关文章

独立主题需要有index.php模板文件,wordpress 主题模板文件列表及其作用

wordpress 主题模板文件列表及其作用主页:1.home.php2.index.php文章页&#xff1a;1.single-{post_type}.php – 如果文章类型是videos(即视频)&#xff0c;WordPress就会去查找single-videos.php(WordPress 3.0及以上版本支持)2.single.php3.index.php页面:1.自定义模板 – 在…

vim编辑技巧

转载于:https://blog.51cto.com/ovcer/1353415

IT基础设施最佳实践ITIL

IT基础设施最佳实践ITIL1. 前言1.1.困扰问题随着IT技术在企业中的应用越广&#xff0c;企业业务流程的正常运作就越离不开IT部门的支持。IT在给企业业务带来效益的同时&#xff0c;也带来了成本和风险的 困扰&#xff0c;尤其是在某些特殊行业&#xff0c;例如电信、金融等行业…

一个本科生,只用了两年就拿下诺贝尔奖,拯救了无数糖尿病患者

全世界只有3.14 % 的人关注了青少年数学之旅早在三千五百年前&#xff0c;古埃及就已经有对糖尿病的描述&#xff0c;两千多年前&#xff0c;古希腊医生亚的阿勒特奥斯把这种主要症状为“排泄多且甜的尿液”的疾病命名为“Diabetes”&#xff0c;也就是糖尿病。几千年来&#x…

C#中使用jieba.NET、WordCloudSharp制作词云图

词云简介“词云”由美国西北大学新闻学副教授、新媒体专业主任里奇戈登&#xff08;Rich Gordon&#xff09;于2006年最先使用&#xff0c;是通过形成“关键词云层”或“关键词渲染”&#xff0c;对文本中出现频率较高的“关键词”的视觉上的突出。网上大部分文章介绍的是使用P…

js框架页弹出页面关闭

2019独角兽企业重金招聘Python工程师标准>>> <!-- lang: js --> function to(href) {if (top.location self.location) {//window.opener null;window.opener.location.reload();window.open(, _self);window.close();} else {window.location.href href;}…

[超享]linux共享3160命令

点击进入Linux超全的命令学习博客[url]http://my0451.blog.sohu.com/action/m_list-ebi_3f3e214792-c_622615/entry/[/url]转载于:https://blog.51cto.com/jankie/11433

php和web服务器,php与web服务器关系

在解析这个问题前&#xff0c;先要讲述一个概念&#xff0c;什么是cgi程序&#xff1f;1.什么是cgi程序&#xff0c;cgi与fastcgi的区别CGI的中文名称是通用网关接口&#xff0c;是外部应用程序与web服务器之间的接口标准。CGI规范允许web服务器执行外部程序&#xff0c;并将它…

股市红涨绿跌色系定义真的是中国特色吗?

20080809 郑昀玩聚 与G共舞刚才发表的《适应中国特色 谷歌财经图标改绿色为红色.》&#xff0c;提及“在其他证券交易所&#xff0c;股市上涨都是使用绿色&#xff0c;下跌使用红色&#xff0c;唯独中国大陆的沪深股市&#xff0c;颜色显示相反”。 一.并非独此一家 实际上&…

狂言50年要拿30个诺奖的日本,如今怎么样了?

全世界只有3.14 % 的人关注了青少年数学之旅2019年10月9日&#xff0c;日本化学家吉野彰&#xff08;Akira Yoshino&#xff09;因在锂离子电池的发明和应用领域做出的卓越贡献&#xff0c;与美国科学家 John B. Goodenough、英国科学奖 M. Stanley Whittingham 一起荣获2019年…

VS2022+.NET6+C#10,.NET开发起飞

VS2022.NET6C#10一起体验是啥感觉&#xff1f;爽&#xff01;令人印象深刻的是VS2022打开超大项目的流畅&#xff0c;.NET6极致简化的MiniAPI框架&#xff0c;C#10各种炫酷新语法。看看下图的代码你能认识吗&#xff1f;来自.NET6的MiniAPI框架&#xff0c;直接在MapGet里面使用…

jdk8之lambda

2019独角兽企业重金招聘Python工程师标准>>> Oracle号称今年一定发布jdk8, 即使有bug, jdk8无疑最大的宠儿就是lambda表达式了&#xff0c;还是直接上代码&#xff0c;看看lambda表达式怎么了。 环境&#xff1a; openjdk8, eclipse4.3.1(支持jdk8编译) 实例代码&…

【转】Asp.Net中Excel操作权限的问题

近日在打开原来写的一个网页程序运行时&#xff0c;出现了Excel操作权限的问题&#xff0c;具体的说就是在代码中调用下面这段与Excel操作有关的语句时 Application curExcelApp new ApplicationClass(); 提示权限不足&#xff0c;具体的提示内容如下&#xff1a; 检索 COM 类…

再一贴[亲爱的,我不小心怀孕了~!]

这是我们第一次见面的酒吧。我们在这里度过了多少个夜晚&#xff0c;有过多少美好的回忆。  可是4月1日那天&#xff0c;我的心情很沉重&#xff0c;很忧郁。我不知道&#xff0c;这个消息对于我&#xff0c;对于你&#xff0c;对于我们意味着什么。我是如此渴望而又恐惧。 …

python路径长度限制,Linux下文件名长度限制

Linux下文件名长度限制出现场景&#xff1a;在迭代中有一个需求是将pdf文件名修改为所有班级的名称集合&#xff0c;出现的班级过多导致的文件名过长在linux下无法创建文件和文件夹的情况解决方式&#xff1a;经过查证&#xff0c;linux中文件名最长为255字符&#xff0c;文件路…

Visual Studio 2022 预览版2 发布啦

我们很高兴地宣布 Visual Studio 2022 的第二个预览版发布啦&#xff01;预览版 1 是有史以来第一个 64 位 Visual Studio&#xff0c;提供了改进的可扩展性。从预览版 2 开始&#xff0c;我们专注于提供有关个人和团队生产力、现代开发和不断创新等主题的新功能。在本文中&…

批作业是小学老师的一大乐趣 | 今日最佳

全世界只有3.14 % 的人关注了青少年数学之旅

【小安翻唱】凉宫春日的忧郁--冒険でしょでしょ第五届外语歌曲大赛助兴节目~绫魂论坛送aya的生日礼物筹备开始~...

第五届外语歌曲大赛如火如荼的进行的听了那么多参赛选手的歌曲自己也不由的也想唱唱正好遇上绫魂论坛的各位管理员为aya筹备生日礼物 好吧~ 我也来小唱一首 小小的final版送上~&#xff08;非正式版 请无视错词和中间以及最后的杂音 灭哈哈&#xff09; 顺便再借用了一下某位选…

免费电影下载

迅雷资源,速度稳定.我一直都在用..好东西不能一个人独享,拿出来分享..菜熊视讯最大的影视迅雷下载站&#xff01;完全免费&#xff0c;并且每天还大批量更新&#xff01;[url]http://www.caixiong.com/?445419.htm[/url]转载于:https://blog.51cto.com/douvip/12485

进入51cto之后的发展方向

这里是个不错的学习环境&#xff0c;值得珍惜。来到这里&#xff0c;我发现这里正是我梦寐以求的学习之天堂&#xff0c;这里可以肆无忌惮的贪婪的学习&#xff0c;可以感受到融洽的美好的氛围。我很愉快的开通的博客&#xff0c;并且珍惜这一次难得的机会去疯狂学习。我要确定…