Skip to content

Replace Algolia docs search with in-memory BM25 - #1188

Open
kamath wants to merge 3 commits into
mainfrom
cursor/bm25-search-5ca6
Open

kamath wants to merge 3 commits into
mainfrom
cursor/bm25-search-5ca6

Conversation

@kamath

@kamath kamath commented Sep 11, 2026 •

Copy link
Copy Markdown
Contributor

Preview deployments can search the current branch. Docs search no longer depends on the production Algolia crawler or NEXT_PUBLIC_ALGOLIA_* keys.

What changed

  • Build a search corpus from authored MDX and generated toolkit JSON (app/_lib/search/build-index.ts)
  • Serve it from /api/search-index (statically generated at build time)
  • Rank hits in the browser with multi-field BM25 (title > heading > content)
  • Keep the existing ⌘K modal, including heading/tool deep links and one result per page
  • Remove the Algolia widget, crawler config, reindex workflows, and algoliasearch / react-instantsearch dependencies

Toolkit tool records use the same anchors as the toolkit pages (githubcreateissue), so a hit for Github.CreateIssue lands on the tool section.

How to verify

  1. Open the Vercel preview for this branch
  2. Press ⌘K and search api key, github create issue, and a heading from a page you changed on the branch
  3. Confirm results deep-link to the right page or tool, and that search works without Algolia env vars

Follow-up

The first preview build failed because Next.js requires export const dynamic to be a string literal. That export is now force-static.

Open in Web Open in Cursor 

Index authored MDX and generated toolkit docs at build time and rank
hits in the browser, so preview deployments search the current branch
without crawler credentials.
@vercel

vercel Bot commented Sep 11, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
docs Ready Ready Preview Sep 25, 2026 2:08pm UTC

Request Review

Next.js rejects ConditionalExpression on route `dynamic` exports, which
broke the Vercel preview build. force-static is a string literal and
still rebuilds the index on each request in next dev.
@kamath
kamath marked this pull request as ready for review September 14, 2026 16:41
@kamath
kamath enabled auto-merge (squash) September 14, 2026 16:42

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale Bugbot comment from a previous run.

Comment thread app/_lib/search/mdx-documents.ts
Comment thread app/_lib/search/mdx-documents.ts

@teallarson teallarson left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good overall — I'd merge once heading links are fixed.

Blocker: section deep links. Nextra uses github-slugger for heading IDs; our custom slug doesn't always match (~64 in app/en — duplicate Example → example-1, inline-code headings, etc.).

Side note: first ⌘K pulls ~1 MB gzip. Fine for now, but kinda big.

— Strider 🐦‍⬛, Teal's agent · approved by Teal

Comment thread app/_lib/search/mdx-documents.ts Outdated
@cursor

cursor Bot commented Sep 16, 2026

Copy link
Copy Markdown

Thanks Teal — the heading-slug blocker checks out. Nextra uses github-slugger with per-page dedupe; our custom slug drifts on 64 headings. We’ll switch to a per-page Slugger and cover duplicates / __init__ / --flag in tests.

The ~1 MB gzip note is also accurate. Fine for this preview; we can slim the corpus later if it becomes a problem.

@kamath
kamath requested a review from teallarson September 17, 2026 00:59
@teallarson

Copy link
Copy Markdown
Contributor

@kamath your agent tagged me for another review on this, but there haven't been any commits since my previous review, and there are several unresolved bugbot findings as well. I think it missed a step!

… ESM stripping

- Slug headings with a per-page github-slugger, fed the same flattened text
  Nextra uses, and count Tabs.Tab labels and <summary> text toward dedupe.
- Ignore import/export lines inside code fences and end ESM statements when
  their brackets balance, so multi-line imports without semicolons work.
- Index headings indented inside JSX components.
@cursor

cursor Bot commented Sep 25, 2026

Copy link
Copy Markdown

@teallarson sorry about the earlier ping; the fix hadn't been pushed yet. It's in be10329 now. Both Bugbot findings and your heading-anchor blocker are fixed, and every section URL in the index matches the ID Nextra renders on all app/en pages. The ~1 MB first-load corpus is unchanged and can be slimmed in a follow-up.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes using high effort and found 2 potential issues.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, have a team admin enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit be10329. Configure here.

Comment thread app/_lib/search/mdx-documents.ts
Comment thread app/_lib/search/mdx-documents.ts

Copy link
Copy Markdown
Contributor

Thanks for taking this on. Getting off Algolia is a good goal, but I'd like to check the approach first. Indexing the MDX source means we maintain our own search engine and MDX parser. It also drops text rendered by components, doesn't handle plurals, and downloads a multi-MB index on every page load.

I'm curious whether you looked at Nextra's built-in Pagefind search. It indexes the built HTML, which seems like it could mean less code and better results. I haven't used it myself, so I don't know its drawbacks. The ones I'd guess at are no search in pnpm dev, and deep links to individual tools needing extra markup. Is there a reason it wouldn't work here?

If we keep this approach, these need fixing first:

  • lowercase queries like "github" and "oauth" don't match those pages;
  • the search slot renders twice, which builds two indexes and makes Cmd+K open two dialogs;
  • the index should load when search is opened, not on every page.

— Strider 🐦‍⬛, Teal's agent · approved by Teal

Copy link
Copy Markdown
Contributor

@kamath do we still want to see this PR through, or should we close it?

kamath commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

i'd kick to @laughnan to see if he's interested before closing; i don't have capacity to take this over the line rn

laughnan commented Oct 9, 2026 •

Copy link
Copy Markdown
Contributor

created GRO-522 (linked to this diff) and put into the docs consolidation project so i don't forget

This branch was successfully deployed

1 active deployment
Preview — be10329f Deployed Sep 25, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants