You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
hw-web/Admin.NET-v2/HW_PORTAL_搜索改造方案.md

28 KiB

hw-portal 搜索改造方案

1. 文档目的

  • 文档范围:给 Admin.NET-v2 中的 hw-portal 插件模块设计一套新的搜索方案。
  • 目标结果:使用 EF Core 10 + MySQL 普通搜索引擎(全文检索 + 兜底模糊搜索),替换当前基于 UNION ALL + LIKE 的关键词搜索。
  • 约束来源:基于当前对话已确认,不做向量搜索,不接大模型,不接本地 embedding 模型,保持现有搜索接口不变,允许新增搜索索引表与数据库索引。
  • 文档用途:供后续开发、代码评审、数据库设计、前后端联调、测试验收使用。

2. 当前理解

2.1 当前项目结构理解

  • 后端主框架:Furion + SqlSugar
  • 当前新增的门户模块:Admin.NET\Plugins\Admin.NET.Plugin.HwPortal
  • 当前 hw-portal 已经是独立插件项目,但搜索仍是“兼容 MyBatis XML”的方案不是 EF Core 方案。
  • 当前仓库里没有发现 EF CoreDbContextNpgsql/Pomelo/MySql EF Provider 等依赖,说明 EF Core 搜索子系统需要从零引入。

2.2 当前搜索实现理解

当前搜索主入口:

当前搜索核心实现:

当前搜索 SQL

当前索引重建实现:

当前文本抽取器:

2.3 当前搜索的实际问题

  1. 搜索结果直接查业务表,UNION ALL + LIKE 性能一般。
  2. 搜索和业务表强耦合,新增来源或调整评分成本高。
  3. 重建接口当前是空实现,没有真正索引重建能力。
  4. 关键词搜索只能做字符匹配,不是“搜索引擎式”的独立索引架构。
  5. 当前不是 EF Core 方案,也没有独立搜索库/搜索表/全文索引。

3. 对话中的选择结论

本方案不是拍脑袋决定,而是基于当前对话确认的选择:

3.1 已确认选择

  • 不做 Vector Search
  • 不使用大模型
  • 可以做“普通搜索引擎”
  • 保持原接口不变
  • 允许改数据库结构
  • 搜索相关改造切到 EF Core 10

3.2 因此得出的最终方案

不是:

  • EF Core 10 + Vector Search
  • Elasticsearch
  • Easy-ES
  • 继续使用 MyBatis XML + LIKE

而是:

  • EF Core 10 + MySQL 全文检索FULLTEXT
  • 辅助 LIKE 兜底
  • 独立搜索索引表
  • 独立搜索 DbContext
  • 新旧搜索逻辑保留过渡接口,但默认切新方案

4. 具体需求

4.1 业务需求

  1. 前台搜索继续支持:
    • 关键词搜索
    • 分页
    • 命中摘要
    • 高亮
    • 路由跳转
  2. 编辑端搜索继续支持:
    • 编辑路由 editRoute
  3. 搜索来源继续覆盖:
    • 菜单 hw_web_menu
    • 页面 hw_web
    • 页面详情 hw_web1
    • 资料文档 hw_web_document
    • 配置类型 hw_portal_config_type
  4. 后台继续保留“重建索引”接口。

4.2 技术需求

  1. 新搜索查询必须使用 EF Core 10
  2. 新搜索必须有独立搜索索引表,而不是每次直查原业务表
  3. 新搜索必须兼容 MySQL
  4. 现有公开接口路径与返回 DTO 不变
  5. 要支持增量同步索引,不允许每次保存都全量扫描业务表

5. 应用场景分析

5.1 用户搜索官网内容

典型场景:

  • 用户输入“工业物联网”
  • 需要搜到:
    • 产品中心配置类型
    • 产品页面 JSON 文本
    • 页面详情 JSON 文本
    • 相关文档
    • 菜单项

要求:

  • 响应时间稳定
  • 标题命中优先
  • 摘要可读
  • 可跳前台页面

5.2 编辑端搜索

典型场景:

  • 运营同事在后台搜索某个页面或某个资料文档
  • 需要直接跳到编辑页

要求:

  • 复用同一套索引
  • 只是在结果里补 editRoute

5.3 数据更新后的搜索可见性

典型场景:

  • 新增/修改页面 JSON
  • 更新资料文档
  • 新增菜单

