Python后端中间件专题18:搜索不是只输入一个词——过滤、排序与高亮
Python后端中间件专题18搜索不是只输入一个词——过滤、排序与高亮第 17 课为标题和正文建了全文字段为租户、状态和标签保留了精确字段。现在客服输入“VPN 登录”只想看本租户的 open/high 工单。一个搜索框背后至少有五件事处理关键词、强制租户边界、叠加业务过滤、限制返回量、解释命中的位置。漏了第一件会搜不到漏了第二件是数据事故。本课从一份具体的查询请求倒推 DSL最后让build_search_body()同时承担“用户可以选择什么”和“用户永远不能取消什么”。上一课练习答案答案 EX-17-01 [code]将下面完整脚本保存为mapping_probe.py从project/运行。它不是手抄一份期望 JSON而是直接读取 TicketFlow 的模块字段类型被误改时断言会失败。fromticketflow.search.mappingimportticket_index_definition definitionticket_index_definition()propertiesdefinition[mappings][properties]assertdefinition[mappings][dynamic]strictassertproperties[ticket_id][type]keywordassertproperties[tenant_id][type]keywordassertproperties[status][type]keywordassertproperties[title][type]textprint(dynamicdefinition[mappings][dynamic])forfieldin(ticket_id,tenant_id,status,title):print(f{field}{properties[field][type]})从project/运行命令python mapping_probe.py预期输出dynamicstrict ticket_idkeyword tenant_idkeyword statuskeyword titletext这组断言把“租户必须精确匹配”落实为可执行合同它仍没有访问真实 ES所以不要把keyword配置正确误报成索引已经建成。答案 EX-17-02 [prose]用户关键词“VPN”进入bool.must内的multi_match因为它决定是否命中并影响相关性。tenant_id当前租户必须进入不可绕过的bool.filterstatusopen与priorityhigh也是filter它们是精确的是/否条件不该贡献_score。如果把租户放进全文相关性查询一方面它会被分析器处理而失去精确边界另一方面调用方可能为了空关键词把整个must删除顺手删除租户条件。过滤器必须由服务端用认证上下文构造绝不能从用户提交的tenant_id原样信任。一张请求拆成三条不同的规则假设已经认证出tenant-a。用户提交{q: VPN 登录 ,statuses:[open],priority:high,size:20}先去掉关键词两端空格但不能去掉租户过滤。其等价查询骨架是{query:{bool:{filter:[{term:{tenant_id:tenant-a}},{terms:{status:[open]}},{term:{priority:high}}],must:[{multi_match:{query:VPN 登录,fields:[title,body],type:best_fields,operator:and}}]}}}本课lesson-18的best_fields在多个字段里选最匹配的字段operator: and要求查询词在同一个字段的匹配语境中满足要求它不是“跨所有字段任意拼凑”的万能 AND。第 19 课再引入字段 boost 与游标。本文先把“硬过滤”和“全文相关性”分开避免一开始就被评分公式淹没。空关键词不是空租户用户清空搜索框仍可以浏览当前租户、当前状态的列表。build_search_body()这时只把must换成match_all保留filterfromticketflow.search.queryimportSearchFilters,build_search_body bodybuild_search_body(tenant_idtenant-a,query ,filtersSearchFilters(statuses(open,)),size5,)assertbody[query][bool][filter][0]{term:{tenant_id:tenant-a}}assertbody[query][bool][must][{match_all:{}}]print(tenant filter retained; empty query becomes match_all)将上述片段保存为该文件再从project/运行命令python empty_query_probe.py。预期输出tenant filter retained; empty query becomes match_all这是很值得单测的安全性质如果未来开发者为“空搜索框”走一条完全不同的查询分支跨租户泄漏往往就出在这种分支里。排序、高亮、上限搜索结果还要能用本课先按updated_at desc、再按ticket_id asc排序。同一更新时间下必须有第二个唯一稳定键不然连续两页可能重排。这里不用 ES 内部_idElasticsearch 8 不支持把它当普通字段稳定排序第 17 课已经把业务 ID 复制到keyword类型的ticket_id。size被限制在1..100防止用户要求一次返回几十万条。这里还不是深分页方案第 19 课会增加search_after。高亮指定title、bodyES 命中时可能返回em.../em片段。它是显示辅助不是可信 HTML前端要采用安全渲染策略不能把任意工单正文和高亮片段直接当未净化 HTML 注入页面。高亮为空也不等于没有命中召回和展示是两件事。track_total_hits: true让页面取得准确总数但在大规模查询上有成本。如果产品只需要“还有下一页”后续可以单独评估上限与性能不要为了看起来完整默认所有搜索都必须精确统计百万级总数。用测试攻击查询构造器从课程根运行本课独立 checkpointpython tools/verify_executable_revisions.py--lesson 18--python python正式 revision 命令在隔离的课时树运行 baseline 与至 18 的历史合同不是最终项目的单项 unit1 passed。远端 selectortests/integration/test_search_elasticsearch.py::test_real_search_enforces_tenant_status_sort_highlight_without_leakage用唯一 ES 索引投放同词的跨租户、跨 status 工单核对真实查询的租户/status 过滤、排序、高亮及无泄漏其未运行结果是 PENDING。如果你暂时没有真实 ESAPI 请求尚不能证明真实索引返回了什么只声称构造器合同的本地结果。真实 ES 查询和租户负例留在远端集成验收中未跑标SKIP。出错时先问哪一层两租户同名工单都命中看服务端是否始终插入tenant_id的term不能只看页面是否隐藏了对方结果。筛选open却返回closed检查 Mapping 是否keyword、过滤值是否正确、当前索引是否仍是旧 alias。搜索“VPN 登录”没有结果先用_analyze看 token再确认operator: and是否符合产品意图不是简单把租户过滤删掉。高亮字段为空先核实查询是否命中正文、是否开启 highlight然后分清结果命中与高亮展示。size10000报错这正是防护应分页或改导出作业不扩大 HTTP 查询上限。本篇的边界可以一句话检验用户能选业务过滤和关键词却不能决定自己属于哪个租户。本课练习练习 EX-18-01 [code]保存query_guard_probe.py调用build_search_body()两次一次给出“VPN 登录 open/high”一次给出全空格关键词断言两次第一条 filter 都是当前租户第二次must为match_all并打印两次的must。第 19 课给出完整答案。练习 EX-18-02 [prose]解释为什么updated_at desc后还要加ticket_id asc再说明为何稳定的排序键并不自动保证“索引发生更新时跨多页绝对无重无漏”。想一想第 19 课引入游标后仍有哪些边界。下一篇预告第 19 课会从一次“排序结果看起来不合理”的客服反馈出发解释title^3的权重、search_after游标和聚合你会看到“相关性”是可调可测的产品选择而非一个神秘分数。本课完整核心模块search/query.py以下是lesson-18时点的完整模块。它还没有第 19 课的 boost 和 cursor这是有意保留的教学增量。from__future__importannotationsfromdataclassesimportdataclassdataclass(frozenTrue,slotsTrue)classSearchFilters:statuses:tuple[str,...]()priority:str|NoneNonetags:tuple[str,...]()defbuild_search_body(*,tenant_id:str,query:str,filters:SearchFilters,size:int,)-dict[str,object]:Build a bounded search request with a mandatory tenant boundary.ifnot1size100:raiseValueError(size must be between 1 and 100)exact_filters:list[dict[str,object]][{term:{tenant_id:tenant_id}}]iffilters.statuses:exact_filters.append({terms:{status:list(filters.statuses)}})iffilters.priorityisnotNone:exact_filters.append({term:{priority:filters.priority}})iffilters.tags:exact_filters.append({terms:{tags:list(filters.tags)}})normalized_queryquery.strip()must:list[dict[str,object]]([{multi_match:{query:normalized_query,fields:[title,body],type:best_fields,operator:and,}}]ifnormalized_queryelse[{match_all:{}}])body:dict[str,object]{query:{bool:{filter:exact_filters,must:must}},sort:[{updated_at:desc},{ticket_id:asc}],highlight:{fields:{title:{},body:{}}},size:size,track_total_hits:True,}returnbody

