【直播切片工作台】第34章:关键词检索体系

简介: 本章详解关键词检索DSL实现:服务层解析`keywords`参数(逗号=AND、竖线=OR),生成二维结构;仓储层将其安全翻译为参数化SQL,支持任意字段模糊匹配,全程零注入风险。(239字)

第34章:关键词检索体系

任务列表、素材列表等多个查询接口都支持 keywords 参数:"," 表与、"|" 表或,如 发布会,精剪|一键 表示"(发布会 AND 精剪)OR 一键"。本章解析这套小型查询 DSL 的两层实现——service/keywords.go 负责解析,repository/keyword_filter.go 负责翻译为 SQL。

流程

表达式先在服务层解析为二维结构 KeywordGroups:外层是 OR 组,内层是 AND 词。仓储层再把每组拼成 SQL 片段,组间 OR 连接,最终成为一条 WHERE 子句。

flowchart LR
    A["游戏,周末|发布会,2026"] --> B["parseKeywordExpr<br/>service 层"]
    B --> C["[[&quot;游戏&quot;,&quot;周末&quot;],<br/>[&quot;发布会&quot;,&quot;2026&quot;]]"]
    C --> D["applyKeywordGroups<br/>repository 层"]
    D --> E["WHERE ((t.video_project_name LIKE ? AND LIKE ?)<br/>OR (LIKE ? AND LIKE ?))"]
    E --> F["参数依次绑定<br/>%游戏% %周末% %发布会% %2026%"]

匹配目标由调用方决定:任务列表模糊匹配 video_project_name,素材列表匹配 name 等字段——termClause 参数化的设计让同一套分组逻辑可复用于任意表与任意列组合。

实现

parseKeywordExpr 的健壮性体现在"三层清洗":先 Trim 整串、再丢弃空 OR 段、再对组内每个词 Trim + 转小写 + 丢弃空词。因此 "游戏,,周末 | " 这类含杂质输入也能安全解析为 [["游戏","周末"]],不会产生空串 LIKE(空 pattern 的 % 会匹配所有行,语义灾难)。

小写化的动机:不同字段在 SQL 侧用 LOWER(col) LIKE ? 匹配(termClause 由调用方决定大小写处理),解析层统一小写保证两侧约定一致——Go 侧 strings.ToLower 与 PostgreSQL 的 LOWER 对中文行为无差,对英文等场景则对齐了语义。

applyKeywordGroups 是典型的参数化 SQL 组装:termClause 是含 ? 占位符的字符串模板(如 (LOWER(t.video_project_name) LIKE ?)),每个词按 argsPerTerm 重复绑定同一 %kw% pattern。整个 WHERE 串的每个占位符都有对应实参,全程零字符串拼接用户输入——没有注入面。空组、空 clause、非正参数个数的守卫让函数对任何非法输入都直接返回原查询。

分组的 SQL 形态是 (A AND B) OR (C AND D)——括号保证 AND/OR 优先级不被误解,这是手工拼布尔逻辑时最常见的事故点,这里用 strings.Join 加显式括号彻底规避。

📌 设计决策

  • 逗号=AND、竖线=OR 的选型:与多数电商筛选习惯(逗号表并列 OR)相反,这里取"逗号收窄、竖线扩展"——搜索"发布会 精剪"时用户预期是同时满足。文档与 Swagger 注释里反复强调该语义。
  • 词法层下沉服务、SQL 层下沉仓储:解析不碰数据库,组装不碰字符串清洗,两层各自可测;KeywordGroups 是唯一契约类型。
  • 不引入全文检索引擎:LIKE %kw% 无法走索引,但项目规模(单实例、万级任务量)下足够;换 pg_trgm 或 ES 的成本在当前阶段不划算。
  • termClause 参数化而非列名硬编码:同一函数可对多列(name LIKE ? OR remark LIKE ?,argsPerTerm=2)复用,扩展一个搜索面只改一个字符串。

代码示例

片段 1 展示表达式解析(摘自 service/keywords.go,完整函数):

// parseKeywordExpr 解析关键词表达式:
//   - "," 分隔的词为「与」(AND)
//   - "|" 分隔的组为「或」(OR)
//
// 例:"游戏,周末|发布会,2026" => [["游戏","周末"],["发布会","2026"]]。
// 空白与空段会被丢弃;词统一转小写。
func parseKeywordExpr(raw string) repository.KeywordGroups {
   
    raw = strings.TrimSpace(raw)
    if raw == "" {
   
        return nil
    }
    orParts := strings.Split(raw, "|")
    groups := make(repository.KeywordGroups, 0, len(orParts))
    for _, orPart := range orParts {
   
        orPart = strings.TrimSpace(orPart)
        if orPart == "" {
   
            continue
        }
        andParts := strings.Split(orPart, ",")
        group := make([]string, 0, len(andParts))
        for _, part := range andParts {
   
            kw := strings.ToLower(strings.TrimSpace(part))
            if kw != "" {
   
                group = append(group, kw)
            }
        }
        if len(group) > 0 {
   
            groups = append(groups, group)
        }
    }
    if len(groups) == 0 {
   
        return nil
    }
    return groups
}

片段 2 展示 SQL 组装(摘自 repository/keyword_filter.go,完整核心函数):

