前言

最近在整理本地的一些笔记

有些日期不太对的,我的博客上有记录发布和更新时间,所以我去搜索了一下

这时候发现 StarBlog 的搜索功能太简陋了

虽然上次更新增加了一大波功能,也优化了一下搜索功能,之前只能搜索标题,现在可以搜索正文内容了。详见: StarBlog v1.3.0 新版本,一大波更新以及迁移服务器部署

不过有个问题是没有权重,标题的权重应该比正文更高的

按理说这些应该得加入全文检索引擎,Elasticsearch、MeiliSearch 之类的来实现。但这些需要额外的服务,太重了。

再不济也要用 Lucene.NET 这种,这是 Elasticsearch 的基础,但不需要额外服务,纯本地嵌入式,支持权重控制、高亮、分词等功能。

但为了快速实现,这些我都不想用,先用最简单的方式来改进。

同时我也重写了搜索结果页面,之前的页面太业余了。

极简实现

最终我的方案是:在内存里手动算权重 + Regex 实现结果高亮

成本非常低,效果也不错

实现效果

来看看效果吧

这套 StarBlog 的前端是 Bootstrap,样式都得靠 CSS,相对于我现在用的 Tailwind CSS、Shadcn/ui、Magic UI 之类的,太原始了,重写这个界面已经尽力了hhh

代码

OK,接下来是大家不感兴趣的代码环节

模型

先定义搜索结果模型

public class SearchPost {
public Post Post { get; set; }
public int TitleScore { get; set; } public int ContentScore { get; set; } // 标题每命中一次+100分
// 内容命中+1分
public int Score => TitleScore * 100 + ContentScore;
public string HighlightedTitle { get; set; }
public string HighlightedSnippet { get; set; }
}

搜索逻辑

搜索功能逻辑都在 src/StarBlog.Web/Controllers/SearchController.cs 文件里

在内村里计算权重,从数据库查询出来后,用 Linq 计算权重,关键词出现一次为一分;总分是在 SearchPost 里计算的,标题每命中一次+100分,内容命中+1分

var searchPosts = _postRepo
.Where(a => a.IsPublish)
.Where(a =>
a.Title!.Contains(keyword) ||
a.Content.Contains(keyword)
)
.Include(a => a.Category)
.ToList()
.Select(p => new SearchPost {
Post = p,
TitleScore = p.Title.ToLower().Split(keyword).Length - 1,
ContentScore = p.Content?.ToLower().Split(keyword).Length - 1 ?? 0,
})
.OrderByDescending(x => x.Score)
.ToList();

搜索结果高亮

使用正则表达式来实现结果高亮

var regex = new Regex(Regex.Escape(keyword), RegexOptions.IgnoreCase);
foreach (var item in searchPosts) {
item.HighlightedTitle = regex.Replace(item.Post.Title, m => $"<mark>{m.Value}</mark>");
item.HighlightedSnippet = GetHighlightedSnippet(item.Post.Content, keyword);
}

生成高亮片段摘要

思路很简单:

  • 找到第一个命中的位置
  • 截取前后一定长度的内容(比如前后各 50 个字符)
  • 再用 Regex 替换加 <mark> 高亮
  • 最后拼上 ... 作为省略号
public static string GetHighlightedSnippet(string content, string keyword, int snippetLength = 100) {
if (string.IsNullOrEmpty(content) || string.IsNullOrEmpty(keyword))
return string.Empty; var regex = new Regex(Regex.Escape(keyword), RegexOptions.IgnoreCase);
var match = regex.Match(content); if (!match.Success) {
// 没匹配到,直接取前 snippetLength*2 个字符作为摘要
return content.Length > snippetLength * 2
? content.Substring(0, snippetLength * 2) + "..."
: content;
} // 计算截取范围(匹配位置前后各 snippetLength)
int start = Math.Max(0, match.Index - snippetLength);
int length = Math.Min(content.Length - start, match.Length + snippetLength * 2); string snippet = content.Substring(start, length); // 高亮处理
snippet = regex.Replace(snippet, m => $"<mark>{m.Value}</mark>"); // 前后补省略号(如果不是全文开头或结尾)
if (start > 0) snippet = "..." + snippet;
if (start + length < content.Length) snippet += "..."; return snippet;
}

小结

重构之后体验更上一层

不过在老架构上修修补补终究不是长久之计

等有空就得赶紧开始 v2 新版的开发

PS:接下来也许会拓展一下这个搜索功能,加入多个关键词搜索的支持,再进一步搭配 Lucene.NET 也不无可能。

