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}