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