要求:

  • 保存成功后索引同步更新
  • 不依赖手工全量重建

6. 技术选型分析

6.1 为什么不继续用当前 SQL 搜索

当前 HwSearchMapper.xml 的特点:

  • 多张表 UNION ALL
  • 每张表都做 %keyword%
  • 排序靠写死分数和更新时间

优点:

  • 简单
  • 快速可用

问题:

  • 性能扩展性差
  • 搜索能力依赖业务表结构
  • 不像真正搜索引擎
  • 不利于后续继续增加来源

6.2 为什么不选向量搜索

向量搜索至少需要:

  1. 文本 embedding 模型
  2. 向量字段
  3. 相似度检索能力

当前对话里已明确:

  • 不做向量搜索
  • 不用大模型

所以本次方案不能再写成 Vector Search

6.3 为什么选 MySQL FULLTEXT + 独立搜索表

优点:

  1. 不依赖外部搜索引擎
  2. 不依赖模型
  3. 能明显优于当前 LIKE 方案
  4. 可以保留原有 DTO 和接口
  5. 可先做成普通搜索引擎,后续再升级成 ES/向量检索

6.4 为什么引入 EF Core 10

虽然主项目主链路是 SqlSugar + Furion,但搜索子系统可以独立引入 EF Core理由如下

  1. 搜索索引表是新增子系统,不必受当前 MyBatis 兼容层约束
  2. EF Core 对“独立读写模型 + 独立迁移 + 查询表达式”很合适
  3. 后续若要演进到更多数据库特性EF Core 子系统维护更清晰

7. 改造后的目标架构

用户请求 /portal/search
        ↓
HwSearchController
        ↓
HwSearchService
        ↓
HwPortalSearchDbContext
        ↓
hw_portal_search_doc搜索索引表
        ↓
MySQL FULLTEXT / LIKE 兜底

业务数据更新流程:

业务保存成功(页面/菜单/文档/配置类型)
        ↓
对应业务 Service
        ↓
IHwSearchIndexService.UpsertAsync(...)
        ↓
PortalSearchDocConverter
        ↓
hw_portal_search_doc

8. 数据库设计方案

8.1 新增搜索索引表

建议表名:

hw_portal_search_doc

建议字段:

