Repository navigation
search_graph: non-ASCII queries return noise, and docstrings are never indexed #1401
Description
Summary
search_graph(query=...) returns unranked noise for any query written in a non-Latin script, and no amount of documentation helps, because docstrings are not part of the full-text index either. On a codebase documented in Russian (or Greek, or Japanese) the BM25 mode is effectively unusable.
Two independent causes, both small:
1. The query tokenizer drops every non-ASCII byte. bm25_build_match() (src/mcp/mcp.c) accepts only a-z, A-Z, 0-9 and _ as token bytes; everything else is a separator. A Cyrillic query is therefore tokenized to nothing, reaches FTS5 as an empty MATCH, and the caller gets whatever the fallback path returns.
2. Docstrings are not indexed. nodes_fts is declared as fts5(name, qualified_name, label, file_path, ...) in src/store/store.c. Identifiers in real projects are overwhelmingly English even when the team writes prose in another language, so the only text that could match a native-language query is exactly the text that never enters the index.
Reproduction
Index any project that has a non-English docstring, e.g.
def _validate_args(name: str, arguments: dict) -> str | None:
"""Провалидировать аргументы по схеме инструмента."""codebase-memory-mcp cli search_graph --project <p> --query 'провалидировать аргументы схеме'
Expected: the documented function.
Actual (v0.9.0): unrelated nodes — in my case .env.example, a Coordinates class and a README section; the target function is nowhere in the results.
The English control query behaves as expected, which is what makes the failure easy to miss.
Suggested fix
- Treat bytes
>= 0x80as token bytes inbm25_build_match(). No UTF-8 decoding is required — the point is only to keep multi-byte sequences intact and let the FTS5unicode61tokenizer apply the real word-boundary rules, which it already does correctly on both sides. - Add a
docstringcolumn tonodes_ftsand populate it fromjson_extract(properties, '$.docstring')in both backfill paths (pipeline.c,pipeline_incremental.c). Existing databases carry the 4-column table, so the schema probe has to drop and recreate it; the table is contentless, so nothing is lost — the next backfill repopulates it fromnodes.
Result after the fix
Same query, same project:
total: 1
search_mode: bm25
...server._validate_args Function src/yandex_biz_mcp/server.py 78-96 -18.89
The English query improves as well — validate arguments schema now ranks _validate_args first, where previously it returned noise: docstrings carry intent that identifiers alone do not.
I have a patch with a regression test ready and will open a PR referencing this issue.
Environment
- v0.9.0 (release build, UI variant), macOS 15.5 arm64
- Reproduced on several projects; the smallest is an 8-tool Python MCP server, 137 nodes
Activity
Metatau commentedon Aug 1, 2026
Patch attached below rather than as a PR: I opened #1403, then made my fork private, which closed the PR and left an outdated revision in it. Please do not take the diff from #1403 — it has a defect I found afterwards (described in that PR's last comment and fixed here).
Applies cleanly to
mainat d6be58e (git apply --checkpasses). Two files of implementation, one of tests, plus the two backfill call sites.What it does
bm25_build_match()treats bytes>= 0x80as token bytes, so a non-Latin query is no longer tokenized to nothing. No UTF-8 decoding — just keeping multi-byte sequences intact for theunicode61tokenizer.nodes_ftsgains adocstringcolumn, populated fromjson_extract(properties, '$.docstring')in both backfill paths.Verified
Reproducing the upgrade sequence on real databases:
Full suite:
6824 passed, 1 failed, 4 skipped. The failure istool_detect_changes_contained_commands_clean_up_error_and_successatASSERT(merge_base_reported)—git merge-basereporting, untouched by this diff; it passes 3/3 in isolation and the same build alternated between 0 and 1 failures across full runs, so I read it as flaky.lintpassed in your CI on this code.Known limitation
The upgrade runs when the schema is created, not on read: upgrading the binary alone neither breaks nor fixes an existing database until the project is indexed again. I also tried self-healing an already-emptied index by probing the shadow table on open, but could not make it fire reliably across projects, so I left it out rather than ship behaviour I cannot explain.
No DCO sign-off on my side, so treat this as material to use as you see fit rather than a contribution ready to merge.
Patch (git format-patch)
DeusData commentedon Aug 3, 2026
Thank you for separating the non-ASCII query behavior from the missing-docstring search surface and for providing a patch sketch. Current storage uses the Unicode-aware FTS tokenizer, while docstrings are consumed by the semantic pipeline rather than exposed through the same BM25 document path; the observed result therefore spans two concrete indexing/query contracts. I have routed this as a high-priority
0.9.1-rccorrectness bug. Any implementation should arrive as a focused, signed PR with independent tests for both contracts so authorship and review remain clean.DeusData commentedon Sep 26, 2026
Thank you, @Metatau, for splitting this into two contracts. That split was exactly right, because half of it has landed since!
Docstrings are now in the full-text index:
nodes_ftsgained abodycolumn that carries each node's docstring (#518/#519). The query side is still open.bm25_build_match()still keeps only ASCII identifier bytes, so on current main a Cyrillic query falls back to an unranked listing instead of a BM25 search, while the English control ranks correctly.Your fix is the right shape for what remains: treat bytes ≥0x80 as token bytes and let
unicode61handle word boundaries. Since you've already written it, would you like to open it as a small signed PR covering only the tokenizer, with a non-ASCII query test? If you'd rather not, just say so and we'll carry it with credit to you.Metatau commentedon Sep 26, 2026
DeusData commentedon Oct 25, 2026
Thank you for explicitly offering the tokenizer work for maintainer follow-through and identifying mixed-script and CJK cases. No signed PR is needed from you; the remaining tokenizer work is on us. Sorry for the delay; this is waiting on maintainer follow-up, and I’ll follow up soon.