Hacktoberfest 2026: le issue che i maintainer hanno segnato per ottobre, aperte e adatte ai principianti. Sfoglia le issue Hacktoberfest

docs(adr): ADR-0005: Server-Side Search Index

Aperta
#13 0 commenti 0 reazioni 1 assegnatario Vedi su GitHub

@aRustyDev ci sta già lavorando.

Dal 5/1/2026.

Valutazione

Questa issue non è ancora stata valutata.

Descrizione

documentation

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

  1. HTMX Philosophy - Server returns HTML, not JSON
  2. Performance - Large books have large indexes
  3. Authorization - Search results should respect access control
  4. 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

References

Lingua principale
Rust
Stelle
0
Fork
1
Metriche di merge delle PR
Nessuna PR unita negli ultimi 30g

Preparare l'ambiente

Come iniziare

  1. Leggi tutta la issue e poi la guida ai contributi del progetto.
  2. Commenta sulla issue per dire che te ne occupi tu — evita che due persone facciano lo stesso lavoro.
  3. Fai un fork del repository e lavora su un branch.
  4. Apri una pull request che faccia riferimento al numero della issue.

Altre issue di aRustyDev/mdbook-htmx

Tutte le issue di aRustyDev/mdbook-htmx

Issue simili

Altre issue su Rust

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.