// applyKeywordGroups 将关键词表达式应用到查询:组内 AND、组间 OR。
// termClause 为单个词的匹配 SQL(可含多个 ?),argsPerTerm 为每个词需要绑定的参数个数;
// 每个 ? 都会绑定同一 pattern "%kw%"。
func applyKeywordGroups(query *gorm.DB, groups KeywordGroups, termClause string, argsPerTerm int) *gorm.DB {
   
    if KeywordGroupsEmpty(groups) || termClause == "" || argsPerTerm <= 0 {
   
        return query
    }
    var orParts []string
    args := make([]interface{
   }, 0)
    for _, group := range groups {
   
        if len(group) == 0 {
   
            continue
        }
        andParts := make([]string, 0, len(group))
        for _, kw := range group {
   
            andParts = append(andParts, "("+termClause+")")
            pattern := "%" + kw + "%"
            for i := 0; i < argsPerTerm; i++ {
   
                args = append(args, pattern)
            }
        }
        orParts = append(orParts, "("+strings.Join(andParts, " AND ")+")")
    }
    if len(orParts) == 0 {
   
        return query
    }
    return query.Where(strings.Join(orParts, " OR "), args...)
}

小结

不到一百行代码支撑了全系统的搜索筛选:一层词法、一层 SQL 翻译,中间用一个二维字符串切片做契约。参数化占位符加显式括号,让"动态拼 WHERE"这一高危操作保持了零注入面。

思考题

  1. 若用户输入 游戏,周末,发布会,直播,带货,讲解,拆解,复盘(一个 8 词 AND 组),LIKE 链条对查询计划的性能影响如何?是否需要限制组内词数上限?
  2. 关键词统一小写后,若 termClause 未配合 LOWER(col),英文大小写匹配会静默失效——这类跨层契约你会用什么机制(测试、断言、注释规范)来守住?

项目信息

  • GitHub仓库:Chyona/live-mixer
相关文章
|
2月前
|
人工智能 缓存 安全
阿里云最新版通义千问大模型全功能解析:百万上下文+全模态Agent企业落地指南
随着生成式AI技术持续迭代,阿里云通义千问完成多轮基座升级,形成覆盖旗舰高性能、均衡通用、高速轻量、全模态视听理解的完整模型家族,不再局限简单问答对话,进化成具备长文本深度理解、多模态感知、自主智能体执行、工程级代码开发能力的通用AI底座,面向普通个人用户、独立开发者、中小企业以及大型政企提供分层能力输出,既可以网页端直接交互使用,也可以通过阿里云百炼平台API接口集成到业务系统,还支持开源权重本地私有化部署,适配不同层级业务诉求。
1934 1
|
11月前
|
机器学习/深度学习 人工智能 搜索推荐
构建AI智能体:七十一、模型评估指南:准确率、精确率、F1分数与ROC/AUC的深度解析
本文系统介绍了机器学习模型评估的核心指标与方法。首先阐述了混淆矩阵的构成(TP/FP/FN/TN),并基于此详细讲解了准确率、精确率、召回率和F1分数的计算原理和适用场景。特别指出准确率在不平衡数据中的局限性,强调精确率(减少误报)和召回率(减少漏报)的权衡关系。然后介绍了ROC曲线和AUC值的解读方法,说明如何通过调整分类阈值来优化模型性能。最后总结了不同业务场景下的指标选择策略:高精度场景侧重精确率,高召回场景关注召回率,平衡场景优选F1分数,不平衡数据则推荐使用AUC评估。
1196 20
|
7月前
|
人工智能
HappyHorse 1.0 系列模型使用指南
HappyHorse 1.0 是一款基于原生多模态架构的新一代 AI 视频生成模型,支持音视频协同生成;产品深度适配广告营销、电商展示、短剧制作与社交媒体创意等内容生产场景。
|
11月前
|
机器学习/深度学习 缓存 物联网
打造社交APP人物动漫化:通义万相wan2.x训练优化指南
本项目基于通义万相AIGC模型,为社交APP打造“真人变身跳舞动漫仙女”特效视频生成功能。通过LoRA微调与全量训练结合,并引入Sage Attention、TeaCache、xDIT并行等优化技术,实现高质量、高效率的动漫风格视频生成,兼顾视觉效果与落地成本,最终优选性价比最高的wan2.1 lora模型用于生产部署。(239字)
2954 106
|
3月前
|
设计模式 Web App开发 人工智能
【AI】Agent 全栈进阶|系统化学习路线专题
描述 Agent 的概念、核心构成,规划出一套循序渐进的 Agent 开发学习路径,从大模型调用、工具调用、RAG、运行模式、记忆机制再到工程化调试,同时附上多款适合入门钻研的开源参考项目
2666 7
|
11月前
|
消息中间件 人工智能 NoSQL
AgentScope x RocketMQ:打造企业级高可靠 A2A 智能体通信基座
Apache RocketMQ 推出轻量级通信模型 LiteTopic,专为 AI 场景设计,结合 A2A 协议与 AgentScope 框架,实现多智能体高效、可靠协作,支持海量会话持久化、断点续传与动态订阅,重塑企业级 AI 应用架构。
534 28
|
12月前
|
人工智能 定位技术
千问APP来咯!会聊天,能办事,还免费!!
千问APP公测上线!基于全新Qwen3模型,打造全能AI助手,覆盖办公、地图、健康、购物等多场景,免费畅享智能聊天与办事体验。即刻下载,让AI成为你的日常伙伴。
8263 3
|
12月前
|
人工智能 自然语言处理 大数据
💡 反常识观点:好的项目计划书不是写出来的,是问出来的【提示词工程】
深度解析项目计划书从"写作思维"到"问答思维"的认知革命,通过完整的AI指令框架和实战案例,帮助开发者掌握深度问答方法,提升项目决策质量和成功概率。文章强调AI不是写作工具,而是思维升级的助推器。
556 11
|
人工智能 自然语言处理 开发者
周报不是流水账,这个AI指令帮你写出让老板点赞的工作汇报
一个帮助技术人快速生成专业工作周报的AI指令,通过结构化输入和价值导向表达,让你的周报从流水账变成让老板点赞的高质量汇报,15分钟搞定原本需要1小时的周报撰写。
2548 80

热门文章

最新文章