1use hawdb_core::{LabelId, Value};
18use hawdb_plan_cypher::SortDirection;
19use hawdb_storage::{NodeRecord, RelRecord};
20use std::cmp::Ordering;
21use std::collections::BTreeMap;
22
23#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct Binding {
25 pub values: BTreeMap<String, Value>,
26 pub nodes: BTreeMap<String, NodeRecord>,
27 pub relationships: BTreeMap<String, RelRecord>,
28}
29
30impl Binding {
31 pub fn values(values: BTreeMap<String, Value>) -> Self {
32 Self {
33 values,
34 nodes: BTreeMap::new(),
35 relationships: BTreeMap::new(),
36 }
37 }
38
39 pub fn scalar(name: impl Into<String>, value: Value) -> Self {
40 Self::values(BTreeMap::from([(name.into(), value)]))
41 }
42}
43
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct TopNBinding {
46 pub sort_values: Vec<(Value, SortDirection)>,
47 pub ordinal: u64,
48 pub binding: Binding,
49}
50
51impl TopNBinding {
52 pub fn memory_bytes(&self) -> usize {
53 binding_memory_bytes(&self.binding).saturating_add(self.sort_values.iter().fold(
54 std::mem::size_of::<Vec<(Value, SortDirection)>>(),
55 |total, (value, _)| total.saturating_add(value_memory_bytes(value)),
56 ))
57 }
58}
59
60impl Ord for TopNBinding {
61 fn cmp(&self, other: &Self) -> Ordering {
62 for ((left, direction), (right, other_direction)) in
63 self.sort_values.iter().zip(&other.sort_values)
64 {
65 debug_assert_eq!(direction, other_direction);
66 let ordering = match direction {
67 SortDirection::Asc => left.cmp(right),
68 SortDirection::Desc => left.cmp(right).reverse(),
69 };
70 if ordering != Ordering::Equal {
71 return ordering;
72 }
73 }
74 self.ordinal.cmp(&other.ordinal)
75 }
76}
77
78impl PartialOrd for TopNBinding {
79 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
80 Some(self.cmp(other))
81 }
82}
83
84pub fn map_payload_bytes(values: &BTreeMap<String, Value>) -> usize {
85 values.iter().fold(0usize, |total, (name, value)| {
86 total
87 .saturating_add(name.len())
88 .saturating_add(value_payload_bytes(value))
89 })
90}
91
92pub fn map_memory_bytes(values: &BTreeMap<String, Value>) -> usize {
93 std::mem::size_of::<BTreeMap<String, Value>>().saturating_add(values.iter().fold(
94 0usize,
95 |total, (name, value)| {
96 total
97 .saturating_add(std::mem::size_of::<(String, Value)>() * 3)
98 .saturating_add(name.len())
99 .saturating_add(value_memory_bytes(value))
100 },
101 ))
102}
103
104pub fn value_memory_bytes(value: &Value) -> usize {
105 std::mem::size_of::<Value>().saturating_add(match value {
106 Value::Null | Value::Bool(_) | Value::Int(_) | Value::Float(_) => 0,
107 Value::String(value) => value.len(),
108 Value::Binary(value) => value.len(),
109 Value::Uuid(_) => 16,
110 Value::List(values) => values
111 .iter()
112 .fold(std::mem::size_of::<Vec<Value>>(), |total, value| {
113 total.saturating_add(value_memory_bytes(value))
114 }),
115 Value::Map(values) => map_memory_bytes(values),
116 })
117}
118
119pub fn binding_payload_bytes(binding: &Binding) -> usize {
120 map_payload_bytes(&binding.values)
121 .saturating_add(binding.nodes.iter().fold(0usize, |total, (name, node)| {
122 total
123 .saturating_add(name.len())
124 .saturating_add(std::mem::size_of_val(&node.id))
125 .saturating_add(
126 node.labels
127 .len()
128 .saturating_mul(std::mem::size_of::<LabelId>()),
129 )
130 .saturating_add(map_payload_bytes(&node.properties))
131 }))
132 .saturating_add(
133 binding
134 .relationships
135 .iter()
136 .fold(0usize, |total, (name, relationship)| {
137 total
138 .saturating_add(name.len())
139 .saturating_add(std::mem::size_of_val(&relationship.id))
140 .saturating_add(std::mem::size_of_val(&relationship.source))
141 .saturating_add(std::mem::size_of_val(&relationship.target))
142 .saturating_add(std::mem::size_of_val(&relationship.rel_type))
143 .saturating_add(map_payload_bytes(&relationship.properties))
144 }),
145 )
146}
147
148pub fn binding_memory_bytes(binding: &Binding) -> usize {
149 std::mem::size_of::<Binding>()
150 .saturating_add(binding_payload_bytes(binding))
151 .saturating_add(
152 binding
153 .values
154 .len()
155 .saturating_add(binding.nodes.len())
156 .saturating_add(binding.relationships.len())
157 .saturating_mul(std::mem::size_of::<usize>() * 6),
158 )
159}
160
161pub fn node_memory_bytes(node: &NodeRecord) -> usize {
162 std::mem::size_of::<NodeRecord>()
163 .saturating_add(
164 node.labels
165 .len()
166 .saturating_mul(std::mem::size_of::<LabelId>() * 3),
167 )
168 .saturating_add(map_memory_bytes(&node.properties))
169}
170
171pub fn relationship_memory_bytes(relationship: &RelRecord) -> usize {
172 std::mem::size_of::<RelRecord>().saturating_add(map_memory_bytes(&relationship.properties))
173}
174
175pub fn value_payload_bytes(value: &Value) -> usize {
176 match value {
177 Value::Null => 0,
178 Value::Bool(_) => std::mem::size_of::<bool>(),
179 Value::Int(_) => std::mem::size_of::<i64>(),
180 Value::Float(_) => std::mem::size_of::<f64>(),
181 Value::String(value) => value.len(),
182 Value::Binary(value) => value.len(),
183 Value::Uuid(_) => 16,
184 Value::List(values) => values.iter().fold(0usize, |total, value| {
185 total.saturating_add(value_payload_bytes(value))
186 }),
187 Value::Map(values) => map_payload_bytes(values),
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194 use hawdb_core::RelTypeId;
195 use hawdb_storage::{NodeId, RelId};
196 use std::collections::BTreeSet;
197
198 #[test]
199 fn value_only_constructors_leave_graph_bindings_empty() {
200 let values = Binding::values(BTreeMap::from([("left".to_string(), Value::Int(1))]));
201 assert_eq!(values.values["left"], Value::Int(1));
202 assert!(values.nodes.is_empty());
203 assert!(values.relationships.is_empty());
204
205 let scalar = Binding::scalar("right", Value::Bool(true));
206 assert_eq!(scalar.values["right"], Value::Bool(true));
207 assert!(scalar.nodes.is_empty());
208 assert!(scalar.relationships.is_empty());
209 }
210
211 #[test]
212 fn binding_accounting_includes_nested_values_and_graph_records() {
213 let binding = Binding {
214 values: BTreeMap::from([(
215 "nested".to_string(),
216 Value::List(vec![Value::Int(1), Value::String("two".to_string())]),
217 )]),
218 nodes: BTreeMap::from([(
219 "n".to_string(),
220 NodeRecord {
221 id: NodeId(7),
222 labels: BTreeSet::from([LabelId(3)]),
223 properties: BTreeMap::new(),
224 },
225 )]),
226 relationships: BTreeMap::from([(
227 "r".to_string(),
228 RelRecord {
229 id: RelId(11),
230 source: NodeId(7),
231 target: NodeId(8),
232 rel_type: RelTypeId(4),
233 properties: BTreeMap::new(),
234 },
235 )]),
236 };
237
238 assert!(binding_payload_bytes(&binding) > "nested".len() + "two".len());
239 assert!(binding_memory_bytes(&binding) > binding_payload_bytes(&binding));
240 }
241}