Skip to content

fix(search-index): index every page in full as heading-aligned chunks - #419

Closed
ysyneu wants to merge 1 commit into
feat/ai-srefrom
fix/meili-heading-chunks
Closed

ysyneu wants to merge 1 commit into
feat/ai-srefrom
fix/meili-heading-chunks

Conversation

@ysyneu

@ysyneu ysyneu commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Problem

scripts/upload.sh built one Meilisearch document per page and cut the cleaned text with cut -c1-6000. GNU cut -c counts bytes, so Chinese pages were cut at about 2000 characters, often inside a UTF-8 sequence (the stored text then ends in U+FFFD). Re-running the old cleaning over the current tree:

Locale Pages Pages truncated Text never indexed
zh 279 129 44.3%
en 281 144 47.2%

The longest pages (changelog, CLI reference, OpenAPI catalog) kept 8–12% of their text. The embedder also sees at most 4000 bytes of each document (documentTemplateMaxBytes), so a single embedding per page covers only its opening.

Change

  • scripts/build_index_docs.py (new) builds the index documents. Each page (frontmatter description plus body) is packed into chunks of whole headings and paragraphs of up to 3000 UTF-8 bytes. A chunk plus its title fits the 4000-byte embedder template.
    • Each chunk starts with a Page > Section > Subsection breadcrumb, so it is understandable on its own.
    • A paragraph longer than a chunk is split at line boundaries, never inside a character.
    • MDX/HTML tags are stripped only outside code fences. title="…" attribute text, ALL_CAPS placeholders such as <YOUR_APP_KEY>, and code samples are kept. import/export lines are dropped.
    • Document id = <md5 of page path>-<chunk index>; fields are unchanged (id/title/content/locale/url).
  • scripts/upload.sh uploads the builder's output in batches of 20. The reconcile step still deletes ids the build no longer produces. On the first run that removes the old one-per-page ids, after the new chunks are uploaded.

Full tree: 560 pages → about 2160 chunks, median 2.7 KB, max 3000 bytes, no U+FFFD.

Test

  • python3 -m unittest tests.test_build_index_docs runs 6 tests: long Chinese page fully indexed within the byte limit, oversized line split on character boundaries, tag stripping keeps titles/placeholders/code, breadcrumb, ids/locale/urls, link-only page indexed by its description.
  • Ran upload.sh against a scratch index: every chunk was embedded, with 0 failed tasks.

Search callers that dedupe hits by url should now merge same-page chunks instead of dropping them.

upload.sh built one Meilisearch document per page and cut its cleaned text
at 6000 characters with GNU cut -c, which counts bytes. Chinese pages were
cut at about 2000 characters, often inside a UTF-8 sequence, and the long
tail of most long pages never reached the index.

scripts/build_index_docs.py now builds the documents: each page (frontmatter
description + body) is packed into chunks of whole headings and paragraphs
up to 3000 UTF-8 bytes, so a chunk plus its title fits the embedder's
4000-byte document template. A chunk starts with a 'Page > Section >
Subsection' breadcrumb. Tags are stripped outside code fences only, keeping
title attributes, ALL_CAPS placeholders and code samples. Document ids are
'<md5 of page path>-<chunk index>'; the existing reconcile step deletes ids a
build no longer produces, including the old one-per-page ids.
@ysyneu

ysyneu commented Sep 21, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by #420, which indexes each page in full as one document instead of splitting it into heading-aligned chunks.

@ysyneu ysyneu closed this Sep 21, 2026
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.

1 participant