CREATE TABLE `hw_portal_search_doc` (
  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
  `doc_id` VARCHAR(128) NOT NULL COMMENT '文档唯一标识,例如 menu:1、web:7',
  `source_type` VARCHAR(32) NOT NULL COMMENT '来源类型menu/web/web1/document/configType',
  `biz_id` VARCHAR(64) NULL COMMENT '业务主键字符串',
  `title` VARCHAR(500) NULL COMMENT '搜索标题',
  `content` LONGTEXT NULL COMMENT '搜索正文',
  `web_code` VARCHAR(64) NULL COMMENT '页面编码',
  `type_id` VARCHAR(64) NULL COMMENT '类型ID',
  `device_id` VARCHAR(64) NULL COMMENT '设备ID',
  `menu_id` VARCHAR(64) NULL COMMENT '菜单ID',
  `document_id` VARCHAR(64) NULL COMMENT '文档ID',
  `base_score` INT NOT NULL DEFAULT 0 COMMENT '基础分',
  `route` VARCHAR(255) NULL COMMENT '展示路由',
  `route_query_json` JSON NULL COMMENT '展示路由参数',
  `edit_route` VARCHAR(255) NULL COMMENT '编辑路由',
  `is_delete` CHAR(1) NOT NULL DEFAULT '0' COMMENT '逻辑删除',
  `updated_at` DATETIME NULL COMMENT '业务更新时间',
  `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '索引创建时间',
  `modified_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '索引更新时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_hw_portal_search_doc_doc_id` (`doc_id`),
  KEY `idx_hw_portal_search_doc_source_type` (`source_type`),
  KEY `idx_hw_portal_search_doc_updated_at` (`updated_at`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='hw-portal 搜索索引表';

全文索引:

ALTER TABLE `hw_portal_search_doc`
ADD FULLTEXT KEY `ft_hw_portal_search_doc_title_content` (`title`, `content`);

8.2 设计理由

  • doc_id:保证一个业务对象只对应一条搜索文档
  • source_type:保留来源分类
  • title/content:统一搜索输入
  • route/edit_route:避免查询后再做过多二次拼装
  • updated_at:用于排序
  • base_score:保留当前旧搜索里“不同来源不同基础权重”的思路

9. 代码改造总览

9.1 现有代码保留

保留但用途调整:

9.2 现有代码废弃或降级为兼容模式

这些文件可以:

  • 保留一版作为 legacy 回退
  • 或重命名为 LegacyHwSearchService

9.3 建议新增文件位置

推荐新增目录:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf

建议文件:

  • SearchEf/HwPortalSearchDbContext.cs
  • SearchEf/Entity/HwPortalSearchDoc.cs
  • SearchEf/Option/HwPortalSearchOptions.cs
  • SearchEf/Service/IHwSearchIndexService.cs
  • SearchEf/Service/HwSearchIndexService.cs
  • SearchEf/Service/HwSearchQueryService.cs
  • SearchEf/Service/HwSearchRebuildService.cs
  • SearchEf/Extensions/HwPortalSearchServiceCollectionExtensions.cs

如希望保持插件结构更规整,也可以落在:

  • Entity/Search
  • Service/SearchEf
  • Option

10. 具体代码设计

10.1 搜索索引实体

建议文件:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Entity\HwPortalSearchDoc.cs

建议代码:

using Microsoft.EntityFrameworkCore;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace Admin.NET.Plugin.HwPortal.SearchEf.Entity;

/// <summary>
/// hw-portal 搜索索引实体。
/// 这不是业务原表,而是专门为搜索准备的索引表。
/// </summary>
[Table("hw_portal_search_doc")]
[Index(nameof(DocId), IsUnique = true)]
[Index(nameof(SourceType))]
[Index(nameof(UpdatedAt))]
public class HwPortalSearchDoc
{
    [Key]
    public long Id { get; set; }

    /// <summary>
    /// 文档唯一键。
    /// 例如menu:12、web:7、document:abc001。
    /// 用它实现幂等更新。
    /// </summary>
    [Required]
    [MaxLength(128)]
    public string DocId { get; set; } = string.Empty;

    /// <summary>
    /// 来源类型。
    /// 值域与当前 PortalSearchDocConverter 里的常量保持一致。
    /// </summary>
    [Required]
    [MaxLength(32)]
    public string SourceType { get; set; } = string.Empty;

    [MaxLength(64)]
    public string? BizId { get; set; }

    [MaxLength(500)]
    public string? Title { get; set; }

    /// <summary>
    /// 正文。
    /// 这里通常会存“已抽取、已清洗”的文本,而不是原始 JSON。
    /// </summary>
    public string? Content { get; set; }

    [MaxLength(64)]
    public string? WebCode { get; set; }

    [MaxLength(64)]
    public string? TypeId { get; set; }

    [MaxLength(64)]
    public string? DeviceId { get; set; }

    [MaxLength(64)]
    public string? MenuId { get; set; }

    [MaxLength(64)]
    public string? DocumentId { get; set; }

    /// <summary>
    /// 基础分。
    /// 保留旧系统“菜单比正文更高”的评分思想。
    /// </summary>
    public int BaseScore { get; set; }

    [MaxLength(255)]
    public string? Route { get; set; }

    public string? RouteQueryJson { get; set; }

    [MaxLength(255)]
    public string? EditRoute { get; set; }

    [MaxLength(1)]
    public string IsDelete { get; set; } = "0";

    public DateTime? UpdatedAt { get; set; }

    public DateTime CreatedAt { get; set; }

    public DateTime ModifiedAt { get; set; }
}

10.2 搜索 DbContext

建议文件:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\HwPortalSearchDbContext.cs

建议代码:

using Admin.NET.Plugin.HwPortal.SearchEf.Entity;
using Microsoft.EntityFrameworkCore;

namespace Admin.NET.Plugin.HwPortal.SearchEf;

/// <summary>
/// 搜索子系统专用 DbContext。
/// 只管理搜索相关表,不把整个 hw-portal 都切到 EF Core。
/// </summary>
public class HwPortalSearchDbContext : DbContext
{
    public HwPortalSearchDbContext(DbContextOptions<HwPortalSearchDbContext> options) : base(options)
    {
    }

    public DbSet<HwPortalSearchDoc> SearchDocs => Set<HwPortalSearchDoc>();

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        base.OnModelCreating(modelBuilder);

        // Why
        // 这里做 EF 层的结构约束声明。
        // FULLTEXT 索引建议仍通过 SQL 脚本或迁移脚本手工加,避免 provider 差异。
        modelBuilder.Entity<HwPortalSearchDoc>(entity =>
        {
            entity.Property(x => x.Content).HasColumnType("longtext");
            entity.Property(x => x.RouteQueryJson).HasColumnType("json");
        });
    }
}

10.3 搜索索引服务接口

建议文件:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\IHwSearchIndexService.cs

建议代码:

namespace Admin.NET.Plugin.HwPortal.SearchEf.Service;

/// <summary>
/// 搜索索引服务接口。
/// 负责把业务对象同步成搜索文档。
/// </summary>
public interface IHwSearchIndexService
{
    Task UpsertMenuAsync(HwWebMenu menu, CancellationToken cancellationToken = default);

    Task UpsertWebAsync(HwWeb web, CancellationToken cancellationToken = default);

    Task UpsertWeb1Async(HwWeb1 web1, CancellationToken cancellationToken = default);

    Task UpsertDocumentAsync(HwWebDocument document, CancellationToken cancellationToken = default);

    Task UpsertConfigTypeAsync(HwPortalConfigType configType, CancellationToken cancellationToken = default);

    Task DeleteByDocIdAsync(string docId, CancellationToken cancellationToken = default);

    Task RebuildAllAsync(CancellationToken cancellationToken = default);
}

10.4 搜索查询服务

建议文件:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\HwSearchQueryService.cs

建议代码片段:

using Admin.NET.Plugin.HwPortal.SearchEf.Entity;
using Microsoft.EntityFrameworkCore;

namespace Admin.NET.Plugin.HwPortal.SearchEf.Service;

/// <summary>
/// 搜索查询服务。
/// 负责对索引表做查询,而不是直接查业务表。
/// </summary>
public class HwSearchQueryService
{
    private readonly HwPortalSearchDbContext _db;

    public HwSearchQueryService(HwPortalSearchDbContext db)
    {
        _db = db;
    }

    public async Task<List<HwPortalSearchDoc>> SearchAsync(string keyword, int take, CancellationToken cancellationToken = default)
    {
        keyword = keyword.Trim();

        // 第一阶段:先走普通 LIKE 版本,确保功能先跑通。
        // 第二阶段:再补 FromSqlRaw + MATCH AGAINST 做 FULLTEXT 优化。
        IQueryable<HwPortalSearchDoc> query = _db.SearchDocs
            .AsNoTracking()
            .Where(x => x.IsDelete == "0")
            .Where(x =>
                (x.Title != null && EF.Functions.Like(x.Title, $"%{keyword}%")) ||
                (x.Content != null && EF.Functions.Like(x.Content, $"%{keyword}%")))
            .OrderByDescending(x => x.BaseScore)
            .ThenByDescending(x => x.UpdatedAt);

        return await query.Take(take).ToListAsync(cancellationToken);
    }
}

说明:

  • 第一阶段先保证 EF Core 路径可用。
  • 第二阶段建议改成:
    • FromSqlInterpolated
    • MATCH(title, content) AGAINST (...)
  • 这样能兼顾“先实现”和“后优化”。

10.5 搜索重建服务

建议文件:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\HwSearchRebuildService.cs

建议代码片段:

namespace Admin.NET.Plugin.HwPortal.SearchEf.Service;

/// <summary>
/// 全量重建搜索索引。
/// 当前管理后台的 /portal/search/admin/rebuild 最终会调用这里。
/// </summary>
public class HwSearchRebuildService : IHwSearchRebuildService
{
    private readonly HwPortalSearchDbContext _db;
    private readonly IHwSearchIndexService _indexService;
    private readonly HwWebMenuService _menuService;
    private readonly HwWebService _webService;
    private readonly HwWeb1Service _web1Service;
    private readonly HwWebDocumentService _documentService;
    private readonly HwPortalConfigTypeService _configTypeService;

    public HwSearchRebuildService(
        HwPortalSearchDbContext db,
        IHwSearchIndexService indexService,
        HwWebMenuService menuService,
        HwWebService webService,
        HwWeb1Service web1Service,
        HwWebDocumentService documentService,
        HwPortalConfigTypeService configTypeService)
    {
        _db = db;
        _indexService = indexService;
        _menuService = menuService;
        _webService = webService;
        _web1Service = web1Service;
        _documentService = documentService;
        _configTypeService = configTypeService;
    }

    public async Task RebuildAllAsync()
    {
        // Why
        // 全量重建时,先清空索引表,再重新从业务表扫描一次。
        await _db.Database.ExecuteSqlRawAsync("TRUNCATE TABLE hw_portal_search_doc;");

        List<HwWebMenu> menus = await _menuService.SelectHwWebMenuList(new HwWebMenu());
        foreach (HwWebMenu item in menus)
        {
            await _indexService.UpsertMenuAsync(item);
        }

        List<HwWeb> webs = await _webService.SelectHwWebList(new HwWeb());
        foreach (HwWeb item in webs)
        {
            await _indexService.UpsertWebAsync(item);
        }

        // 其余来源同理继续补齐。
    }
}

10.6 与现有控制器对接

保持现有文件位置不变:

建议改造方式:

  • 控制器路径不改
  • 入参不改
  • 返回 DTO 不改
  • 内部调用从旧 HwSearchService 切到新 HwSearchQueryService

11. 关键代码改造位置

11.1 插件启动注册

当前文件:

当前问题:

  • 只注册了 XML 执行器
  • IHwSearchRebuildService 绑定的是空实现

建议新增注册:

using Microsoft.EntityFrameworkCore;

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<HwPortalMapperRegistry>();
    services.AddScoped<HwPortalMyBatisExecutor>();
    services.AddScoped<PortalSearchDocConverter>();

    // 新增:注册搜索专用 DbContext。
    // Why
    // 搜索子系统是独立读写模型,不和 SqlSugar 主链路抢职责。
    services.AddDbContext<HwPortalSearchDbContext>(options =>
    {
        options.UseMySql(
            "这里读取配置中的搜索连接串",
            ServerVersion.AutoDetect("这里读取配置中的搜索连接串"));
    });

    services.AddScoped<IHwSearchIndexService, HwSearchIndexService>();
    services.AddScoped<HwSearchQueryService>();
    services.AddScoped<IHwSearchRebuildService, HwSearchRebuildService>();
}