相关新闻

Python后端中间件专题17:`LIKE` 查不到想要的工单——倒排索引与 Mapping

Python后端中间件专题17:`LIKE` 查不到想要的工单——倒排索引与 Mapping

Python后端中间件专题17:LIKE 查不到想要的工单——倒排索引与 Mapping 客服说:“我明明记得有一张 VPN 登录失败的工单,为什么输入‘登录失败’能找到,输入‘VPN 凭据’却没有?”如果数据库里只有几十张工单&#xff…

2026/10/12 2:21:19 阅读更多 →
mlpack 安装完全指南:从包管理器、源码编译到语言绑定与测试程序

mlpack 安装完全指南:从包管理器、源码编译到语言绑定与测试程序

人工智能机器学习深度学习 【免费下载链接】mlpack mlpack: a fast, header-only C machine learning library 项目地址: https://gitcode.com/gh_mirrors/ml/mlpack 点击查看 免费下载 本文以 mlpack 官方安装文档为核心,系统梳理在 C 程序、命令行、P…

2026/10/12 2:20:18 阅读更多 →
ant-design-blazor Spin 卡片加载中(嵌套模式)完全指南:让现有容器一键进入加载状态

ant-design-blazor Spin 卡片加载中(嵌套模式)完全指南:让现有容器一键进入加载状态

