docs(adr): ADR-0005: Server-Side Search Index
@aRustyDev 已经在做这个了。
开始于 2026年1月5日。
评估
这个 Issue 还没有评估数据。
描述
ADR-0005: Server-Side Search Index
Status
Accepted
Context
MDBook's default HTML renderer includes client-side search powered by elasticlunr.js. The search index is loaded as JSON and executed in the browser.
For an HTMX-based architecture, we need to decide where search execution happens.
Decision Drivers
- HTMX Philosophy - Server returns HTML, not JSON
- Performance - Large books have large indexes
- Authorization - Search results should respect access control
- Simplicity - Minimize client-side JavaScript
Options Considered
Option A: Client-Side Search (like mdbook-html)
Ship elasticlunr.js and JSON index to browser.
<input type="search" onkeyup="search(this.value)">
<script>
const index = elasticlunr.Index.load(searchIndex);
function search(query) {
const results = index.search(query);
document.getElementById('results').innerHTML = renderResults(results);
}
</script>
Pros:
- No server required
- Instant results (after index load)
- Works offline
Cons:
- Large index download (100KB+ for big books)
- Slow initial load
- JavaScript required
- No auth filtering (all content in index)
- Against HTMX philosophy
Option B: Server-Side Search with JSON Response
Server searches, returns JSON, client renders.
<input hx-get="/search" hx-trigger="input changed delay:300ms">
Server returns:
{"results": [{"title": "Chapter 1", "url": "/docs/ch1"}]}
Pros:
- Index stays on server
- Can filter by auth
Cons:
- Requires client-side rendering
- Not hypermedia (JSON, not HTML)
- More JavaScript
Option C: Server-Side Search with HTML Response (Recommended)
Server searches, returns HTML fragment.
<input
type="search"
name="q"
hx-get="/docs/search"
hx-trigger="input changed delay:300ms"
hx-target="#search-results"
hx-swap="innerHTML">
<div id="search-results"></div>
Server returns:
<ul class="search-results">
<li><a href="/docs/ch1">Chapter 1</a><p>...matching text...</p></li>
<li><a href="/docs/ch2">Chapter 2</a><p>...matching text...</p></li>
</ul>
Pros:
- True hypermedia (HTML response)
- No client-side rendering logic
- Authorization-aware (filter results)
- Index never sent to client
- Works with minimal JS (just HTMX)
Cons:
- Requires server
- Network latency per search
Decision
Generate server-side search index; output HTML search results template.
Index Format
Default: search-index.json
{
"config": {
"heading_split_level": 3
},
"documents": [
{
"path": "/docs/chapter-1",
"title": "Getting Started",
"body": "Full text content...",
"headings": [
{"level": 2, "text": "Installation", "anchor": "#installation"},
{"level": 2, "text": "Configuration", "anchor": "#configuration"}
],
"auth": {
"access": "public"
}
}
]
}
Optional: search-index.sqlite for large books (using FTS5).
Search Partial Template
<!-- templates/partials/search-results.html -->
{% if results %}
<ul class="search-results">
{% for result in results %}
<li>
<a href="{{ result.path }}" hx-boost="true">
<strong>{{ result.title }}</strong>
</a>
<p class="excerpt">{{ result.excerpt | safe }}</p>
</li>
{% endfor %}
</ul>
{% else %}
<p class="no-results">No results found for "{{ query }}"</p>
{% endif %}
Server Implementation
// Cloudflare Worker example
import Fuse from 'fuse.js';
import searchIndex from './search-index.json';
const fuse = new Fuse(searchIndex.documents, {
keys: ['title', 'body', 'headings.text'],
includeMatches: true,
threshold: 0.3,
});
app.get('/docs/search', async (c) => {
const query = c.req.query('q');
const user = await getUser(c.req);
let results = fuse.search(query);
// Filter by auth
results = results.filter(r =>
r.item.auth.access === 'public' ||
user?.roles.includes(r.item.auth.roles)
);
return c.html(await render('partials/search-results.html', {
query,
results: results.slice(0, 10)
}));
});
Consequences
Positive
- True hypermedia pattern
- Authorization-aware search
- No large index download
- Minimal client JavaScript
- Consistent with HTMX philosophy
Negative
- Requires server for search
- Network latency per query
- Server must implement search logic
Mitigation
- Debounce search input (300ms)
- Loading indicator during search
- Provide reference implementations (Workers, Deno, Node)
- Optional SQLite FTS5 for large books
Configuration
[output.htmx.search]
enabled = true
index-format = "json" # "json" | "sqlite"
heading-split-level = 3 # Index headings up to H3
include-body = true # Include full text
max-excerpt-length = 200 # Snippet length
Related ADRs
- ADR-0009: Audience Scopes - Generates scope-filtered search indexes (e.g.,
search-index.developers.json) - ADR-0021: Search Index Format Evolution - Versioning and migration strategy for the search index format
References
- 主要语言
- Rust
- 星标
- 0
- 派生
- 1
- PR 合并指标
- 30 天内没有已合并 PR
环境准备
- 没有 Dockerfile 或 Docker Compose 文件
- 没有 Pull Request 模板
- 阅读贡献指南
从这里开始
- 先读完整个 Issue,再读项目的贡献指南。
- 在 Issue 下留言说明你要接手 —— 这能避免两个人做同样的事。
- Fork 仓库,在一个分支上完成修改。
- 提交 Pull Request,并在描述里引用这个 Issue 编号。
aRustyDev/mdbook-htmx 的其他 Issue
-
feat(ci): Implement cross-repo-validate compliance for docs sync可能重新可做 @aRustyDev 于 293 天前认领,目前没有进行中的 PR。 未关闭ci enhancement
aRustyDev/mdbook-htmx#58 · 1 条评论 · 已指派 1 人 ·
-
docs(htmx): Explain how HTMX works可能重新可做 @aRustyDev 于 295 天前认领,目前没有进行中的 PR。 未关闭
aRustyDev/mdbook-htmx#48 · 3 条评论 · 已指派 1 人 ·
-
docs(mdbook-htmx): Parent Issue for documentation可能重新可做 @aRustyDev 于 295 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#47 · 已指派 1 人 ·
-
docs(examples): Secure Self-Hosted Meilisearch via Cloudflare可能重新可做 @aRustyDev 于 295 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#46 · 已指派 1 人 ·
-
docs(examples): Kubernetes with Meilisearch可能重新可做 @aRustyDev 于 295 天前认领,目前没有进行中的 PR。 未关闭documentation
aRustyDev/mdbook-htmx#45 · 已指派 1 人 ·
查看 aRustyDev/mdbook-htmx 的全部 Issue
相似的 Issue
-
desktop documentation
难度 1/5 1 小时以内 新手友好度 90/100
vfarcic/dot-agent-deck#2008 ·
维护者通常 1 天内回复
-
needs triage
难度 1/5 1 小时以内 新手友好度 85/100
pbakaus/impeccable#1210 ·
维护者通常 1 天内回复
-
area: ci priority: P4 ready-for-agent
难度 2/5 1-3 小时 新手友好度 74/100
维护者通常 1 天内回复
-
L: javascript
难度 2/5 1-3 小时 新手友好度 72/100
dependabot/dependabot-core#16744 ·
维护者通常 1 天内回复
-
bug
难度 2/5 1-3 小时 新手友好度 68/100
nautechsystems/nautilus_trader#5486 · 1 条评论 ·
维护者通常 1 天内回复