1. 首页
  2. 文章列表
  3. 博主开源项目——基于EntityFrameworkCore和Lucene.NET实现的全文搜索引擎库

博主最近在迁这个博客项目从.NET Framework到.NET Core 2.2的时候,发现Lucene.NET的API已经完全变了,之前的LuceneHelper自然就不能用了,于是临时使用了SearchExtensions这个库来解决,但是用过几天之后自然也就发现了问题,那就是搜索不能按结果匹配度进行排序,而且还慢,所以博客搜索页的搜索结果自然也就不是那么尽人意,比如搜索“会声会影”,这样的搜索结果排序明显不是你们想要的吧!

懒得勤快的博客_互联网分享精神

于是,又重新研究Lucene,终于封装了一个看上去个人觉得还很好用的API,几天之后,做成一个第三方库开源出来分享给大家。

项目地址

https://github.com/ldqk/Masuit.LuceneEFCore.SearchEngine

项目简介

基于EntityFrameworkCore和Lucene.NET实现的全文检索搜索引擎,可轻松实现高性能的全文检索。可以轻松应用于任何基于EntityFrameworkCore的实体框架数据库。

开发环境:VisualStudio2017 15.9+Windows10

语法版本:C#7.0

编译环境:Windows10 x64 + VisualStudio2017 + .NET Core 2.2

项目源代码托管于github,程序包发布于nuget。

项目趋势

懒得勤快的博客_互联网分享精神

为什么没有集成到Masuit.Tools这个库?

因为这个项目又引入了几个Lucene相关的库,如果集成到Masuit.Tools,这必将给原来的项目增加了更多的引用包,使用过程中也有可能没有使用Lucene的场景,这就造成了项目更加的臃肿,所以做了个新的项目。

为什么有这个库?现成的ElasticSearch不好么?

ES确实很好用,但我想的是还有很多的小站没必要上那么重量级的中间件,于是原生lucene库不失为一种好的选择,然而原生LuceneAPI的学习成本也相对较高,所以专门封装了这个库。

更新日志

v1.0.3.2:

搜索选项增加匹配度阈值设定,默认0.5

快速开始

EntityFrameworkCore基架搭建

新建项目,并安装EntityFrameworkCore相关库以及全文检索包:

PM> Install-Package Masuit.LuceneEFCore.SearchEngine_int
PM> Install-Package Masuit.LuceneEFCore.SearchEngine_long
PM> Install-Package Masuit.LuceneEFCore.SearchEngine_string
PM> Install-Package Masuit.LuceneEFCore.SearchEngine_Guid
#根据你的项目情况,选择对应的后缀版本,提供了4个主键版本的库,后缀为int的代表主键是基于int自增类型的,后缀为Guid的代表主键是基于Guid类型的...

按照套路我们需要首先搭建好EntityFrameworkCore的基架,即数据库上下文和实体对象;

准备数据库上下文对象:

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所标记。

/// <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标签。

为什么实体类要继承LuceneIndexableBaseEntity?

LuceneIndexableBaseEntity源代码如下:

/// <summary>
/// 需要被索引的实体基类
/// </summary>
public abstract class LuceneIndexableBaseEntity : ILuceneIndexable
{
    /// <summary>
    /// 主键id
    /// </summary>
    [LuceneIndex(Name = "Id", Store = Field.Store.YES, Index = Field.Index.NOT_ANALYZED), Key]
    public int Id { get; set; }
    /// <summary>
    /// 索引唯一id
    /// </summary>
    [LuceneIndex(Name = "IndexId", Store = Field.Store.YES, Index = Field.Index.NOT_ANALYZED)]
    [NotMapped]
    public string IndexId
    {
        get => GetType().Name + ":" + Id;
        set
        {
        }
    }
    /// <summary>
    /// 转换成Lucene文档
    /// </summary>
    /// <returns></returns>
    public virtual Document ToDocument()
    {
        // 将实体对象转换成Lucene文档的逻辑
    }
}

实体继承自LuceneIndexableBaseEntity后,方便封装的Lucene可以直接调用ToDocument方法进行存储,同时,主键Id和IndexId需要参与Lucene索引文档的唯一标识(但IndexId不会生成到数据库)。