前端UI组件设计系统 【免费下载链接】ant-design-blazor 基于 Ant Design 与 Blazor 的前端组件库。让开发者解放生产力,实现更大价值。 项目地址: https://gitcode.com/ant-design-blazor/ant-design-blazor 点击查看 免费下载 导读 Spin(…

2026/10/12 2:20:18 阅读更多 →

最新新闻

Apache Beam 依赖管理实践指南:从依赖冲突到升级政策的完整解读

Apache Beam 依赖管理实践指南:从依赖冲突到升级政策的完整解读

批处理流处理大数据 【免费下载链接】beam Apache Beam is a unified programming model for Batch and Streaming data processing. 项目地址: https://gitcode.com/gh_mirrors/beam15/beam 点击查看 免费下载 导读 本文以 Apache Beam 官方《Dependencies Guide…

2026/10/12 3:20:57 阅读更多 →
环境变量配置架构决策记录(ADR)解析:.env、.env.defaults 与 .env.schema 三件套实战

环境变量配置架构决策记录(ADR)解析:.env、.env.defaults 与 .env.schema 三件套实战

【免费下载链接】architecture-decision-record Architecture decision record (ADR) examples for software planning, IT leadership, and template documentation 项目地址: https://gitcode.com/gh_mirrors/ar/architecture-decision-record 点击查看 免费下载 …

2026/10/12 3:20:57 阅读更多 →
智算网络排障手记:用原厂工具秒杀AllReduce训练锯齿问题

智算网络排障手记:用原厂工具秒杀AllReduce训练锯齿问题

AllReduce 训练一直锯齿?手把手教你用 hccn_tool 交换机命令,30 分钟定位 RoCEv2 拥塞根因 如果你的 AllReduce 训练出现 Step Time 锯齿、吞吐掉 30%、Wireshark 看到 PSN 重传——大概率不是网卡坏了,而是拥塞控制链路出了三件套&#xff…

2026/10/12 3:20:57 阅读更多 →
Cobra 手册页生成实战:为你的 Go CLI 命令一键产出 man page

Cobra 手册页生成实战:为你的 Go CLI 命令一键产出 man page

云原生可观测性容器编排运维 【免费下载链接】scope Monitoring, visualisation & management for Docker & Kubernetes 项目地址: https://gitcode.com/gh_mirrors/sc/scope 点击查看 免费下载 导读 本文基于仓库中 vendor/github.com/spf13/cobra/man_d…

2026/10/12 3:20:57 阅读更多 →
注解(Annotation)原理与自定义注解实战:你不懂的注解背后究竟发生了什么?

注解(Annotation)原理与自定义注解实战:你不懂的注解背后究竟发生了什么?

全文目录:开篇语前言一、注解是什么?注解的基本用法1. 基本语法二、注解的原理:编译期、类加载期、运行时的作用1. 编译期(注解不参与程序运行)2. 类加载期(注解可以用作元数据)3. 运行时&#…

2026/10/12 3:20:57 阅读更多 →
PLC基本指令详解:从位逻辑到定时计数,掌握梯形图编程核心

PLC基本指令详解:从位逻辑到定时计数,掌握梯形图编程核心

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 3:19:56 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →