懒得勤快 6 years ago
parent
commit
92e1e0c7a7
2 changed files with 257 additions and 5 deletions
  1. 193 2
      README.md
  2. 64 3
      WebSearchDemo/Controllers/HomeController.cs

+ 193 - 2
README.md

@@ -1,2 +1,193 @@
-# Masuit.LuceneEFCore.SearchEngine
-基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎
+### 基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎
+基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎,可轻松实现高性能的全文检索。可以轻松应用于任何基于EntityFrameworkCore的实体框架数据库。
+
+### 快速开始
+#### EntityFrameworkCore基架搭建
+新建项目,并安装EntityFrameworkCore相关库以及全文检索包:
+```shell
+Install-Package Masuit.LuceneEFCore.SearchEngine_int
+#根据你的项目情况,选择对应的后缀版本,提供了4个主键版本的库,后缀为int的代表主键是基于int自增类型的,后缀为Guid的代表主键是基于Guid自增类型的...
+```
+按照套路我们需要首先搭建好EntityFrameworkCore的基架,即数据库上下文和实体对象;
+
+准备数据库上下文对象:
+```csharp
+public class DataContext : DbContext
+{
+    public DataContext(DbContextOptions<DataContext> options) : base(options){}
+    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
+    {
+        base.OnConfiguring(optionsBuilder);
+        optionsBuilder.UseQueryTrackingBehavior(QueryTrackingBehavior.TrackAll);
+    }
+    public virtual DbSet<Post> Post { get; set; }
+}
+```
+准备实体对象,这里开始需要注意了,要想这个库的数据被全文检索,需要符合两个条件:
+1.实体必须继承自LuceneIndexableBaseEntity;
+2.需要被检索的字段需要被LuceneIndexAttribute所标记。
+```csharp
+/// <summary>
+/// 文章
+/// </summary>
+[Table("Post")]
+public class Post : LuceneIndexableBaseEntity
+{
+    public Post()
+    {
+        PostDate = DateTime.Now;
+    }
+
+    /// <summary>
+    /// 标题
+    /// </summary>
+    [Required(ErrorMessage = "文章标题不能为空!"), LuceneIndex]
+    public string Title { get; set; }
+
+    /// <summary>
+    /// 作者
+    /// </summary>
+    [Required, MaxLength(24, ErrorMessage = "作者名最长支持24个字符!"), LuceneIndex]
+    public string Author { get; set; }
+
+    /// <summary>
+    /// 内容
+    /// </summary>
+    [Required(ErrorMessage = "文章内容不能为空!"), LuceneIndex(IsHtml = true)]
+    public string Content { get; set; }
+
+    /// <summary>
+    /// 发表时间
+    /// </summary>
+    public DateTime PostDate { get; set; }
+
+    /// <summary>
+    /// 作者邮箱
+    /// </summary>
+    [Required(ErrorMessage = "作者邮箱不能为空!"), LuceneIndex]
+    public string Email { get; set; }
+
+    /// <summary>
+    /// 标签
+    /// </summary>
+    [StringLength(256, ErrorMessage = "标签最大允许255个字符"), LuceneIndex]
+    public string Label { get; set; }
+
+    /// <summary>
+    /// 文章关键词
+    /// </summary>
+    [StringLength(256, ErrorMessage = "文章关键词最大允许255个字符"), LuceneIndex]
+    public string Keyword { get; set; }
+
+}
+```
+LuceneIndexAttribute对应的4个自定义参数:
+1.Name:自定义索引字段名,默认为空;
+2.Index:索引行为,默认为Field.Index.ANALYZED;
+3.Store:是否被存储到索引库,默认为Field.Store.YES;
+4.IsHtml:是否是html,默认为false,若标记为true,则在索引解析时会先清空其中的html标签。
+#### 搜索引擎配置
+Startup.cs
+```csharp
+public void ConfigureServices(IServiceCollection services)
+{
+    // ...
+    services.AddDbContext<DataContext>(db =>
+    {
+        db.UseSqlServer("Data Source=.;Initial Catalog=MyBlogs;Integrated Security=True");
+    });// 配置数据库上下文
+    services.AddSearchEngine<DataContext>(new LuceneIndexerOptions()
+    {
+        Path = "lucene"
+    });// 依赖注入搜索引擎,并配置索引库路径
+    // ...
+}
+```
+HomeController.cs
+```csharp
+[Route("[controller]/[action]")]
+public class HomeController : Controller
+{
+    private readonly ISearchEngine<DataContext> _searchEngine;
+    private readonly ILuceneIndexer _luceneIndexer;
+    public HomeController(ISearchEngine<DataContext> searchEngine, ILuceneIndexer luceneIndexer)
+    {
+        _searchEngine = searchEngine;
+        _luceneIndexer = luceneIndexer;
+    }
+
+    /// <summary>
+    /// 搜索
+    /// </summary>
+    /// <param name="s">关键词</param>
+    /// <param name="page">第几页</param>
+    /// <param name="size">页大小</param>
+    /// <returns></returns>
+    [HttpGet]
+    public async Task<IActionResult> Index(string s, int page, int size)
+    {
+        //var result = _searchEngine.ScoredSearch<Post>(new SearchOptions(s, page, size, "Title,Content,Email,Author"));
+        var result = _searchEngine.ScoredSearch<Post>(new SearchOptions(s, page, size, typeof(Post)));
+        return Ok(result);
+    }
+
+    /// <summary>
+    /// 创建索引
+    /// </summary>
+    [HttpGet]
+    public void CreateIndex()
+    {
+        //_searchEngine.CreateIndex();//扫描所有数据表,创建符合条件的库的索引
+        _searchEngine.CreateIndex(new List<string>() { nameof(Post) });//创建指定的数据表的索引
+    }
+
+    /// <summary>
+    /// 添加索引
+    /// </summary>
+    [HttpPost]
+    public void AddIndex(Post p)
+    {
+        // 添加到数据库并更新索引
+        _searchEngine.Context.Post.Add(p);
+        _searchEngine.SaveChanges();
+
+        //_luceneIndexer.Add(p); //单纯的只添加索引库
+    }
+
+    /// <summary>
+    /// 删除索引
+    /// </summary>
+    [HttpDelete]
+    public void DeleteIndex(Post post)
+    {
+        //从数据库删除并更新索引库
+        Post p = _searchEngine.Context.Post.Find(post.Id);
+        _searchEngine.Context.Post.Remove(p);
+        _searchEngine.SaveChanges();
+
+        //_luceneIndexer.Delete(p);// 单纯的从索引库移除
+    }
+
+    /// <summary>
+    /// 更新索引库
+    /// </summary>
+    /// <param name="post"></param>
+    [HttpPatch]
+    public void UpdateIndex(Post post)
+    {
+        //从数据库更新并同步索引库
+        Post p = _searchEngine.Context.Post.Find(post.Id);
+        // update...
+        _searchEngine.Context.Post.Update(p);
+        _searchEngine.SaveChanges();
+
+        //_luceneIndexer.Update(p);// 单纯的更新索引库
+    }
+}
+```
+#### 关于更新索引
+要在执行任何CRUD操作后更新索引,只需从ISearchEngine调用SaveChanges()方法,而不是从DataContext调用SaveChanges()。 这才会更新索引,然后会自动调用DataContexts的SaveChanges()方法。如果直接调用DataContexts的SaveChanges()方法,只会保存到数据库,而不会更新索引库。
+#### 关于搜索结果
+搜索返回IScoredSearchResultCollection<T>,其中包括执行搜索所花费的时间,命中总数以及每个包含的对象的结果集以及在搜索中匹配度的数量。
+
+<font color=#f00>特别注意:单元测试中使用内存RAM目录进行索引和搜索,但这仅用于测试目的,真实生产环境应使用物理磁盘的目录。</font>

