code wiki / _hdl_build / nx_query.nx

nx_query.nx source

↩ module page · 108 lines · 4536 B

1// nx_query.nx -- the sovereign QUERY EXECUTOR over a dataset (the "SELECT agg(col) WHERE col op val GROUP BY 2// col" engine, structured-params form -- a string/typed-syntax parser is the thin front-end follow-on). This 3// is what makes the analytics QUERYABLE instead of called function-by-function. Composes nx_dataframe 4// (filter + groupby + reductions). Integer-exact. Works on in-memory columns OR seg_store-loaded columns 5// (via nx_analyst_store). license_tier: ORIGINAL 6import "nx_syscalls.nx" 7import "_hdl_build/nx_dataframe.nx" 8 9// aggregate kinds 10const Q_COUNT: i64 = 0 11const Q_SUM: i64 = 1 12const Q_AVG: i64 = 2 13const Q_MIN: i64 = 3 14const Q_MAX: i64 = 4 15// filter ops (mirror df_filter): 0 ==, 1 <, 2 <=, 3 >, 4 >=, 5 = no filter (all rows) 16const Q_ALL: i64 = 5 17 18func q_match(v: i64, op: i64, thr: i64) -> i64 { 19 if op == Q_ALL { return 1 } 20 if op == 0 { if v == thr { return 1 } return 0 } 21 if op == 1 { if v < thr { return 1 } return 0 } 22 if op == 2 { if v <= thr { return 1 } return 0 } 23 if op == 3 { if v > thr { return 1 } return 0 } 24 if op == 4 { if v >= thr { return 1 } return 0 } 25 return 0 26} 27 28// aggregate agg_col over the rows where filter_col matches (op,thr). Returns the scalar. 29// avg returns integer floor; count ignores agg_col. min/max over an empty set return 0. 30func q_scalar(agg_col: *i64, filter_col: *i64, n: i64, filter_op: i64, filter_thr: i64, agg_kind: i64) -> i64 { 31 var cnt: i64 = 0 32 var sum: i64 = 0 33 var mn: i64 = 0 34 var mx: i64 = 0 35 var seen: i64 = 0 36 var i: i64 = 0 37 while i < n { 38 if q_match(filter_col[i], filter_op, filter_thr) == 1 { 39 let v: i64 = agg_col[i] 40 cnt = cnt + 1 41 sum = sum + v 42 if seen == 0 { mn = v; mx = v; seen = 1 } else { 43 if v < mn { mn = v } 44 if v > mx { mx = v } 45 } 46 } 47 i = i + 1 48 } 49 if agg_kind == Q_COUNT { return cnt } 50 if agg_kind == Q_SUM { return sum } 51 if agg_kind == Q_AVG { if cnt == 0 { return 0 } return sum / cnt } 52 if agg_kind == Q_MIN { return mn } 53 if agg_kind == Q_MAX { return mx } 54 return 0 55} 56 57// GROUP BY group_col: aggregate agg_col within each distinct group, over rows passing the filter. Writes the 58// distinct group keys to out_keys and their aggregate to out_agg; returns the group count. First-seen order. 59func q_grouped(agg_col: *i64, filter_col: *i64, group_col: *i64, n: i64, filter_op: i64, filter_thr: i64, agg_kind: i64, out_keys: *i64, out_agg: *i64, out_cnt: *i64, max_groups: i64) -> i64 { 60 var g: i64 = 0 61 // out_agg accumulates sum (or count/min/max); out_cnt tracks per-group row count for AVG. 62 var i: i64 = 0 63 while i < n { 64 if q_match(filter_col[i], filter_op, filter_thr) == 1 { 65 let k: i64 = group_col[i] 66 let v: i64 = agg_col[i] 67 // find group 68 var found: i64 = 0 - 1 69 var j: i64 = 0 70 while j < g { if out_keys[j] == k { found = j; j = g } else { j = j + 1 } } 71 if found < 0 { 72 if g < max_groups { 73 out_keys[g] = k 74 out_cnt[g] = 1 75 if agg_kind == Q_COUNT { out_agg[g] = 1 } 76 if agg_kind == Q_SUM { out_agg[g] = v } 77 if agg_kind == Q_AVG { out_agg[g] = v } 78 if agg_kind == Q_MIN { out_agg[g] = v } 79 if agg_kind == Q_MAX { out_agg[g] = v } 80 g = g + 1 81 } 82 } else { 83 out_cnt[found] = out_cnt[found] + 1 84 if agg_kind == Q_COUNT { out_agg[found] = out_agg[found] + 1 } 85 if agg_kind == Q_SUM { out_agg[found] = out_agg[found] + v } 86 if agg_kind == Q_AVG { out_agg[found] = out_agg[found] + v } 87 if agg_kind == Q_MIN { if v < out_agg[found] { out_agg[found] = v } } 88 if agg_kind == Q_MAX { if v > out_agg[found] { out_agg[found] = v } } 89 } 90 } 91 i = i + 1 92 } 93 // finalize AVG: divide accumulated sum by per-group count 94 if agg_kind == Q_AVG { 95 var f: i64 = 0 96 while f < g { if out_cnt[f] > 0 { out_agg[f] = out_agg[f] / out_cnt[f] } f = f + 1 } 97 } 98 return g 99} 100 101func q_kind_str(k: i64) -> *u8 { 102 if k == Q_COUNT { return "count" as *u8 } 103 if k == Q_SUM { return "sum" as *u8 } 104 if k == Q_AVG { return "avg" as *u8 } 105 if k == Q_MIN { return "min" as *u8 } 106 if k == Q_MAX { return "max" as *u8 } 107 return "?" as *u8 108}