docs(adr): ADR-0005: Server-Side Search Index
@aRustyDev ci sta già lavorando.
Dal 5/1/2026.
Valutazione
Questa issue non è ancora stata valutata.
Descrizione
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
- Lingua principale
- Rust
- Stelle
- 0
- Fork
- 1
- Metriche di merge delle PR
- Nessuna PR unita negli ultimi 30g
Preparare l'ambiente
- Nessun Dockerfile né file Docker Compose
- Nessun modello di pull request
- Leggi la guida per i contributori
Come iniziare
- Leggi tutta la issue e poi la guida ai contributi del progetto.
- Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
- Fai un fork del repository e lavora su un branch.
- Apri una pull request che faccia riferimento al numero della issue.
Altre issue di aRustyDev/mdbook-htmx
-
feat(ci): Implement cross-repo-validate compliance for docs syncForse di nuovo libera @aRustyDev l’ha presa 283 giorni fa e non c’è nessuna pull request aperta. Apertaci enhancement
aRustyDev/mdbook-htmx#58 · 1 commento · 1 assegnatario ·
-
docs(htmx): Explain how HTMX worksForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Aperta
aRustyDev/mdbook-htmx#48 · 3 commenti · 1 assegnatario ·
-
docs(mdbook-htmx): Parent Issue for documentationForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#47 · 1 assegnatario ·
-
docs(examples): Secure Self-Hosted Meilisearch via CloudflareForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#46 · 1 assegnatario ·
-
docs(examples): Kubernetes with MeilisearchForse di nuovo libera @aRustyDev l’ha presa 285 giorni fa e non c’è nessuna pull request aperta. Apertadocumentation
aRustyDev/mdbook-htmx#45 · 1 assegnatario ·
Tutte le issue di aRustyDev/mdbook-htmx
Issue simili
-
documentation
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
IntersectMBO/developer-experience#347 ·
I maintainer di solito rispondono entro 2 giorni
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
registrystack/registry-stack#2168 ·
I maintainer di solito rispondono entro 1 giorno
-
bug crate: 3d help wanted triage
Difficoltà 2/5 1-3 ore Idoneità per principianti 78/100
kornia/kornia-rs#1203 · 1 commento ·
I maintainer di solito rispondono entro 1 giorno
-
Difficoltà 2/5 1-3 ore Idoneità per principianti 76/100
I maintainer di solito rispondono entro 1 giorno
-
[Bug] ComponentType classifies 0x8A8A-0xEAEA as GREASE; mls-extensions §7.5 makes them private useAperta
Difficoltà 2/5 1-3 ore Idoneità per principianti 85/100
I maintainer di solito rispondono entro 1 giorno