linkrs_storage/edge/csr_with_properties/
encoding.rs1use super::{CsrWithProperties, FSST_MAX_SYMBOLS};
2use linkrs_core::{StorageError, StorageResult, Value};
3use std::sync::Arc;
4
5impl CsrWithProperties {
6 fn zone_bounds(&self, column: &str) -> Option<(Value, Value)> {
13 let col = self
14 .column_index
15 .get(column)
16 .and_then(|&idx| self.property_columns.get(idx))?;
17 let mut min: Option<Value> = None;
18 let mut max: Option<Value> = None;
19 for zone in col.zone_maps() {
20 if let Some(v) = &zone.min {
21 match &min {
22 Some(cur)
23 if crate::vertex::column::compare_values(cur, v)
24 != std::cmp::Ordering::Greater => {}
25 _ => min = Some(v.clone()),
26 }
27 }
28 if let Some(v) = &zone.max {
29 match &max {
30 Some(cur)
31 if crate::vertex::column::compare_values(cur, v)
32 != std::cmp::Ordering::Less => {}
33 _ => max = Some(v.clone()),
34 }
35 }
36 }
37 match (min, max) {
38 (Some(lower), Some(upper)) => Some((lower, upper)),
39 _ => None,
40 }
41 }
42
43 pub fn column_stats_snapshot(
44 &self,
45 column: &str,
46 ) -> Option<crate::stats_reader::ColumnStatsSnapshot> {
47 let col = self
48 .column_index
49 .get(column)
50 .and_then(|&idx| self.property_columns.get(idx))?;
51 let bounds = self.zone_bounds(column);
52 let persisted = col.stats();
53 let null_count = persisted.as_ref().map(|s| s.null_count);
54 let (distinct_count, hll) = match persisted.as_ref().and_then(|s| s.hll.clone()) {
55 Some(h) => {
56 let est = h.estimate();
57 (Some(est), persisted.as_ref().and_then(|s| s.hll.clone()))
58 }
59 None => (None, None),
60 };
61 Some(crate::stats_reader::ColumnStatsSnapshot {
62 row_count: self.row_count as u64,
63 null_count,
64 distinct_count,
65 hll,
66 min_value: bounds.as_ref().map(|(lower, _)| lower.clone()),
67 max_value: bounds.as_ref().map(|(_, upper)| upper.clone()),
68 })
69 }
70
71 pub fn prune_bounds(&self, column: &str) -> Option<(Value, Value)> {
75 self.zone_bounds(column)
76 }
77
78 pub fn column_encoding_type(&self, column: &str) -> Option<crate::encoding::EncodingType> {
80 self.column_index
81 .get(column)
82 .and_then(|&idx| self.property_columns.get(idx))
83 .map(|c| c.encoding_type())
84 }
85
86 pub fn apply_encoding_to_column(
92 &mut self,
93 column: &str,
94 encoding_type: crate::encoding::EncodingType,
95 fsst_max_symbols: usize,
96 ) -> StorageResult<()> {
97 let idx = self
98 .column_index
99 .get(column)
100 .copied()
101 .ok_or_else(|| StorageError::column_not_found(column.to_string()))?;
102 let col = &mut self.property_columns[idx];
103 if col.is_empty() {
104 return Ok(());
105 }
106 col.apply_encoding_to_chunks(encoding_type, fsst_max_symbols)?;
107 if let Some(schema) = self.property_schema.get_mut(idx) {
108 schema.encoding_type = col.encoding_type();
109 }
110 Ok(())
111 }
112
113 pub fn auto_encode_properties(&mut self) -> usize {
120 let targets = self.all_column_targets();
121 self.encode_targets(&targets, 0)
122 }
123
124 pub fn adapt_encodings_for_checkpoint(
133 &mut self,
134 dirty_only: Option<&[Arc<str>]>,
135 min_rows: usize,
136 ) -> usize {
137 let targets = match dirty_only {
138 Some(names) if !names.is_empty() => names
139 .iter()
140 .filter_map(|name| {
141 self.column_index
142 .get(&**name)
143 .map(|idx| (*idx, name.clone()))
144 })
145 .collect(),
146 _ => self.all_column_targets(),
147 };
148 self.encode_targets(&targets, min_rows)
149 }
150
151 fn all_column_targets(&self) -> Vec<(usize, Arc<str>)> {
153 self.property_schema
154 .iter()
155 .enumerate()
156 .map(|(idx, schema)| (idx, schema.name.clone()))
157 .collect()
158 }
159
160 fn encode_targets(&mut self, targets: &[(usize, Arc<str>)], min_rows: usize) -> usize {
167 let selector = crate::encoding::EncodingSelector::default();
168 let mut encoded = 0usize;
169 for (idx, name) in targets {
170 let Some(col) = self.property_columns.get(*idx) else {
171 continue;
172 };
173 if col.len() < min_rows || col.is_empty() {
174 continue;
175 }
176 let selected = col.select_encoding(&selector);
177 if selected == crate::encoding::EncodingType::None || selected == col.encoding_type() {
178 continue;
179 }
180 if self
181 .apply_encoding_to_column(name, selected, FSST_MAX_SYMBOLS)
182 .is_ok()
183 {
184 encoded += 1;
185 }
186 }
187 encoded
188 }
189}