Skip to main content

nodedb_query/window/
helpers.rs

1// SPDX-License-Identifier: Apache-2.0
2
3//! Shared helpers for window-function evaluation.
4
5use std::collections::HashMap;
6
7/// Group row indices by partition key, preserving first-seen partition order.
8pub(super) fn build_partitions(
9    rows: &[(String, serde_json::Value)],
10    partition_by: &[String],
11) -> Vec<Vec<usize>> {
12    if partition_by.is_empty() {
13        return vec![(0..rows.len()).collect()];
14    }
15
16    let mut groups: HashMap<String, Vec<usize>> = HashMap::new();
17    let mut order = Vec::new();
18
19    for (i, (_id, doc)) in rows.iter().enumerate() {
20        // Partition key uses JSON serialization: the string literal "null" and a missing
21        // field both produce "null" here, but a JSON string value "null" serializes as
22        // "\"null\"" and is therefore distinct from a missing field. This is intentional.
23        let key: String = partition_by
24            .iter()
25            .map(|col| {
26                doc.get(col)
27                    .map(|v| v.to_string())
28                    .unwrap_or_else(|| "null".to_string())
29            })
30            .collect::<Vec<_>>()
31            .join("\x00");
32        let entry = groups.entry(key.clone()).or_default();
33        if entry.is_empty() {
34            order.push(key);
35        }
36        entry.push(i);
37    }
38
39    order.iter().filter_map(|k| groups.remove(k)).collect()
40}
41
42pub(super) fn set_window_col(row: &mut serde_json::Value, alias: &str, val: serde_json::Value) {
43    if let serde_json::Value::Object(map) = row {
44        map.insert(alias.to_string(), val);
45    }
46}
47
48pub(super) fn get_field(doc: &serde_json::Value, field: &str) -> serde_json::Value {
49    doc.get(field).cloned().unwrap_or(serde_json::Value::Null)
50}
51
52pub(super) fn as_f64(v: &serde_json::Value) -> Option<f64> {
53    match v {
54        serde_json::Value::Number(n) => n.as_f64(),
55        serde_json::Value::String(s) => s.parse().ok(),
56        _ => None,
57    }
58}
59
60/// Returns true when row at index `b` has the same ORDER BY key as row at
61/// index `a` (used by peer-aware ranking like RANK and PERCENT_RANK).
62pub(super) fn order_keys_equal(
63    rows: &[(String, serde_json::Value)],
64    a: usize,
65    b: usize,
66    order_by: &[(String, bool)],
67) -> bool {
68    order_by
69        .iter()
70        .all(|(col, _)| get_field(&rows[a].1, col) == get_field(&rows[b].1, col))
71}