Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
ALGOLIA_API_KEY=algolia_api_key
ALGOLIA_APPLICATION_ID=algolia_application_id
TYPESENSE_URL=https://your-cluster.a1.typesense.net
TYPESENSE_PUBLIC_API_KEY=your_search_only_api_key
TYPESENSE_PRIVATE_API_KEY=typesense_private_api_key_required_for_indexing_only
# Optional. Overrides the branch-derived read alias.
# TYPESENSE_COLLECTION=directus-docs
DIRECTUS_URL=https://marketing-directus-url.com
GOOGLE_TAG_MANAGER_ID=GTM-PTLT3GH
POSTHOG_API_HOST=https://directus.io/ingest
Expand Down
80 changes: 80 additions & 0 deletions .github/workflows/search-index.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: Search Index

on:
push:
branches:
- main
paths: &index-paths
- 'content/**'
- 'scripts/index-docs.ts'
- 'scripts/index-docs-chunker.ts'
- 'shared/utils/parseTypesenseUrl.ts'
- 'shared/utils/docsSections.ts'
- 'app/utils/slugify.ts'
- 'server/data/synonyms.ts'
- 'lib/typesenseAlias.ts'
- 'pnpm-lock.yaml'
- '.github/workflows/search-index.yml'
pull_request:
branches:
- main
paths: *index-paths
workflow_dispatch:

concurrency:
group: search-index-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: false

permissions:
contents: read

jobs:
preview-index:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
env:
TYPESENSE_URL: ${{ secrets.TYPESENSE_URL }}
TYPESENSE_PUBLIC_API_KEY: ${{ secrets.TYPESENSE_PUBLIC_API_KEY }}
TYPESENSE_PRIVATE_API_KEY: ${{ secrets.TYPESENSE_PRIVATE_API_KEY }}
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10.29.2

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Index preview collection
run: pnpm index:docs

prod-index:
if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
runs-on: ubuntu-latest
env:
TYPESENSE_URL: ${{ secrets.TYPESENSE_URL }}
TYPESENSE_PUBLIC_API_KEY: ${{ secrets.TYPESENSE_PUBLIC_API_KEY }}
TYPESENSE_PRIVATE_API_KEY: ${{ secrets.TYPESENSE_PRIVATE_API_KEY }}
steps:
- uses: actions/checkout@v4

- uses: pnpm/action-setup@v4
with:
version: 10.29.2

- uses: actions/setup-node@v4
with:
node-version: 22
cache: pnpm

- name: Install dependencies
run: pnpm install --frozen-lockfile

- name: Index production collection
run: pnpm index:docs
1 change: 1 addition & 0 deletions .nuxtrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
setups.@nuxt/test-utils="4.0.3"
43 changes: 40 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,14 @@ pnpm build

### Repository Tooling

The repository includes scripts that keep docs routes stable when files move.
The repository includes scripts that keep docs routes stable when files move and that index the docs into Typesense.

```bash
pnpm stable-ids:ensure # Add missing stableId frontmatter
pnpm stable-ids:check # Validate stableId frontmatter
pnpm redirects:sync # Update redirects.json for moved pages
pnpm redirects:check # Check redirect coverage without writing files
pnpm index:docs # Build the search index in Typesense
pnpm typecheck:scripts # Type check repository scripts
```

Expand Down Expand Up @@ -109,9 +110,45 @@ The documentation automatically deploys to Vercel when changes are merged into t
- [GitHub Issues](https://github.com/directus/docs/issues) (Report Bugs)
- [Roadmap](https://roadmap.directus.io) (Roadmap & Feature Requests)

## Making changes to Algolia Search
## 🔍 Search

The docs make use of the Algolia Crawler to index the content. The crawler is found at the bottom left in the Algolia dashboard under `Data Sources > Crawler > directus`. To make changes on how the crawler works, go to the `Editor` tab and make your changes. By default the crawler runs once a day but you can also manually run it. In order to tweak the ranking of search results, go to the `Search > Configure > Index > Configuration > Ranking and Sorting` tab.
Search is powered by [Typesense](https://typesense.org). The browser palette (`UCommandPalette`-based) lives at `app/components/DocsSearchPalette.vue` and queries Typesense directly via `app/services/typesenseService.ts`. The official `typesense` npm client is used by the indexer only.

### Indexing

The indexer at `scripts/index-docs.ts` walks `/content`, chunks each Markdown page, attaches synonyms, and pushes everything to Typesense. OpenAPI indexing is deferred to a later branch. Run it locally with:

```bash
pnpm index:docs
```

CI runs the same command on every push to `main` (production index) and on every PR commit (per-branch preview index). See `.github/workflows/search-index.yml`.

### Collection naming

Indexes use a blue/green slot pattern with a stable alias:

- `main` -> alias `directus-docs`, slots `directus-docs-a` / `directus-docs-b`
- Branch `bry/foo` -> alias `directus-docs-preview-bry-foo`, slots `...-a` / `...-b`
- Local branch runs use the same branch-derived alias as CI

Each indexer run writes to whichever slot the alias is not currently pointing at, swaps the alias, then deletes the previous slot.

For one-off writes, override the index target with `TYPESENSE_INDEX_TARGET=...`.

The browser reads from `TYPESENSE_COLLECTION` when set. Otherwise it derives the same branch alias as the indexer. The app reads the alias, never the `-a` / `-b` slot name.

### Ranking

Section boosts and personalization live in `buildPersonalizedSortBy` in `app/composables/useDocsSearch.ts`. The same `sectionPriority` array drives both the Typesense `_eval` boost order and the chip-bar render order in the palette.

### Synonyms

Search synonyms live in `server/data/synonyms.ts` and are pushed to Typesense on every indexer run. Two formats: `multiway` (equivalent terms) and `oneway` (directional shorthand -> canonical, e.g. `db -> database`). Header comment in the file explains both.

### Search-friendly content

Write H2s and first paragraphs so they work as standalone search results.

<br />

Expand Down
2 changes: 1 addition & 1 deletion app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default defineAppConfig({
search: {
backend: 'algolia',
backend: 'typesense',
},

ui: {
Expand Down
30 changes: 0 additions & 30 deletions app/assets/css/algolia.css

This file was deleted.

Loading