11.2 业务服务改造点

需要接入索引增量同步的文件:

建议改造模式:

public class HwWebService : ITransient
{
    private readonly IHwSearchIndexService _searchIndexService;

    public async Task<int> InsertHwWeb(HwWeb input)
    {
        long identity = await _executor.InsertReturnIdentityAsync(Mapper, "insertHwWeb", input);
        input.WebId = identity;

        if (identity > 0)
        {
            // Why
            // 不再做全量重建,而是只更新这一个文档的搜索索引。
            await _searchIndexService.UpsertWebAsync(input);
        }

        return identity > 0 ? 1 : 0;
    }
}

12. 配置设计

建议新增配置文件:

Admin.NET\Admin.NET.Application\Configuration\Search.json

建议内容:

{
  "$schema": "https://gitee.com/dotnetchina/Furion/raw/v4/schemas/v4/furion-schema.json",
  "HwPortalSearch": {
    "Engine": "mysql_fulltext",
    "EnableLegacyFallback": true,
    "ConnectionString": "Server=localhost;Database=hw_portal;Uid=root;Pwd=123456;CharSet=utf8mb4;",
    "BatchSize": 500,
    "TakeLimit": 500
  }
}

建议新增配置类:

Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Option\HwPortalSearchOptions.cs

13. 返回结构兼容要求

