tantivy_columnar/column_values/
monotonic_column.rs

1use std::fmt::Debug;
2use std::marker::PhantomData;
3use std::ops::{Range, RangeInclusive};
4
5use crate::column_values::monotonic_mapping::StrictlyMonotonicFn;
6use crate::ColumnValues;
7
8struct MonotonicMappingColumn<C, T, Input> {
9    from_column: C,
10    monotonic_mapping: T,
11    _phantom: PhantomData<Input>,
12}
13
14/// Creates a view of a column transformed by a strictly monotonic mapping. See
15/// [`StrictlyMonotonicFn`].
16///
17/// E.g. apply a gcd monotonic_mapping([100, 200, 300]) == [1, 2, 3]
18/// monotonic_mapping.mapping() is expected to be injective, and we should always have
19/// monotonic_mapping.inverse(monotonic_mapping.mapping(el)) == el
20///
21/// The inverse of the mapping is required for:
22/// `fn get_positions_for_value_range(&self, range: RangeInclusive<T>) -> Vec<u64> `
23/// The user provides the original value range and we need to monotonic map them in the same way the
24/// serialization does before calling the underlying column.
25///
26/// Note that when opening a codec, the monotonic_mapping should be the inverse of the mapping
27/// during serialization. And therefore the monotonic_mapping_inv when opening is the same as
28/// monotonic_mapping during serialization.
29pub fn monotonic_map_column<C, T, Input, Output>(
30    from_column: C,
31    monotonic_mapping: T,
32) -> impl ColumnValues<Output>
33where
34    C: ColumnValues<Input> + 'static,
35    T: StrictlyMonotonicFn<Input, Output> + Send + Sync + 'static,
36    Input: PartialOrd + Debug + Send + Sync + Clone + 'static,
37    Output: PartialOrd + Debug + Send + Sync + Clone + 'static,
38{
39    MonotonicMappingColumn {
40        from_column,
41        monotonic_mapping,
42        _phantom: PhantomData,
43    }
44}
45
46impl<C, T, Input, Output> ColumnValues<Output> for MonotonicMappingColumn<C, T, Input>
47where
48    C: ColumnValues<Input> + 'static,
49    T: StrictlyMonotonicFn<Input, Output> + Send + Sync + 'static,
50    Input: PartialOrd + Send + Debug + Sync + Clone + 'static,
51    Output: PartialOrd + Send + Debug + Sync + Clone + 'static,
52{
53    #[inline(always)]
54    fn get_val(&self, idx: u32) -> Output {
55        let from_val = self.from_column.get_val(idx);
56        self.monotonic_mapping.mapping(from_val)
57    }
58
59    fn min_value(&self) -> Output {
60        let from_min_value = self.from_column.min_value();
61        self.monotonic_mapping.mapping(from_min_value)
62    }
63
64    fn max_value(&self) -> Output {
65        let from_max_value = self.from_column.max_value();
66        self.monotonic_mapping.mapping(from_max_value)
67    }
68
69    fn num_vals(&self) -> u32 {
70        self.from_column.num_vals()
71    }
72
73    fn iter(&self) -> Box<dyn Iterator<Item = Output> + '_> {
74        Box::new(
75            self.from_column
76                .iter()
77                .map(|el| self.monotonic_mapping.mapping(el)),
78        )
79    }
80
81    fn get_row_ids_for_value_range(
82        &self,
83        range: RangeInclusive<Output>,
84        doc_id_range: Range<u32>,
85        positions: &mut Vec<u32>,
86    ) {
87        self.from_column.get_row_ids_for_value_range(
88            self.monotonic_mapping.inverse(range.start().clone())
89                ..=self.monotonic_mapping.inverse(range.end().clone()),
90            doc_id_range,
91            positions,
92        )
93    }
94
95    // We voluntarily do not implement get_range as it yields a regression,
96    // and we do not have any specialized implementation anyway.
97}
98
99#[cfg(test)]
100mod tests {
101    use super::*;
102    use crate::column_values::monotonic_mapping::{
103        StrictlyMonotonicMappingInverter, StrictlyMonotonicMappingToInternal,
104    };
105    use crate::column_values::VecColumn;
106
107    #[test]
108    fn test_monotonic_mapping_iter() {
109        let vals: Vec<u64> = (0..100u64).map(|el| el * 10).collect();
110        let col = VecColumn::from(vals);
111        let mapped = monotonic_map_column(
112            col,
113            StrictlyMonotonicMappingInverter::from(StrictlyMonotonicMappingToInternal::<i64>::new()),
114        );
115        let val_i64s: Vec<u64> = mapped.iter().collect();
116        for i in 0..100 {
117            assert_eq!(val_i64s[i as usize], mapped.get_val(i));
118        }
119    }
120}