Shared msgpack-row sorting utilities used by scan handlers. Sort msgpack-map rows by `(field, ascending)` keys. Decodes each row to JSON, extracts the sort fields, and reorders the original msgpack bytes. The `bool` matches the document scan convention (`true` = ascending). Decode failures for individual rows are logged at debug level and treated as `null` for comparison purposes, so they sort to
(
rows: &mut [Vec<u8>],
sort_keys: &[(String, bool)],
)
| 10 | /// as `null` for comparison purposes, so they sort to the start/end rather |
| 11 | /// than causing the entire sort to fail. |
| 12 | pub(in crate::data::executor) fn sort_msgpack_rows( |
| 13 | rows: &mut [Vec<u8>], |
| 14 | sort_keys: &[(String, bool)], |
| 15 | ) { |
| 16 | let decoded: Vec<serde_json::Value> = rows |
| 17 | .iter() |
| 18 | .map(|r| match nodedb_types::json_from_msgpack(r) { |
| 19 | Ok(v) => v, |
| 20 | Err(e) => { |
| 21 | tracing::debug!(err = %e, "msgpack decode failed during sort; treating row as null"); |
| 22 | serde_json::Value::Null |
| 23 | } |
| 24 | }) |
| 25 | .collect(); |
| 26 | |
| 27 | let mut indices: Vec<usize> = (0..rows.len()).collect(); |
| 28 | indices.sort_by(|&a, &b| { |
| 29 | for (field, asc) in sort_keys { |
| 30 | let va = decoded[a].get(field).unwrap_or(&serde_json::Value::Null); |
| 31 | let vb = decoded[b].get(field).unwrap_or(&serde_json::Value::Null); |
| 32 | let ord = compare_json(va, vb); |
| 33 | if ord != std::cmp::Ordering::Equal { |
| 34 | return if *asc { ord } else { ord.reverse() }; |
| 35 | } |
| 36 | } |
| 37 | std::cmp::Ordering::Equal |
| 38 | }); |
| 39 | |
| 40 | let original: Vec<Vec<u8>> = rows.to_vec(); |
| 41 | for (dst, src) in indices.iter().enumerate() { |
| 42 | rows[dst] = original[*src].clone(); |
| 43 | } |
| 44 | } |
| 45 | |
| 46 | fn compare_json(a: &serde_json::Value, b: &serde_json::Value) -> std::cmp::Ordering { |
| 47 | use serde_json::Value; |
no test coverage detected