nx_search_snippet_extract.nx
buildroot/runtime/nx_search_snippet_extract.nx
about
nx_search_snippet_extract.nx -- V1 search snippet extractor.
Context window around query-term matches + <mark>...</mark> highlight.
COMPOSES (per NISHI_SMALL_SHARP_COMPOSABLE_STANDARD §4.1 M7):
hub/nx_search_query_parser (NxSearchQuery + iter accessors)
nx_html_escape (html_escape for safe emit)
nx_search_inverted (lowercase + token-char convention)
COMPOSED BY:
wiki/nx_wiki_search_render (next commit)
(future) any site rendering search results
Status: V1. 2026-05-27.
WINNER-TIER: BASELINE-C provisional
INCUMBENTS: Lucene's PostingsHighlighter / FastVectorHighlighter,
Tantivy's SnippetGenerator, Bleve's SearchResult.Hit
Fragments, Whoosh's whoosh.highlight, Algolia's
_snippetResult
NUMBERS: V1 ships first-match-centered window + per-term
<mark>; paired latency bench vs Lucene Postings-
Highlighter pending real workload
GAP: Lucene picks densest-cluster window (more relevant
snippet); V1 picks first-match (simpler, deterministic).
V2 ships densest-cluster scorer.
PLAN: M-next: V2 cluster-density window picker per
charter §5.2
EXEMPTION REASON: n/a; provisional pending measurement
V1 SCOPE per NISHI_SEARCH_CHARTER.md §5.1:
- Caller-allocated output buffer
- Case-insensitive term matching (ASCII lowercase fold)
- Window: NX_SSE_WIN_BEFORE bytes before first match +
NX_SSE_WIN_AFTER bytes after; clipped to doc bounds
- Each query-term occurrence inside the window wrapped in
<mark>...</mark>; rest of bytes html_escape'd
- "..." prefix/suffix when window doesn't reach doc bounds
- Empty doc + empty query both well-defined (no error)
V2 SCOPE (TODO):
dependencies 4 imports · 2 importers
imports: nx_syscalls.nxnx_search_query_parser.nxnx_search_inverted.nxnx_html_escape.nx
imported by: nx_search_render_html.nxnx_wiki_answer.nx
structs
| none |
consts
| 52 | const NX_SSE_OK: i64 = 0 |
| 53 | const NX_SSE_BAD_INPUT: i64 = 1700 |
| 54 | const NX_SSE_OUTPUT_OVERFLOW: i64 = 1701 |
| 55 | const NX_SSE_DOC_TOO_LARGE: i64 = 1702 |
| 56 | const NX_SSE_LOOP_BUDGET: i64 = 1703 |
| 57 | const NX_SSE_ESCAPE_FAIL: i64 = 1704 |
| 58 | const NX_SSE_NOT_IMPLEMENTED: i64 = 1705 |
| 61 | const NX_SSE_WIN_BEFORE: i64 = 64 |
| 62 | const NX_SSE_WIN_AFTER: i64 = 192 |
| 63 | const NX_SSE_DEFAULT_OUTPUT_CAP: i64 = 2048 |
| 64 | const NX_SSE_MAX_DOC_BYTES: i64 = 1048576 // 1 MB cap per snippet input |
| 65 | const NX_SSE_LOOP_BUDGET_CAP: i64 = 10000000 |
| 66 | const NX_SSE_ELLIPSIS: *u8 = "..." as *u8 |
| 67 | const NX_SSE_ELLIPSIS_N: i64 = 3 |
| 68 | const NX_SSE_MARK_OPEN: *u8 = "<mark>" as *u8 |
| 69 | const NX_SSE_MARK_OPEN_N: i64 = 6 |
| 70 | const NX_SSE_MARK_CLOSE: *u8 = "</mark>" as *u8 |
| 71 | const NX_SSE_MARK_CLOSE_N: i64 = 7 |
| 72 | const NX_SSE_MAX_TERMS_CONSIDER: i64 = 32 |
functions
| 76 | func nx_sse_lower(c: i64) -> i64 called by 1: nx_sse_match_at |
| 86 | func nx_sse_match_at(src: *u8, src_n: i64, pos: i64, |
| 107 | func nx_sse_find_first_match(src: *u8, src_n: i64, called by 1: nx_search_snippet_extract calls 4: nx_search_query_countsys_mmapnx_search_query_term_atnx_sse_match_at |
| 148 | func nx_sse_match_len_at(src: *u8, src_n: i64, pos: i64, called by 1: nx_search_snippet_extract calls 4: nx_search_query_countsys_mmapnx_search_query_term_atnx_sse_match_at |
| 175 | func nx_sse_put_raw(out: *u8, cap: i64, off: i64, called by 1: nx_search_snippet_extract |
| 190 | func nx_sse_put_escaped(out: *u8, cap: i64, off: i64, |
| 205 | func nx_search_snippet_extract(out: *u8, out_cap: i64, |