欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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">
                
            
            
                
                    操作
                
                
                    '">在线阅读
                
            
        
        
        
        
        

注:需要引入几个类库

Lucene .NET 全文检索

OK,到此为止,一个简单的Demo出来了,看看效果吧:

Lucene .NET 全文检索

(PS:欢迎广大朋友参与导论关于Lucene的问题,有兴趣的话,可以加偶的扣扣:1686336218,成功的路上有我也有你。)