vortex_file/footer/
file_statistics.rs1use std::sync::Arc;
10
11use flatbuffers::FlatBufferBuilder;
12use flatbuffers::WIPOffset;
13use itertools::Itertools;
14use vortex_array::dtype::DType;
15use vortex_array::flatbuffers::FlatBufferRoot;
16use vortex_array::flatbuffers::WriteFlatBuffer;
17use vortex_array::stats::StatsSet;
18use vortex_error::VortexResult;
19use vortex_error::vortex_ensure_eq;
20use vortex_session::VortexSession;
21
22use crate::flatbuffers::footer as fb;
23
24#[derive(Clone, Debug)]
30pub struct FileStatistics {
31 stats: Arc<[StatsSet]>,
33 dtypes: Arc<[DType]>,
35}
36
37impl FileStatistics {
38 pub fn new(stats: Arc<[StatsSet]>, dtypes: Arc<[DType]>) -> Self {
44 assert_eq!(
45 stats.len(),
46 dtypes.len(),
47 "stats and dtypes must have the same length"
48 );
49
50 Self { stats, dtypes }
51 }
52
53 pub fn new_with_dtype(stats: Arc<[StatsSet]>, file_dtype: &DType) -> Self {
62 if let DType::Struct(struct_fields, _) = file_dtype {
63 assert_eq!(
64 stats.len(),
65 struct_fields.nfields(),
66 "stats length must match number of struct fields"
67 );
68
69 let dtypes = struct_fields.fields().collect();
70
71 Self { stats, dtypes }
72 } else {
73 assert_eq!(
74 stats.len(),
75 1,
76 "non-struct dtype must have exactly 1 statistic"
77 );
78
79 Self {
80 stats,
81 dtypes: Arc::new([file_dtype.clone()]),
82 }
83 }
84 }
85
86 pub fn from_flatbuffer<'a>(
91 fb: &fb::FileStatistics<'a>,
92 file_dtype: &DType,
93 session: &VortexSession,
94 ) -> VortexResult<Self> {
95 let field_stats = fb.field_stats().unwrap_or_default();
96
97 if let DType::Struct(struct_fields, _) = file_dtype {
98 vortex_ensure_eq!(field_stats.len(), struct_fields.nfields());
99
100 let stats_sets: Arc<[StatsSet]> = field_stats
101 .into_iter()
102 .zip(struct_fields.fields())
103 .map(|(array_stat, field_dtype)| {
104 StatsSet::from_flatbuffer(&array_stat, &field_dtype, session)
105 })
106 .try_collect()?;
107
108 let dtypes = struct_fields.fields().collect();
109
110 Ok(Self {
111 stats: stats_sets,
112 dtypes,
113 })
114 } else {
115 vortex_ensure_eq!(field_stats.len(), 1);
116
117 let array_stat = field_stats.get(0);
118 let stats_set = StatsSet::from_flatbuffer(&array_stat, file_dtype, session)?;
119
120 Ok(Self {
121 stats: Arc::new([stats_set]),
122 dtypes: Arc::new([file_dtype.clone()]),
123 })
124 }
125 }
126
127 pub fn stats_sets(&self) -> &Arc<[StatsSet]> {
129 &self.stats
130 }
131
132 pub fn dtypes(&self) -> &Arc<[DType]> {
134 &self.dtypes
135 }
136
137 pub fn get(&self, field_idx: usize) -> (&StatsSet, &DType) {
143 (&self.stats[field_idx], &self.dtypes[field_idx])
144 }
145}
146
147impl<'a> IntoIterator for &'a FileStatistics {
148 type Item = (&'a StatsSet, &'a DType);
149 type IntoIter = std::iter::Zip<std::slice::Iter<'a, StatsSet>, std::slice::Iter<'a, DType>>;
150
151 fn into_iter(self) -> Self::IntoIter {
152 self.stats.iter().zip(self.dtypes.iter())
153 }
154}
155
156impl FlatBufferRoot for FileStatistics {}
157
158impl WriteFlatBuffer for FileStatistics {
159 type Target<'a> = fb::FileStatistics<'a>;
160
161 fn write_flatbuffer<'fb>(
162 &self,
163 fbb: &mut FlatBufferBuilder<'fb>,
164 ) -> VortexResult<WIPOffset<Self::Target<'fb>>> {
165 let field_stats = self
166 .stats_sets()
167 .iter()
168 .map(|s| s.write_flatbuffer(fbb))
169 .collect::<VortexResult<Vec<_>>>()?;
170 let field_stats = fbb.create_vector(field_stats.as_slice());
171
172 Ok(fb::FileStatistics::create(
173 fbb,
174 &fb::FileStatisticsArgs {
175 field_stats: Some(field_stats),
176 },
177 ))
178 }
179}