code wiki / (root) / nx_lib_search.nx

nx_lib_search.nx source

↩ module page · 114 lines · 4170 B

1// nx_lib_search.nx -- sovereign SEARCH over the nishi-library catalog store. 2// Case-insensitive, multi-term AND over work titles, straight from nx_lib_store 3// -- retires SQLite FTS5's function. Returns a JSON array of matching work_hks. 4// No python, no sqlite, no 3rd-party. (Perf-exceed = persist an inverted index 5// via nx_search_inverted_persist; this rung is functional parity, gated.) 6// license_tier: ORIGINAL | genealogy_id: nishi_library_sovereign_search_2026_06_30 7import "nx_lib_store.nx" 8 9func lsr_lc(c: i64) -> i64 { 10 if c >= 65 { if c <= 90 { return c + 32 } } 11 return c 12} 13 14// case-insensitive: does hay[0..hayn) contain needle[0..nn)? (needle lowercased) 15func lsr_ci_has(hay: *u8, hayn: i64, needle: *u8, nn: i64) -> i64 { 16 if nn == 0 { return 1 } 17 if hayn < nn { return 0 } 18 let last: i64 = hayn - nn 19 var i: i64 = 0 20 while i <= last { 21 var j: i64 = 0 22 var st: i64 = 0 23 while st == 0 { 24 if j >= nn { st = 2 } 25 if st == 0 { 26 if lsr_lc(hay[i + j] as i64) != (needle[j] as i64) { st = 1 } 27 if st == 0 { j = j + 1 } 28 } 29 } 30 if st == 2 { return 1 } 31 i = i + 1 32 } 33 return 0 34} 35 36// title = field 0 of the record (up to the first TAB). 37func lsr_title_len(rec: *u8, reclen: i64) -> i64 { 38 var t: i64 = 0 39 var tabpos: i64 = 0 - 1 40 while t < reclen { if tabpos < 0 { if rec[t] == 9 as u8 { tabpos = t } } t = t + 1 } 41 if tabpos >= 0 { return tabpos } 42 return reclen 43} 44 45// does the WHOLE record (title + doi + abstract + lineage + authors) contain ALL 46// space-separated terms of qlow (lowercased)? Full-text so /?q= matches the same 47// works as /api/search (whose inverted index is full-text) -- e.g. "Archaea" lives 48// in the lineage/abstract field, not the title. lsr_title_len kept for title display. 49func lsr_match(rec: *u8, reclen: i64, qlow: *u8, qn: i64) -> i64 { 50 let tlen: i64 = reclen 51 var ts: i64 = 0 52 var allmatch: i64 = 1 53 var anyterm: i64 = 0 54 while ts < qn { 55 if qlow[ts] == 32 as u8 { ts = ts + 1 } 56 else { 57 var termend: i64 = qn 58 var te: i64 = ts 59 while te < qn { if termend == qn { if qlow[te] == 32 as u8 { termend = te } } te = te + 1 } 60 let termlen: i64 = termend - ts 61 let termptr: *u8 = (qlow as i64 + ts) as *u8 62 anyterm = 1 63 if lsr_ci_has(rec, tlen, termptr, termlen) == 0 { allmatch = 0 } 64 ts = termend 65 } 66 } 67 if anyterm == 0 { return 0 } 68 return allmatch 69} 70 71// SEARCH: query -> JSON array of matching work_hks. out = JSON; returns length. 72func nx_lib_search_query(query: *u8, qn: i64, out: *u8) -> i64 { 73 let qlow: *u8 = sys_mmap(2048) 74 var qi: i64 = 0 75 while qi < qn { qlow[qi] = lsr_lc(query[qi] as i64) as u8; qi = qi + 1 } 76 77 let idxbuf: *u8 = sys_mmap(1048576) 78 let inn: i64 = nx_lib_store_index(idxbuf) 79 let po: *i64 = sys_mmap(16) as *i64 80 let lo: *i64 = sys_mmap(16) as *i64 81 let hkbuf: *u8 = sys_mmap(1024) 82 83 var o: i64 = 0 84 out[o] = 91 as u8; o = o + 1 // '[' 85 var first: i64 = 1 86 var ls: i64 = 0 87 var i: i64 = 0 88 while i <= inn { 89 var eol: i64 = 0 90 if i == inn { eol = 1 } else { if idxbuf[i] == 10 as u8 { eol = 1 } } 91 if eol == 1 { 92 if i > ls { 93 var k: i64 = 0 94 var j: i64 = ls 95 while j < i { hkbuf[k] = idxbuf[j]; k = k + 1; j = j + 1 } 96 hkbuf[k] = 0 as u8 97 if nx_lib_store_get_work(hkbuf, po, lo) >= 0 { 98 if lsr_match(po[0] as *u8, lo[0], qlow, qn) == 1 { 99 if first == 0 { out[o] = 44 as u8; o = o + 1 } 100 first = 0 101 out[o] = 34 as u8; o = o + 1 102 var m: i64 = 0 103 while m < k { out[o] = hkbuf[m]; o = o + 1; m = m + 1 } 104 out[o] = 34 as u8; o = o + 1 105 } 106 } 107 } 108 ls = i + 1 109 } 110 i = i + 1 111 } 112 out[o] = 93 as u8; o = o + 1 // ']' 113 return o 114}