Lucene .NET 全文检索
程序员文章站
2022-03-29 20:41:48
近期做项目中有用到过Lucene,那个模块是由一位前端大神负责的,空闲时间我也做了个关于Lucene做全文检索的Demo,记录下来,方便以后学习。
关于Lucene的原理,网上有长...
近期做项目中有用到过Lucene,那个模块是由一位前端大神负责的,空闲时间我也做了个关于Lucene做全文检索的Demo,记录下来,方便以后学习。
关于Lucene的原理,网上有长篇大论的文章,有兴趣的话可以去阅读,再次我就直奔主题,在代码中分析其原理。
1、创建索引(此处我用的是盘古分词)
注:在后台代码的第一行上加上 #define notes这样一行代码,目的是可以用外侧代码的#if,作用嘛 用过之后就很明白了,嘿嘿。
#region 创建索引 void CreateIndex(object sender, EventArgs e) /// /// 创建索引 /// /// /// private void CreateIndex(object sender, EventArgs e) { //索引存放的物理路径 //this.CreateDirectory(); //给 indexPath 赋值 FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NativeFSLockFactory()); bool isUpdate = IndexReader.IndexExists(directory); //判断索引库文件夹存在并且存在索引库特征文件 if (isUpdate) { //同时只能有一段代码对索引库进行写操作!当使用IndexWriter打开directory的时候会自动给索引库上锁。!!! //如果索引目录被锁定(比如索引过程中程序异常退出),则首先解锁 if (IndexWriter.IsLocked(directory)) //如果索引库文件被锁定了 解锁 { IndexWriter.Unlock(directory); } } //IndexWriter writer = new IndexWriter(indexPath, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); //该方法已过时。 IndexWriter writer = new IndexWriter(directory, new PanGuAnalyzer(), !isUpdate, Lucene.Net.Index.IndexWriter.MaxFieldLength.UNLIMITED); IEnumerable list = bllHelper.GetAllStory(); foreach (Story story in list) { writer.DeleteDocuments(new Term("ID", story.ID.ToString())); Document document = new Document(); //一篇文章,一部小说 //要进行全文检索的字段要设置 Field.Index.ANALYZED !!!!!!!!!!!!!!!!!!!!!!!!!! document.Add(new Field("ID", story.ID.ToString(), Field.Store.YES, Field.Index.NOT_ANALYZED)); document.Add(new Field("Title", story.Title, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); document.Add(new Field("Author", story.Author, Field.Store.YES, Field.Index.NOT_ANALYZED)); document.Add(new Field("Content", story.Content, Field.Store.YES, Field.Index.ANALYZED, Lucene.Net.Documents.Field.TermVector.WITH_POSITIONS_OFFSETS)); document.Add(new Field("URL", story.URL, Field.Store.YES, Field.Index.NOT_ANALYZED)); writer.AddDocument(document); } writer.Close(); directory.Close(); } #endregion
2.接下来就是搜索了
#region 搜索 IEnumerable Search(string keyWord) /// /// 搜索 /// /// 关键字 private IEnumerable Search(string keyWord) { FSDirectory directory = FSDirectory.Open(new DirectoryInfo(indexPath), new NoLockFactory()); IndexReader reader = IndexReader.Open(directory, true); IndexSearcher searcher = new IndexSearcher(reader); //多条件查询 //搜索条件 PhraseQuery queryTitle = new PhraseQuery(); //把用户输入的“北京是首都”分词为“北京 是 首都”三个词,然后添加查询条件 foreach (string word in CommonHelper.SplitWords(keyWord)) { queryTitle.Add(new Term("Title", word)); } queryTitle.SetSlop(100); //多个查询条件的词之间的最大距离。在文章中相隔太远一般也就无意义 //搜索条件 PhraseQuery queryContent = new PhraseQuery(); //把用户输入的“北京是首都”分词为“北京 是 首都”三个词,然后添加查询条件 foreach (string word in CommonHelper.SplitWords(keyWord)) { queryContent.Add(new Term("Content", word)); } queryContent.SetSlop(100); //用BooleanQuery把多个查询条件拼接起来成为一个大的查询条件 BooleanQuery query = new BooleanQuery(); query.Add(queryTitle, BooleanClause.Occur.SHOULD);//可以有 query.Add(queryContent, BooleanClause.Occur.SHOULD);//可以有 #if !notes //组合关系代表的意思如下: //1、MUST和MUST表示“与”的关系,即“并集”。 //2、MUST和MUST_NOT前者包含后者不包含。 //3、MUST_NOT和MUST_NOT没意义 //4、SHOULD与MUST表示MUST,SHOULD失去意义; //5、SHOUlD与MUST_NOT相当于MUST与MUST_NOT。 //6、SHOULD与SHOULD表示“或”的概念。 #endif //create 一个存储查询结果的容器 TopScoreDocCollector collector = TopScoreDocCollector.create(1000, true); searcher.Search(query, null, collector); ScoreDoc[] docs = collector.TopDocs(0, collector.GetTotalHits()).scoreDocs; //得到所有查询结果中的文档 List list = new List(); foreach (ScoreDoc doc in docs) { int docID = doc.doc; //得到查询结果文档的id(Lucene内部分配的id) Document document = searcher.Doc(docID); //根据ID找到对应的Document Story story = new Story(); story.ID = Convert.ToInt32(document.Get("ID")); story.Title = CommonHelper.Highlight(keyWord, document.Get("Title")); story.Author = document.Get("Author"); story.Content = CommonHelper.Highlight(keyWord, document.Get("Content")); //story.Content = document.Get("Content"); story.URL = document.Get("URL"); list.Add(story); } return list; } #endregion
3.帮助类文件
3.1 BusinessHelper类
#region 根据ID获取小说 +Story GetStoryById(int id) /// /// 根据ID获取小说 /// /// ID /// public Story GetStoryById(int id) { string sql = "SELECT * FROM Story nolock WHERE Id = @Id"; using (SqlDataReader reader = SqlHelper.ExecuteDataReader(sql, new SqlParameter("@Id", id))) { if (reader.Read()) { return ToModel(reader); } else { return null; } } } #endregion #region 获取所有的小说 +IEnumerable GetAllStory() /// /// 获取所有的小说 /// /// public IEnumerable GetAllStory() { var list = new List(); string sql = "SELECT * FROM Story nolock"; using (SqlDataReader reader = SqlHelper.ExecuteDataReader(sql)) { while (reader.Read()) { list.Add(ToModel(reader)); } } return list; } #endregion #region 把SqlDataReader转换成实体 Story ToModel(SqlDataReader reader) /// /// 把SqlDataReader转换成实体 /// /// /// private Story ToModel(SqlDataReader reader) { Story story = new Story(); story.ID = (int)ToModelValue(reader, "Id"); story.Title = (string)ToModelValue(reader, "Title"); story.Author = (string)ToModelValue(reader, "Author"); story.Content = (string)ToModelValue(reader, "Content"); story.URL = (string)ToModelValue(reader, "URL"); return story; } #endregion private object ToDBValue(object value) { if (value == null) { return DBNull.Value; } else { return value; } } private object ToModelValue(SqlDataReader reader, string columnName) { if (reader.IsDBNull(reader.GetOrdinal(columnName))) { return null; } else { return reader[columnName]; } }
3.2 CommonHelper类
/// /// 把用户传入的字符串s分割成一个个的词 /// /// /// public static string[] SplitWords(string s) { List list = new List(); Analyzer analyzer = new PanGuAnalyzer(); TokenStream tokenStream = analyzer.TokenStream("", new StringReader(s)); Lucene.Net.Analysis.Token token = null; while ((token = tokenStream.Next()) != null) //Next继续分词,如果没有更多词,则返回null { list.Add(token.TermText());//得到分到的词 } return list.ToArray(); } public static string Highlight(string keyword, string content) { try { //创建HTMLFormatter,参数为高亮单词的前后缀 PanGu.HighLight.SimpleHTMLFormatter simpleHTMLFormatter = new PanGu.HighLight.SimpleHTMLFormatter("", ""); //创建 Highlighter ,输入HTMLFormatter 和 盘古分词对象Semgent PanGu.HighLight.Highlighter highlighter = new PanGu.HighLight.Highlighter(simpleHTMLFormatter, new Segment()); //设置每个摘要段的字符数 highlighter.FragmentSize = 5000; //获取最匹配的摘要段 string result = highlighter.GetBestFragment(keyword, content); if (string.IsNullOrEmpty(result)) { return content; } else { return result; } } catch { return content; } }
3.3 SqlHelper 类
public static string CONNECTIONSTRING = ConfigurationManager.ConnectionStrings["connLuceneDB"].ConnectionString; #region 执行查询方法 +static DataTable ExecuteDataTable(string sql) /// /// 执行查询方法 ///返回DataTable /// /// sql语句 /// public static DataTable ExecuteDataTable(string sql) { using (SqlConnection conn = new SqlConnection(SqlHelper.CONNECTIONSTRING)) { conn.Open(); using (SqlCommand cmd = new SqlCommand(sql, conn)) { SqlDataAdapter da = new SqlDataAdapter(cmd); DataTable dt = new DataTable(); da.Fill(dt); return dt; } }; } #endregion #region 执行查询方法,返回DataReader对象 +static SqlDataReader ExecuteDataReader(string cmdText,params SqlParameter[] parameters) /// /// 执行查询方法,返回DataReader对象 /// /// /// /// public static SqlDataReader ExecuteDataReader(string cmdText, params SqlParameter[] parameters) { SqlConnection conn = new SqlConnection(CONNECTIONSTRING); conn.Open(); using (SqlCommand cmd = conn.CreateCommand()) { cmd.CommandText = cmdText; cmd.Parameters.AddRange(parameters); return cmd.ExecuteReader(CommandBehavior.CloseConnection); } } #endregion #region 执行 增、删、改 的方法 +static void ExecuteNonQuery(string sql, out bool flag) /// /// 执行 增、删、改 的方法 /// /// SQL语句 /// 返回执行结果 true OR false public static bool ExecuteNonQuery(string sql) { var flag = false; using (SqlConnection conn = new SqlConnection(SqlHelper.CONNECTIONSTRING)) { conn.Open(); using (SqlCommand cmd = new SqlCommand(sql, conn)) { flag = cmd.ExecuteNonQuery() > 0 ? true : false; } }; return flag; } #endregion
4.小说实体类
/// /// 小说 实体类 /// public class Story { /// /// 小说编号 /// public int ID { get; set; } /// /// 小说标题 /// public string Title { get; set; } /// /// 作者 /// public string Author { get; set; } /// /// 小说内容 /// public string Content { get; set; } /// /// 小说在线阅读地址 /// public string URL { get; set; } }
5.前台
asp:TextBox ID="txtKW" runat="server" Width="291px"> 编号 '"> 标题 '" runat="server"> 作者 '" runat="server"> 内容 '" runat="server"> 操作 '">在线阅读
注:需要引入几个类库
OK,到此为止,一个简单的Demo出来了,看看效果吧:
(PS:欢迎广大朋友参与导论关于Lucene的问题,有兴趣的话,可以加偶的扣扣:1686336218,成功的路上有我也有你。)
上一篇: 利用CSS制作新闻标题代码教程
下一篇: DB2数据库添加删除约束项