+ 64 - 3
WebSearchDemo/Controllers/HomeController.cs

@@ -1,6 +1,7 @@
 using Masuit.LuceneEFCore.SearchEngine;
 using Masuit.LuceneEFCore.SearchEngine.Interfaces;
 using Microsoft.AspNetCore.Mvc;
+using System.Collections.Generic;
 using System.Threading.Tasks;
 using WebSearchDemo.Database;
 
@@ -10,12 +11,19 @@ namespace WebSearchDemo.Controllers
     public class HomeController : Controller
     {
         private readonly ISearchEngine<DataContext> _searchEngine;
-
-        public HomeController(ISearchEngine<DataContext> searchEngine)
+        private readonly ILuceneIndexer _luceneIndexer;
+        public HomeController(ISearchEngine<DataContext> searchEngine, ILuceneIndexer luceneIndexer)
         {
             _searchEngine = searchEngine;
+            _luceneIndexer = luceneIndexer;
         }
-
+        /// <summary>
+        /// 搜索
+        /// </summary>
+        /// <param name="s">关键词</param>
+        /// <param name="page">第几页</param>
+        /// <param name="size">页大小</param>
+        /// <returns></returns>
         [HttpGet]
         public async Task<IActionResult> Index(string s, int page, int size)
         {
@@ -23,5 +31,58 @@ namespace WebSearchDemo.Controllers
             var result = _searchEngine.ScoredSearch<Post>(new SearchOptions(s, page, size, typeof(Post)));
             return Ok(result);
         }
+
+        /// <summary>
+        /// 创建索引
+        /// </summary>
+        [HttpGet]
+        public void CreateIndex()
+        {
+            //_searchEngine.CreateIndex();//扫描所有数据表,创建符合条件的库的索引
+            _searchEngine.CreateIndex(new List<string>() { nameof(Post) });//创建指定的数据表的索引
+        }
+
+        /// <summary>
+        /// 添加索引
+        /// </summary>
+        [HttpPost]
+        public void AddIndex(Post p)
+        {
+            // 添加到数据库并更新索引
+            _searchEngine.Context.Post.Add(p);
+            _searchEngine.SaveChanges();
+
+            //_luceneIndexer.Add(p); //单纯的只添加索引库
+        }
+
+        /// <summary>
+        /// 删除索引
+        /// </summary>
+        [HttpDelete]
+        public void DeleteIndex(Post post)
+        {
+            //从数据库删除并更新索引库
+            Post p = _searchEngine.Context.Post.Find(post.Id);
+            _searchEngine.Context.Post.Remove(p);
+            _searchEngine.SaveChanges();
+
+            //_luceneIndexer.Delete(p);// 单纯的从索引库移除
+        }
+
+        /// <summary>
+        /// 更新索引库
+        /// </summary>
+        /// <param name="post"></param>
+        [HttpPatch]
+        public void UpdateIndex(Post post)
+        {
+            //从数据库更新并同步索引库
+            Post p = _searchEngine.Context.Post.Find(post.Id);
+            // update...
+            _searchEngine.Context.Post.Update(p);
+            _searchEngine.SaveChanges();
+
+            //_luceneIndexer.Update(p);// 单纯的更新索引库
+        }
     }
 }