现有返回 DTO 不变:

必须保持:

SearchPageDTO

  • total
  • rows

SearchResultDTO

  • sourceType
  • title
  • snippet
  • score
  • route
  • routeQuery
  • editRoute

这样前端调用逻辑不用改。


14. 评分策略建议

建议沿用当前评分思路并做增强:

14.1 基础分

  • menu: 110
  • web1: 90
  • web: 80
  • document: 70
  • configType: 65

14.2 二次加分

  • 标题命中:+20
  • 正文命中:+10
  • 最近更新时间更近:排序优先

14.3 后续可扩展

  • 标题完整命中额外加分
  • 命中次数加权
  • 前台搜索与编辑搜索使用不同的加权策略

15. 方案实施步骤

15.1 第一步:搭建 EF Core 搜索基础设施

  1. 引入 EF Core 10 MySQL Provider
  2. 新建 HwPortalSearchDbContext
  3. 新建 HwPortalSearchDoc 实体
  4. 新建 Search.json 与配置对象
  5. 在插件 Startup 中注册

15.2 第二步:建搜索索引表

  1. 建表 hw_portal_search_doc
  2. 建唯一索引
  3. 建普通索引
  4. 建 FULLTEXT 索引

15.3 第三步:实现索引同步

  1. IHwSearchIndexService
  2. 实现 UpsertMenuAsync
  3. 实现 UpsertWebAsync
  4. 实现 UpsertWeb1Async
  5. 实现 UpsertDocumentAsync
  6. 实现 UpsertConfigTypeAsync

