ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

mdBook 按章节禁用搜索索引实战:`[output.html.search.chapter]` 配置与源码机制全解析

mdBook 按章节禁用搜索索引实战:`[output.html.search.chapter]` 配置与源码机制全解析 开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载本篇技术指南围绕 mdBook 的搜索功能级联控制能力展开通过book.toml中的[output.html.search.chapter]配置你可以精确地让某些章节或整个目录不出现在站点搜索索引中。文章将以仓库中的disable_search_chapter测试用例为主体结合官方渲染器配置文档与 HTML 后端源码讲清配置写法、路径匹配规则、优先级合并逻辑、错误校验以及测试验证方法读完即可在自己的书籍项目中落地该功能。一、功能定位为什么需要按章节禁用搜索索引mdBook 默认会把所有章节的正文、标题和面包屑写入searchindex.js供前端 searcher 检索。但某些场景下部分内容并不适合被检索例如附录、术语表或辅助目录内容琐碎检索命中反而稀释结果质量草稿、废弃章节尚未完成或即将移除不希望被用户搜到包含大量代码或噪音文本的页面token 过多会干扰相关性排序。[output.html.search.chapter]正是为此设计的细粒度开关。它允许以「章节源文件路径」或「目录路径」为 key逐章甚至逐目录地关闭搜索索引且支持递归合并与更具体路径优先。二、测试用例剖析disable_search_chapter 的目录结构仓库中用于验证该功能的集成测试位于 tests/testsuite/search/disable_search_chapter其结构如下disable_search_chapter/ ├── book.toml └── src/ ├── SUMMARY.md ├── second.md ├── first/ │ ├── disable_me.md │ └── keep_me.md └── second/ └── nested.md其中 second/nested.md 内容仅为一个一级标题# Second Nested它作为「目录级禁用」的验证对象配置中禁用了second目录下的所有章节因此这个嵌套子章节连同目录首页second.md中的标题都不会进入索引。SUMMARY.md 定义了四层书结构# Summary - [Keep Me](https://link.gitcode.com/i/25c1056380279b9ca1d0987c9938323d) - [Disable Me](https://link.gitcode.com/i/88bec8227cb42e65d747b3902fae8d43) - [Second](https://link.gitcode.com/i/f17ada920544c78d1738253f9d8c5b50) - [Second Nested](https://link.gitcode.com/i/ae951eb4e64c0c4857ad86de042f4fff)可以看到测试刻意构造了两类禁用目标一个是单个文件first/disable_me.md一个是整个目录second包含second.md与second/nested.md从而分别验证两种 key 写法。三、核心配置详解book.toml 的两种写法该测试的 book.toml 是理解本功能的最佳入口[book] title disable_search_chapter [output.html.search.chapter] second { enable false } first/disable_me.md { enable false }3.1 key 的取值规则根据官方配置文档 guide/src/format/configuration/renderers.md#L296-L308 的说明Each key is the path to the chapter source file or directory, and the value is a table of settings to apply to that path.即 key 可以是章节源文件的路径如first/disable_me.md仅影响该单个文件目录的路径如second递归影响该目录及其子目录下的所有章节包括目录首页second.md与嵌套文件second/nested.md。路径均相对于src目录。官方文档给出的典型组合示例[output.html.search.chapter] # Disables search indexing for all chapters in the appendix directory. appendix { enable false } # Enables search indexing for just this one appendix chapter. appendix/glossary.md { enable true }3.2 唯一的设置项enableenable布尔值默认true控制对应章节是否被写入搜索索引。需要特别注意两点官方文档明确强调该开关不会覆盖全局的output.html.search.enable。全局开关必须为true任何搜索功能才会启用[output.html.search.chapter]只是在此基础上的细化控制。官方建议谨慎使用禁用索引后用户搜索相关术语却找不到本应存在的内容容易造成困惑。仅在「保留该章节会明显拉低检索结果质量」等例外情况下才应关闭。3.3 递归合并与更具体路径优先[output.html.search.chapter]表支持路径前缀匹配并递归合并设置更具体的路径优先级更高。这正是「先禁用整个目录、再单独放行其中一章」组合写法能生效的原因目录级enable false与文件级enable true同时存在时文件级胜出。四、源码级验证从配置到索引的完整链路配置如何作用于最终的searchindex.js核心逻辑集中在 crates/mdbook-html/src/html_handlebars/search.rs整个处理链路由四个函数串联。4.1 入口create_files 中的过滤判断create_files 函数 先对配置做排序与校验再遍历所有章节树逐章决定是否索引let chapter_configs sort_search_config(search_config.chapter); validate_chapter_config(chapter_configs, chapter_trees)?; for ct in chapter_trees { let path settings_path(ct.chapter); let chapter_settings get_chapter_settings(chapter_configs, path); if !chapter_settings.enable.unwrap_or(true) { continue; // 跳过索引 } index_chapter(mut index, search_config, mut doc_urls, ct)?; }关键点在于enable.unwrap_or(true)未在配置中出现的章节默认视为启用只有显式配置enable false才会被continue跳过。被跳过的章节完全不会出现在doc_urls与 elasticlunr 索引中前端搜索自然搜不到。4.2 路径解析settings_pathsettings_path 函数 决定匹配时使用的路径基准——优先取章节的source_path源文件路径缺失时回退到pathfn settings_path(ch: Chapter) - Path { ch.source_path .as_deref() .unwrap_or_else(|| ch.path.as_deref().unwrap()) }4.3 配置排序sort_search_configsort_search_config 函数 把HashMap转为按PathBuf排序的向量。源码注释特别提醒Note: This is case-sensitive, and assumes the author uses the same case as the actual filename.也就是说匹配区分大小写配置 key 的大小写必须与实际文件名一致否则可能校验通过但匹配不上预期的章节。4.4 合并与优先级get_chapter_settingsget_chapter_settings 函数 是「更具体路径优先」的落地实现遍历已排序的配置项凡是source_path.starts_with(path)的配置都会参与合并后遍历到的即更长的、更具体的路径因排序位于后方其enable值覆盖前面的let mut result SearchChapterSettings::default(); for (path, config) in chapter_configs { if source_path.starts_with(path) { result.enable config.enable.or(result.enable); } } result由于排序保证目录路径在前、文件路径在后文件级配置自然覆盖目录级配置。源码中的单元测试 chapter_settings_priority 精确验证了这一优先级矩阵cli { enable false }使cli/index.md关闭而更具体的cli/watch.md { enable true }令其开启cli/inner { enable true }与更具体的cli/inner/foo.md { enable false }组合后cli/inner/foo.md最终为关闭。4.5 配置校验validate_chapter_configvalidate_chapter_config 函数 保证配置不会「静默失效」每个配置 key 必须至少前缀匹配一个章节路径否则构建直接失败并报错。对应的集成测试 chapter_settings_validation_error 验证了错误信息格式ERROR Rendering failed [TAB]Caused by: [output.html.search.chapter] key does-not-exist does not match any chapter paths这意味着拼错路径不会悄无声息地忽略配置而是会在mdbook build阶段立即暴露。五、效果验证测试断言与预期行为集成测试 can_disable_individual_chapters 直接构建了disable_search_chapter这本书解析生成的searchindex*.js后对doc_urls做断言assert!(contains(second.html)); // 目录首页 second.md 仍在 assert!(!contains(second/)); // 嵌套子章节已剔除 assert!(!contains(first/disable_me.html)); // 单文件禁用生效 assert!(contains(first/keep_me.html)); // 未配置的章节保留这四条断言清晰刻画了最终行为章节配置doc_urls 表现first/keep_me.md未配置默认启用first/keep_me.html出现在索引中first/disable_me.mdfirst/disable_me.md { enable false }first/disable_me.html不在索引中second.md目录首页second { enable false }second.html仍在索引中second/nested.md嵌套子章节继承目录级second { enable false }second/nested.html不在索引中其中「second.html仍在」是唯一可能让初学者意外的点目录 keysecond命中的是src/second目录下的所有文件包括nested.md而src/second.md是独立于该目录的另一个文件其路径src/second.md并不以目录src/second为前缀因此不受影响。这个微妙差异正好印证了settings_path与starts_with前缀匹配的精确语义——测试作者刻意构造了这个场景来锁定行为。六、实操指南在自己的书籍中落地6.1 最小配置示例假设书籍结构如下src/ ├── SUMMARY.md ├── chapter_1.md └── appendix/ ├── glossary.md └── faq.md希望在搜索中隐藏整个appendix目录但保留其中的faq.md[book] title My Book [output.html.search] enable true [output.html.search.chapter] appendix { enable false } appendix/faq.md { enable true }构建后执行mdbook build可在生成的book/searchindex.js中确认appendix/glossary.html已消失、appendix/faq.html与chapter_1.html正常存在。6.2 常见问题排查配置了路径但索引未变化先确认 key 大小写与src下实际文件名完全一致源码匹配区分大小写再确认路径确实以章节源文件路径为前缀注意区分src/second.md与src/second/目录是两个不同对象。构建报 does not match any chapter paths说明某个 key 未匹配任何章节通常为路径笔误该错误信息由validate_chapter_config抛出用于主动拦截无效配置。全局开关被误关若[output.html.search]中enable false则[output.html.search.chapter]全部失效——后者不覆盖前者。想确认实际索引内容参考 tests/testsuite/search.rs 中read_book_index的解析方式将book/searchindex*.js还原为 JSON 后检查doc_urls数组。6.3 与搜索其他配置的配合[output.html.search]表还包含heading-split-level默认3决定按几级标题切分检索段落与copy-js默认true控制是否复制 searcher 前端脚本等选项[output.html.search.chapter]仅负责「哪些章节入索引」这一维度两者正交、可自由组合。章节级enable判断发生在索引构建的最前端被禁用的章节不会产生任何 token、doc 条目或前端搜索命中因此在索引体积与搜索质量上都能得到立竿见影的控制效果。七、总结[output.html.search.chapter]是 mdBook HTML 后端为搜索功能提供的细粒度治理能力以文件或目录路径为 key、以enable为唯一开关、以前缀匹配实现递归合并、以更具体路径覆盖目录级设置并在配置无效时于构建期报错。本文以仓库 disable_search_chapter 测试用例为骨架对照 search.rs 源码与官方文档 renderers.md完整还原了从book.toml配置到searchindex.js产物的全链路。掌握该配置后你可以在不影响全局搜索的前提下为目录、草稿或噪音页面精准关闭检索入口实现更干净、更高质量的站内搜索体验。赞分享开发工具文档【免费下载链接】mdBookCreate book from markdown files. Like Gitbook but implemented in Rust项目地址https://gitcode.com/gh_mirrors/md/mdBook点击查看免费下载相关推荐mdBook 索引章节机制剖析README.md 如何生成 index.html 与侧边栏高亮逻辑mdBook 索引章节机制剖析README.md 如何生成 index.html 与侧边栏高亮逻辑 本指南以仓库中 tests/gui/books/index开发工具文档Gitea搜索引擎全文检索与代码搜索配置Gitea搜索引擎全文检索与代码搜索配置 引言解决Gitea自托管环境中的搜索痛点 你是否在使用Gitea时遇到过这些问题代码库数量激增后找不到关键项目文后端代码托管研发协作CI/CDmdBook 中 Rust 代码块 Playground 运行按钮的启用、禁用与源码解析mdBook 中 Rust 代码块 Playground 运行按钮的启用、禁用与源码解析 mdBook 会自动为 Markdown 文档中的 Rust 代码块附开发工具文档创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进