Search
Full-text search architecture using MiniSearch
Search
Full-text search over site content using MiniSearch. The search index is generated at build time from MDX content files.
Architecture
flowchart TB
subgraph Build Time
MDX["MDX files<br/>(products, guides, notes, highlights, s)"] --> Script["generate-search-index.mjs"]
Script --> SearchJSON["search-index.json<br/>(included in dist/)"]
Script --> FunctionsJSON["netlify/functions/search-data.json"]
end
subgraph Production
Browser -->|"GET /api/search?q=..."| NetlifyFn["Netlify Function<br/>(search.ts)"]
NetlifyFn --> FunctionsJSON
end
subgraph Development
Browser2["Browser"] -->|"fetch /search-index.json"| ClientSearch["Client-side MiniSearch"]
ClientSearch --> SearchJSON
end
Production Mode
In production, search requests go to the Netlify Function search.ts which loads the pre-built search index and runs MiniSearch server-side. Results include highlighted titles and excerpts.
Development Mode
In development, the search runs entirely client-side. The search component fetches /search-index.json and creates a MiniSearch instance in the browser. This avoids needing to run Netlify Functions locally for search.
Search Index Generation
Script: scripts/generate-search-index.mjs
Run manually:
node scripts/generate-search-index.mjs
This is also invoked during the build via pnpm build:search-index.
Content Sources
| Content Type | Directory | URL Prefix |
|---|---|---|
| Products | src/mdx/products/ | /products |
| Guides | src/mdx/guides/ | /guides |
| Highlights | src/mdx/highlights/ | /notes |
| Notes | src/mdx/notes/ | /notes |
| Standalone | src/mdx/s/ | /s |
Document Fields
Each document in the index contains:
| Field | Source | Indexed | Stored |
|---|---|---|---|
id | {contentType}/{fileSlug} | No | No |
slug | {urlPrefix}/{routeSlug}/ | No | Yes |
title | Frontmatter title | Yes (boost: 3) | Yes |
description | Frontmatter description | Yes (boost: 2) | Yes |
tags | Frontmatter tags (space-joined) | Yes (boost: 1.5) | Yes |
excerpt | First 500 chars of plain text | Yes | Yes |
createdAt | Frontmatter createdAt | No | Yes |
EN Article Handling
EN articles use .en.mdx file extension. The index generator:
- Uses
fileSlug(with.en) for the documentidto ensure uniqueness (e.g.,guides/col007-power-vol3.en) - Uses
routeSlug(without.en) for the URL slug (e.g.,/guides/col007-power-vol3/)
This ensures JA and EN articles for the same content have unique IDs but share the same route slug.
Exclusions
- Files starting with
_(fragments) are skipped - Files with
avoidListing: truein frontmatter are skipped
Search Options
MiniSearch is configured with:
- Prefix search: enabled (
prefix: true) - Fuzzy matching: 0.2 edit distance (
fuzzy: 0.2) - Field boosting: title (3x) > description (2x) > tags (1.5x) > excerpt (1x)
Keyword Logging
In production, the Netlify Function logs search keywords to Netlify Blobs (keyword-logs store) for analytics. Each entry includes:
keyword: the search querytimestamp: ISO 8601 timestampresultCount: number of results returned
Search UI
Pages
- JA:
/search/(src/astro/pages/search.astro) - EN:
/en/search/(src/astro/pages/en/search.astro)
Components
components/search/search-page-content.tsx— main search page with input and results (React island)components/search/search-result-item.tsx— individual result display
Output Files
| File | Purpose | Git tracked |
|---|---|---|
public/search-index.json | Client-side dev fallback | No (gitignored) |
netlify/functions/search-data.json | Netlify Function bundle input | No (gitignored) |
Both are regenerated at build time and should not be committed.