搜索引擎配置

Startup.cs

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

[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>,其中包括执行搜索所花费的时间,命中总数以及每个包含的对象的结果集以及在搜索中匹配度的数量。

特别注意:单元测试中使用内存RAM目录进行索引和搜索,但这仅用于测试目的,真实生产环境应使用物理磁盘的目录。

分享到:

优云666高速全隧道机场,每日签到免费领流量 [推广]

优云666高速全隧道机场,每日签到免费领流量

真正大鸡场,100多个节点,V2ray节点50多个。港台美日新均有白嫖节点,每日签到送1-7G流量。多条BGP中转/Azure/Dmit/HKT/Hinet/多点IPLC/保证高端用户使用需求。

版权声明:

本文仅用于学习、研究和交流目的,欢迎非商业性质转载。本文链接:https://masuit.com/1437

● 文章内容仅供参考,所涉及的软件以具体使用情况为准!

● 博主在此发文(包括但不限于汉字、拼音、拉丁字母)均为随意敲击键盘所出,用于检验本人电脑键盘录入、屏幕显示的机械、光电性能,并不代表本人局部或全部同意、支持或者反对观点。如需要详查请直接与键盘生产厂商法人代表联系。挖井挑水无水表,不会网购无快递。

● 博主的文章没有高度、深度和广度,只是凑字数。由于博主的水平不高(其实是个菜B),不足和错误之处在所难免,希望大家能够批评指出。

● 博主是利用读书、参考、引用、抄袭、复制和粘贴等多种方式打造成自己的纯镀 24k 文章,请原谅博主成为一个无耻的文档搬运工!

● 文章内容部分来源于互联网,本站不代表任何立场;涉及到的软件来源于互联网,仅供个人学习参考,请勿用于商业用途,版权归软件开发者所有,下载后请务必于24小时内删除,请支持正版!因下载本站任何资源造成的损失,全部责任由使用者本人承担!如果你是版权方,认为本文内容对您的权益有所侵犯,请联系本站管理员,并参照侵删联系的说明提交相应的证明材料,本站将进行严格地资质审查和背景调查后,情况属实的将在三天内对本文删除或修正。本站对互联网版权绝对支持!

● 本站一贯非常高度重视知识产权保护并遵守各项知识产权法律、法规和具有约束力的规范性文件。重视正版,打击盗版。根据法律、法规和规范性文件要求,本站旨在保护权利人的合法权益的措施和步骤,当权利人发现在本站生成的链接所指向的第三方网页的内容侵犯其合法权益时,权利人应事先向本站发出"权利通知",本站将根据当地法律法规和政府规范性文件采取措施移除相关内容或链接。 

● 访问本站的用户必须明白,本站对提供下载的第三方软件不拥有任何权利,其版权归该资源的合法拥有者所有。

● 本站保证站内提供的所有可下载资源(软件等)都是按“原样”提供,本站未做过任何改动;但本网站不保证本站提供的下载资源的准确性、安全性和完整性;同时本站也不承担用户因使用这些下载资源对自己和他人造成任何形式的损失或伤害。不论何种情形我们都不对任何由于使用或无法使用本站提供的信息所造成的直接的、间接的、附带的、特殊的或余波所及的损失、灵失、债务或中断负任何责任﹝不论是可预见或是不可预见的,即使我们巳被告知这种可能性﹞。

● 如遇资源报毒,请参阅:https://masuit.com/misc/14

文章历史版本:

修改次数:2 次 查看历史版本

相关推荐:

浅谈http断点续传的原理以及.NET代码实现,看似挺高端,其实很简单 Autofac在.NET Core中的属性注入
零度分享.NET Core2.2微服务入门实战教程 奉献两套Asp.Net Core最新视频教程
一些小众冷门但却非常实用的.NET(Core)开源库推荐 本站开源项目——.NET万能框架:Masuit.Tools_2.3.1.8版本发布
博主开源项目——本站博客项目MyBlogs.Core,基于.NET Core 3.1 ASP.NET Core MVC/WebAPI中另辟蹊径的全局统一异常处理方式

评论区: