nx_search_onsite_engine.nx
buildroot/runtime/nx_search_onsite_engine.nx
about
nx_search_onsite_engine.nx -- V1 onsite search engine
(composes NxSearchQuery + NxInvIndex -> NxSearchResults).
COMPOSES (per NISHI_SMALL_SHARP_COMPOSABLE_STANDARD §4.1 M7):
hub/nx_search_query_parser (NxSearchQuery struct + accessors)
nx_search_inverted (nx_inv_query_term + NxInvIndex)
nx_hygiene_prims (caller-allocated buffer pattern)
COMPOSED BY:
wiki/nx_wiki_search_handler (next commit; closes /wiki/search 501)
(future) sprinkler-config-site search handler
any site using the inverted-index layer
Status: V1. 2026-05-27.
WINNER-TIER: BASELINE-C provisional
INCUMBENTS: Lucene IndexSearcher (Elasticsearch core),
Tantivy Searcher, Bleve IndexAlias, Whoosh
Searcher, MeiliSearch query executor
NUMBERS: V1 implements AND-only filter + count-of-matched-
terms scoring; paired latency bench vs Lucene
IndexSearcher pending real workload
GAP: Lucene ships BM25 (default since 6.0), boolean
queries with NOT/SHOULD, phrase queries, span
queries, field-boosted multi-match -- V1 ships
JUST implicit-AND + position-stable ranking
PLAN: M-next: V2 BM25F via nx_search_bm25f using
nx_search_inverted_persist's positional postings
(charter §5.2)
EXEMPTION REASON: n/a; provisional pending measurement
V1 SCOPE per NISHI_SEARCH_CHARTER.md §5.1:
- Implicit AND: doc must contain ALL query terms (sealed
no-half-state behavior; V2 adds explicit boolean)
- Score = count of distinct query terms matched per doc
(per V1 charter §3.5; V2 BM25F + LTR)
- Top-N sort descending (max_results from NxSearchQuery)
- Sealed NxSearchResults emit; caller owns rowids+scores arrays
- elapsed_us QoS observable for monitoring pillar
dependencies 3 imports · 4 importers
imports: nx_syscalls.nxnx_search_inverted.nxnx_search_query_parser.nx
imported by: nx_search_handler_flow.nxnx_search_render_html.nxnx_wiki_search_render.nxnx_wiki_search_wiring.nx
structs
| 84 | struct NxSearchResults |
| 121 | struct NxSearchOnsiteCtx |
consts
| 63 | const NX_SOE_OK: i64 = 0 |
| 64 | const NX_SOE_BAD_INPUT: i64 = 1600 |
| 65 | const NX_SOE_INDEX_BAD: i64 = 1601 |
| 66 | const NX_SOE_QUERY_EMPTY: i64 = 1602 |
| 67 | const NX_SOE_RESULTS_CAP_TOO_SMALL: i64 = 1603 |
| 68 | const NX_SOE_COUNTER_CAP_OVERFLOW: i64 = 1604 |
| 69 | const NX_SOE_TERM_BUF_OVERFLOW: i64 = 1605 |
| 70 | const NX_SOE_LOOP_BUDGET: i64 = 1606 |
| 71 | const NX_SOE_NOT_IMPLEMENTED: i64 = 1607 |
| 74 | const NX_SOE_DEFAULT_RESULTS_CAP: i64 = 100 |
| 75 | const NX_SOE_HARD_RESULTS_CAP: i64 = 10000 |
| 76 | const NX_SOE_MAX_TERM_POSTINGS: i64 = 100000 // per-term posting list cap |
| 77 | const NX_SOE_DEFAULT_COUNTER_CAP: i64 = 4000000 // 4M docs * 1 byte = 4MB |
| 78 | const NX_SOE_LOOP_BUDGET_PER_QUERY: i64 = 10000000 // hard cap; bail on runaway |
| 79 | const NX_SOE_MAX_TERMS_USED: i64 = 255 // counter byte saturates here |
| 80 | const NX_SOE_SCRATCH_TERM_BUF: i64 = 128 // 64-char max term + len byte + room |
functions
| 96 | func nx_search_results_init(r: *NxSearchResults, called by 1: nx_search_flow_init |
| 134 | func nx_search_onsite_init(ctx: *NxSearchOnsiteCtx, |
| 161 | func nx_soe_counter_reset(ctx: *NxSearchOnsiteCtx) -> i64 called by 1: nx_search_onsite_run |
| 180 | func nx_soe_apply_term_postings_kind(ctx: *NxSearchOnsiteCtx, |
| 221 | func nx_soe_apply_term_postings(ctx: *NxSearchOnsiteCtx, |
| 267 | func nx_soe_copy_term(ctx: *NxSearchOnsiteCtx, |
| 300 | func nx_soe_collect_results(ctx: *NxSearchOnsiteCtx, |
| 338 | func nx_soe_collect_results_v2(ctx: *NxSearchOnsiteCtx, called by 1: nx_search_onsite_run |
| 381 | func nx_search_onsite_run(ctx: *NxSearchOnsiteCtx, |
| 447 | func nx_search_results_count(r: *NxSearchResults) -> i64 |
| 452 | func nx_search_results_rowid_at(r: *NxSearchResults, i: i64) -> i64 |
| 459 | func nx_search_results_score_at(r: *NxSearchResults, i: i64) -> i64 called by 1: nx_srh_write_card |
| 466 | func nx_search_results_elapsed_us(r: *NxSearchResults) -> i64 |