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}