重写 StarBlog 的搜索功能和页面,支持权重设置和结果高亮的更多相关文章

  1. 带搜索功能,支持绑定对象到节点的TreeView辅助类

    特点: 1.支持数叶子节点与对象绑定 2.支持xml导入,且数据类相关的xml可自定义,只和泛型的实现有关 3.支持节点搜索功能,可在树结构上要求只显示部分节点 4.用C#编写,但与平台关联性低,可移 ...

  2. 一个尖括号能干什么,画一个笑脸开始(为了支持交互,它又增添了JavaScript。HTML页面也越来越臃肿。于是CSS便诞生了。API和核心代码的出现使HTML能够访问更复杂的软件功能--支持更高级的交互和云服务集成。这就是今天的HTML5)

    一个尖括号 < 一个尖括号能干什么 < ? 你可以编出一顶帽子 <(:-p 或一张笑脸 :-> 再或者更直接一些 20世纪90年代初,html作为一种简单标记语言面世,用于在互 ...

  3. Hugging Face 每周速递: Space 支持创建模版应用、Hub 搜索功能增强、BioGPT-Large 还有更多

    每一周,我们的同事都会向社区的成员们发布一些关于 Hugging Face 相关的更新,包括我们的产品和平台更新.社区活动.学习资源和内容更新.开源库和模型更新等,我们将其称之为「Hugging Ne ...

  4. CSS选择器实现搜索功能 驱动过滤搜索技术

    一.CSS选择器可以用来实现搜索功能 CSS选择器可以用来实现搜索功能. 作者以前提过CSS3的选择器结合表单元素可以用来控制元素的显隐,这里,类似的,还是CSS3的选择器,用来过滤和搜索页面元素. ...

  5. 如何使用 Lucene 做网站高亮搜索功能?

    现在基本上所有网站都支持搜索功能,现在搜索的工具有很多,比如Solr.Elasticsearch,它们都是基于 Lucene 实现的,各有各的使用场景.Lucene 比较灵活,中小型项目中使用的比较多 ...

  6. 第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能

    第三百六十九节,Python分布式爬虫打造搜索引擎Scrapy精讲—elasticsearch(搜索引擎)用Django实现搜索功能 Django实现搜索功能 1.在Django配置搜索结果页的路由映 ...

  7. JAVAEE——宜立方商城08:Zookeeper+SolrCloud集群搭建、搜索功能切换到集群版、Activemq消息队列搭建与使用

    1. 学习计划 1.solr集群搭建 2.使用solrj管理solr集群 3.把搜索功能切换到集群版 4.添加商品同步索引库. a) Activemq b) 发送消息 c) 接收消息 2. 什么是So ...

  8. 【Android】3.12 兴趣点( POI)搜索功能

    分类:C#.Android.VS2015.百度地图应用: 创建日期:2016-02-04 一.简介 POI(Point of Interest),中文可以翻译为“兴趣点”.在地理信息系统中,一个POI ...

  9. ASP.NET页面支持的指令

    页面的处理指令 页面指令的处理用于配置执行该页面的运行时环境.在ASP.NET中,指令可以位于页面的任何位置,但良好且常见的习惯是将其置于文件的开始部分.除此,页面指令的名称是不区分大小写的,且指令的 ...

  10. ZKEACMS添加搜索功能,搜索插件说明

    ZKEACMS默认是不支持搜索功能的.但是搜索功能是比较常用的一个功能,使用这个搜索插件,可以让CMS支持搜索: 如下图所示: 数据库 Microstft Sql Server 2008R2 以上 页 ...

随机推荐

  1. C#学习日志

    C#入门篇 EanoJiang/CSharp-: C#入门教程,自用 程序思维题: 两根不均匀的香,烧完一根是1h,怎么用来计时15min呢? 思路:一根香从两头同时点燃烧完是30min,只需再对半即 ...

  2. Jit 报错TracingCheckError:ERROR: Graphs differed across invocations!

    问题描述 使用Tinynn将Pytorch转化为tflite时报错: 发生异常: TracingCheckError (note: full exception trace is shown but ...

  3. 如何在FastAPI中打造坚不可摧的Web安全防线?

    扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 第一章:基础安全框架认知 一.W ...

  4. MongoDB入门实战教程(12)

    MongoDB在4.2版本开始全面支持了多文档事务,这也让MongoDB可以作为OLTP的选项之一,本篇我们就来学习一下MongoDB的多文档事务. 1 ACID支持程度 谈到事务,就不得不提经典的A ...

  5. C# HttpListener 的使用方法

    关于监听回调两次的原因,可能是因为重新监听导致的,所以查到微软上面的解析是说 BeginGetContext方法开始异步 (非阻塞) 调用以接收传入的客户端请求. 在调用此方法之前,必须调用 Star ...

  6. socket.io的小例子

    前言 socket.io是原生ws封装的第三方库,它不仅仅对客户端做了封装,还对服务端也进行了封装. 提供了很多能用得到的功能,比如: 断链自动尝试重链 对不支持ws的浏览器做兼容(降级轮循http) ...

  7. SpringBoot--如何给项目添加配置属性及读取属性

    SpringBoot允许使用配置文件对应用程序进行配置,支持以下不同形式的配置源: 属性文件(比如application.properties) yaml文件(后缀可以是yml或者yaml) 环境变量 ...

  8. 前端开发系列049-基础篇之VueRouter

    VueRouter 路由 MPA(多)页面应用 (Multiple Page Application) SPA(单)页面应用 (Single Page Application) 项目打包后最终只有in ...

  9. RestCloud ETL WebService数据同步到本地

    企业里面有很多业务系统只能提供WebService接口如SAP等,还有一些SaaS系统也只提供WebService的接口如Workday等,对于这些系统的数据我们可以使用ETL工具进行调用然后把XML ...

  10. SciTech-BigDataAIML-Boltzmann constant波尔兹曼常数 + Boltzmann Machine波尔兹曼机模型

    SciTech-BigDataAIML- Boltzmann constant