15.4 第四步:实现查询服务

  1. 新建 HwSearchQueryService
  2. 先实现 LIKE 版 EF 查询
  3. 再补 MATCH AGAINST
  4. 复用现有摘要、高亮、路由逻辑

15.5 第五步:替换控制器调用

  1. 保持控制器不变
  2. 改内部服务依赖
  3. 管理端重建接口切到真正重建服务

15.6 第六步:移除空实现

  1. 去掉 HwNoopSearchRebuildService 默认绑定
  2. 保留旧 XML 搜索作为 legacy 回退实现

16. 测试方案

16.1 功能测试

  1. 搜“工业物联网”,能返回多来源结果
  2. 前台搜索返回 route
  3. 编辑搜索返回 editRoute
  4. 标题命中排在正文命中前面
  5. 空结果时返回空分页对象

16.2 增量同步测试

  1. 新增页面后立即可搜
  2. 修改文档标题后立即可搜到新标题
  3. 删除菜单后搜索不再命中

16.3 重建测试

  1. 执行 /portal/search/admin/rebuild
  2. 清空索引表后重新生成
  3. 重建后结果数与原业务数据量匹配

16.4 性能测试

  1. 对比旧版 UNION ALL + LIKE
  2. 高频关键词响应时间明显下降
  3. 数据量增长后查询性能仍可接受

17. 风险与注意事项

17.1 风险点

  1. 当前仓库还未引入 EF Core 依赖
  2. 当前本机没有 .NET SDK暂时不能做真实编译验证
  3. MySQL FULLTEXT 对中文分词能力有限
  4. 如果业务文本大量是 JSON必须先清洗文本否则全文检索效果很差

17.2 风险应对

  1. 先保留旧搜索实现作为 legacy 回退
  2. 搜索文档始终存“清洗后的文本”
  3. 先做功能正确,再做 MATCH AGAINST 优化
  4. 若后续中文搜索质量不够,再评估:
    • ngram parser
    • ES
    • PostgreSQL + 中文分词

18. 当前不做的内容

本次方案明确不做:

  1. 向量搜索
  2. embedding
  3. 大模型召回
  4. Elasticsearch
  5. 把整个 hw-portal 全量迁到 EF Core

19. 当前建议的实际落地文件清单

19.1 必改

19.2 建议新增

  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\HwPortalSearchDbContext.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Entity\HwPortalSearchDoc.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Option\HwPortalSearchOptions.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\IHwSearchIndexService.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\HwSearchIndexService.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\HwSearchQueryService.cs
  • Admin.NET\Plugins\Admin.NET.Plugin.HwPortal\SearchEf\Service\HwSearchRebuildService.cs
  • Admin.NET\Admin.NET.Application\Configuration\Search.json

20. 总结

当前 hw-portal 的搜索还不是“搜索引擎”,本质上仍是业务表上的 SQL 模糊匹配。
本次方案的核心不是“把 LIKE 换成 EF”而是

  1. 建独立搜索索引表
  2. 用 EF Core 10 管理搜索索引子系统
  3. 用 MySQL FULLTEXT + LIKE 兜底做普通搜索引擎
  4. 保持现有接口和返回结构不变
  5. 把空的索引重建能力变成真正可用的全量/增量索引能力

如果后续你要继续深化,我建议下一份文档再单独写:

  • 《hw-portal 搜索表 SQL 脚本与迁移方案》
  • 《hw-portal 搜索改造详细代码实施清单》
  • 《hw-portal 搜索测试用例与验收清单》

最自律帅气聪明的臧辰浩