native_db_32bit/transaction/query/scan/
secondary_scan.rs

1use crate::db_type::{unwrap_item, DatabaseInnerKeyValue, DatabaseInnerKeyValueRange, Input};
2use crate::InnerKeyValue;
3use redb;
4use std::marker::PhantomData;
5use std::ops::RangeBounds;
6
7/// Scan values from the database by secondary key.
8pub struct SecondaryScan<PrimaryTable, SecondaryTable, T: Input>
9where
10    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
11    SecondaryTable: redb::ReadableTable<DatabaseInnerKeyValue, DatabaseInnerKeyValue>,
12{
13    pub(crate) primary_table: PrimaryTable,
14    pub(crate) secondary_table: SecondaryTable,
15    pub(crate) _marker: PhantomData<T>,
16}
17
18impl<PrimaryTable, SecondaryTable, T: Input> SecondaryScan<PrimaryTable, SecondaryTable, T>
19where
20    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
21    SecondaryTable: redb::ReadableTable<DatabaseInnerKeyValue, DatabaseInnerKeyValue>,
22{
23    pub(crate) fn new(primary_table: PrimaryTable, secondary_table: SecondaryTable) -> Self {
24        Self {
25            primary_table,
26            secondary_table,
27            _marker: PhantomData::default(),
28        }
29    }
30
31    /// Iterate over all values by secondary key.
32    ///
33    /// If the secondary key is [`optional`](struct.DatabaseBuilder.html#optional) you will
34    /// get all values that have the secondary key set.
35    ///
36    /// Anatomy of a secondary key it is a `enum` with the following structure: `<table_name>Key::<name>`.
37    ///
38    /// # Example
39    /// ```rust
40    /// use native_db::*;
41    /// use native_model::{native_model, Model};
42    /// use serde::{Deserialize, Serialize};
43    ///
44    /// #[derive(Serialize, Deserialize)]
45    /// #[native_model(id=1, version=1)]
46    /// #[native_db]
47    /// struct Data {
48    ///     #[primary_key]
49    ///     id: u64,
50    ///     #[secondary_key(optional)]
51    ///     name: Option<String>,
52    /// }
53    ///
54    /// fn main() -> Result<(), db_type::Error> {
55    ///     let mut builder = DatabaseBuilder::new();
56    ///     builder.define::<Data>()?;
57    ///     let db = builder.create_in_memory()?;
58    ///     
59    ///     // Open a read transaction
60    ///     let r = db.r_transaction()?;
61    ///     
62    ///     // Get only values that have the secondary key set (name is not None)
63    ///     let _values: Vec<Data> = r.scan().secondary(DataKey::name)?.all().collect();
64    ///     Ok(())
65    /// }
66    /// ```
67    pub fn all(&self) -> SecondaryScanIterator<PrimaryTable, T> {
68        let range = self
69            .secondary_table
70            .range::<DatabaseInnerKeyValue>(..)
71            .unwrap();
72        SecondaryScanIterator {
73            primary_table: &self.primary_table,
74            range,
75            _marker: PhantomData::default(),
76        }
77    }
78
79    /// Iterate over all values by secondary key.
80    ///
81    /// Anatomy of a secondary key it is a `enum` with the following structure: `<table_name>Key::<name>`.
82    ///
83    /// # Example
84    /// ```rust
85    /// use native_db::*;
86    /// use native_model::{native_model, Model};
87    /// use serde::{Deserialize, Serialize};
88    ///
89    /// #[derive(Serialize, Deserialize)]
90    /// #[native_model(id=1, version=1)]
91    /// #[native_db]
92    /// struct Data {
93    ///     #[primary_key]
94    ///     id: u64,
95    ///     #[secondary_key]
96    ///     name: String,
97    /// }
98    ///
99    /// fn main() -> Result<(), db_type::Error> {
100    ///     let mut builder = DatabaseBuilder::new();
101    ///     builder.define::<Data>()?;
102    ///     let db = builder.create_in_memory()?;
103    ///     
104    ///     // Open a read transaction
105    ///     let r = db.r_transaction()?;
106    ///     
107    ///     // Get only values that have the secondary key name from C to the end
108    ///     let _values: Vec<Data> = r.scan().secondary(DataKey::name)?.range("C"..).collect();
109    ///     Ok(())
110    /// }
111    /// ```
112    pub fn range<TR: InnerKeyValue, R: RangeBounds<TR>>(
113        &self,
114        range: R,
115    ) -> SecondaryScanIterator<PrimaryTable, T> {
116        let database_inner_key_value_range = DatabaseInnerKeyValueRange::new(range);
117        let range = self
118            .secondary_table
119            .range::<DatabaseInnerKeyValue>(database_inner_key_value_range)
120            .unwrap();
121        SecondaryScanIterator {
122            primary_table: &self.primary_table,
123            range,
124            _marker: PhantomData::default(),
125        }
126    }
127
128    /// Iterate over all values by secondary key.
129    ///
130    /// Anatomy of a secondary key it is a `enum` with the following structure: `<table_name>Key::<name>`.
131    ///
132    /// # Example
133    /// ```rust
134    /// use native_db::*;
135    /// use native_model::{native_model, Model};
136    /// use serde::{Deserialize, Serialize};
137    ///
138    /// #[derive(Serialize, Deserialize)]
139    /// #[native_model(id=1, version=1)]
140    /// #[native_db]
141    /// struct Data {
142    ///     #[primary_key]
143    ///     id: u64,
144    ///     #[secondary_key]
145    ///     name: String,
146    /// }
147    ///
148    /// fn main() -> Result<(), db_type::Error> {
149    ///     let mut builder = DatabaseBuilder::new();
150    ///     builder.define::<Data>()?;
151    ///     let db = builder.create_in_memory()?;
152    ///     
153    ///     // Open a read transaction
154    ///     let r = db.r_transaction()?;
155    ///     
156    ///     // Get only values that have the secondary key name starting with "hello"
157    ///     let _values: Vec<Data> = r.scan().secondary(DataKey::name)?.start_with("hello").collect();
158    ///     Ok(())
159    /// }
160    /// ```
161    pub fn start_with<'a>(
162        &'a self,
163        start_with: impl InnerKeyValue + 'a,
164    ) -> SecondaryScanIteratorStartWith<'a, PrimaryTable, T> {
165        let start_with = start_with.database_inner_key_value();
166        let range = self
167            .secondary_table
168            .range::<DatabaseInnerKeyValue>(start_with.clone()..)
169            .unwrap();
170        SecondaryScanIteratorStartWith {
171            primary_table: &self.primary_table,
172            start_with,
173            range,
174            _marker: PhantomData::default(),
175        }
176    }
177}
178
179pub struct SecondaryScanIterator<'a, PrimaryTable, T: Input>
180where
181    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
182{
183    pub(crate) primary_table: &'a PrimaryTable,
184    pub(crate) range: redb::Range<'a, DatabaseInnerKeyValue, DatabaseInnerKeyValue>,
185    pub(crate) _marker: PhantomData<T>,
186}
187
188impl<'a, PrimaryTable, T: Input> Iterator for SecondaryScanIterator<'a, PrimaryTable, T>
189where
190    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
191{
192    type Item = T;
193
194    fn next(&mut self) -> Option<Self::Item> {
195        match self.range.next() {
196            Some(Ok((_, key))) => {
197                if let Ok(value) = self.primary_table.get(key.value()) {
198                    unwrap_item(value)
199                } else {
200                    None
201                }
202            }
203            _ => None,
204        }
205    }
206}
207
208impl<'a, PrimaryTable, T: Input> DoubleEndedIterator for SecondaryScanIterator<'a, PrimaryTable, T>
209where
210    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
211{
212    fn next_back(&mut self) -> Option<Self::Item> {
213        match self.range.next_back() {
214            Some(Ok((_, key))) => unwrap_item(self.primary_table.get(key.value()).unwrap()),
215            _ => None,
216        }
217    }
218}
219
220pub struct SecondaryScanIteratorStartWith<'a, PrimaryTable, T>
221where
222    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
223    T: Input,
224{
225    pub(crate) primary_table: &'a PrimaryTable,
226    pub(crate) start_with: DatabaseInnerKeyValue,
227    pub(crate) range: redb::Range<'a, DatabaseInnerKeyValue, DatabaseInnerKeyValue>,
228    pub(crate) _marker: PhantomData<T>,
229}
230
231impl<'a, PrimaryTable, T> Iterator for SecondaryScanIteratorStartWith<'a, PrimaryTable, T>
232where
233    PrimaryTable: redb::ReadableTable<DatabaseInnerKeyValue, &'static [u8]>,
234    T: Input,
235{
236    type Item = T;
237
238    fn next(&mut self) -> Option<Self::Item> {
239        match self.range.next() {
240            Some(Ok((secondary_key, primary_key))) => {
241                if secondary_key
242                    .value()
243                    .as_slice()
244                    .starts_with(self.start_with.as_slice())
245                {
246                    unwrap_item(self.primary_table.get(primary_key.value()).unwrap())
247                } else {
248                    None
249                }
250            }
251            _ => None,
252        }
253    }
254}