1use core::any::Any;
2
3use crate::{
4 index::{AnyIndex, Index, IndexType},
5 result::DbResult,
6 table::{Table, TableType},
7 Record,
8};
9
10#[allow(unused_imports)]
11use alloc::{boxed::Box, format, vec, vec::Vec, string::{String, ToString}};
12
13pub enum QueryCondition<T>
15where
16 T: TableType + 'static,
17{
18 By(Box<dyn AnyIndex<T>>, Box<dyn Any>),
19 And(Box<QueryCondition<T>>, Box<QueryCondition<T>>),
20 Or(Box<QueryCondition<T>>, Box<QueryCondition<T>>),
21}
22
23pub struct ConditionBuilder<T: TableType + 'static>(QueryCondition<T>);
25
26impl<T: TableType + 'static> ConditionBuilder<T> {
27 pub fn by<I: IndexType + 'static>(index: &Index<T, I>, value: I) -> Self {
34 Self(QueryCondition::By(Box::new(index.clone()), Box::new(value)))
35 }
36
37 pub fn and(left: Self, right: Self) -> Self {
44 Self(QueryCondition::And(Box::new(left.0), Box::new(right.0)))
45 }
46
47 pub fn or(left: Self, right: Self) -> Self {
54 Self(QueryCondition::Or(Box::new(left.0), Box::new(right.0)))
55 }
56
57 pub fn build(self) -> QueryCondition<T> {
63 self.0
64 }
65}
66
67impl<T: TableType + 'static> Into<QueryCondition<T>> for ConditionBuilder<T> {
68 fn into(self) -> QueryCondition<T> {
69 self.build()
70 }
71}
72
73pub struct QueryBuilder<T>
75where
76 T: TableType + 'static,
77{
78 table: Table<T>,
79 condition: Option<QueryCondition<T>>,
80}
81
82impl<T> QueryBuilder<T>
83where
84 T: TableType,
85{
86 pub fn new(table: &Table<T>) -> Self {
92 Self {
93 table: table.clone(),
94 condition: None,
95 }
96 }
97
98 pub fn with_condition<C: Into<QueryCondition<T>>>(mut self, condition: C) -> Self {
105 self.condition = Some(condition.into());
106 self
107 }
108
109 fn check_valid(&self) -> DbResult<()> {
111 match &self.condition {
112 Some(_) => Ok(()),
113 None => Err(crate::result::TinyBaseError::QueryBuilder(
114 "No search condition provided".into(),
115 )),
116 }
117 }
118
119 pub fn select(self) -> DbResult<Vec<Record<T>>> {
125 self.check_valid()?;
126 Self::select_recursive(self.condition.unwrap())
127 }
128
129 pub fn update(self, updater: fn(T) -> T) -> DbResult<Vec<Record<T>>> {
139 self.check_valid()?;
140 let ids: Vec<u64> = Self::select_recursive(self.condition.unwrap())?
141 .iter()
142 .map(|record| record.id)
143 .collect();
144
145 self.table.update(&ids, updater)
146 }
147
148 pub fn delete(self) -> DbResult<Vec<Record<T>>> {
154 self.check_valid()?;
155 let selected = Self::select_recursive(self.condition.unwrap())?;
156
157 let mut removed = vec![];
158
159 for record in &selected {
160 if let Some(record) = self.table.delete(record.id)? {
161 removed.push(record);
162 }
163 }
164
165 Ok(removed)
166 }
167
168 fn select_recursive(condition: QueryCondition<T>) -> DbResult<Vec<Record<T>>> {
170 match condition {
171 QueryCondition::By(index, value) => index.search(value),
172 QueryCondition::And(left, right) => {
173 let left_records = Self::select_recursive(*left)?;
174 let right_records = Self::select_recursive(*right)?;
175
176 let mut intersection: Vec<Record<T>> = left_records.clone();
177 intersection.retain(|record| {
178 right_records
179 .iter()
180 .any(|other_record| record.id == other_record.id)
181 });
182
183 Ok(intersection)
184 }
185 QueryCondition::Or(left, right) => {
186 let mut records: Vec<Record<T>> =
187 Self::select_recursive(*left)?.into_iter().collect();
188 records.extend(Self::select_recursive(*right)?.into_iter());
189
190 let mut seen = Vec::new();
191 records.retain(|item| {
192 if seen.contains(&item.id) {
193 false
194 } else {
195 seen.push(item.id);
196 true
197 }
198 });
199
200 Ok(records)
201 }
202 }
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use alloc::borrow::ToOwned;
209
210 use super::*;
211 use crate::TinyBase;
212
213 #[test]
214 fn query_builder_select_and() {
215 let db = TinyBase::new(None, true);
216 let table: Table<String> = db.open_table("test_table").unwrap();
217
218 let index = table
220 .create_index("name", |value| value.to_owned())
221 .unwrap();
222
223 let length = table.create_index("length", |value| value.len()).unwrap();
224
225 let value1 = table.insert("value1".to_string()).unwrap();
227 table.insert("value2".to_string()).unwrap();
228
229 let result_1 = QueryBuilder::new(&table)
230 .with_condition(ConditionBuilder::and(
231 ConditionBuilder::by(&index, "value1".to_string()),
232 ConditionBuilder::by(&index, "value2".to_string()),
233 ))
234 .select()
235 .expect("Select failed");
236
237 assert_eq!(result_1.len(), 0);
238
239 let result_2 = QueryBuilder::new(&table)
240 .with_condition(ConditionBuilder::and(
241 ConditionBuilder::by(&index, "value1".to_string()),
242 ConditionBuilder::by(&length, 6),
243 ))
244 .select()
245 .expect("Select failed");
246
247 assert_eq!(result_2.len(), 1);
248 assert_eq!(result_2[0].id, value1);
249 }
250
251 #[test]
252 fn query_builder_select_or() {
253 let db = TinyBase::new(None, true);
254 let table: Table<String> = db.open_table("test_table").unwrap();
255
256 let index = table
258 .create_index("name", |value| value.to_owned())
259 .unwrap();
260
261 table.insert("value1".to_string()).unwrap();
263 table.insert("value2".to_string()).unwrap();
264
265 let selected_records = QueryBuilder::new(&table)
266 .with_condition(ConditionBuilder::or(
267 ConditionBuilder::by(&index, "value1".to_string()),
268 ConditionBuilder::by(&index, "value2".to_string()),
269 ))
270 .select()
271 .expect("Select failed");
272
273 assert_eq!(selected_records.len(), 2);
274 }
275
276 #[test]
277 fn query_builder_select_combined() {
278 let db = TinyBase::new(None, true);
279 let table: Table<String> = db.open_table("test_table").unwrap();
280
281 let name = table
283 .create_index("name", |value| value.to_owned())
284 .unwrap();
285
286 let length = table.create_index("length", |value| value.len()).unwrap();
287
288 table.insert("value1".to_string()).unwrap();
290 table.insert("value2".to_string()).unwrap();
291
292 let selected_records = QueryBuilder::new(&table)
293 .with_condition(ConditionBuilder::and(
294 ConditionBuilder::or(
295 ConditionBuilder::by(&name, "value1".to_owned()),
296 ConditionBuilder::by(&name, "value2".to_owned()),
297 ),
298 ConditionBuilder::by(&length, 6),
299 ))
300 .select()
301 .expect("Select failed");
302
303 assert_eq!(selected_records.len(), 2);
304 }
305
306 #[test]
307 fn query_builder_update() {
308 let db = TinyBase::new(None, true);
309 let table: Table<String> = db.open_table("test_table").unwrap();
310
311 let index = table
313 .create_index("name", |value| value.to_owned())
314 .unwrap();
315
316 let length = table.create_index("length", |value| value.len()).unwrap();
317
318 table.insert("value1".to_string()).unwrap();
320 table.insert("value2".to_string()).unwrap();
321
322 let updated_records = QueryBuilder::new(&table)
323 .with_condition(ConditionBuilder::and(
324 ConditionBuilder::by(&index, "value1".to_string()),
325 ConditionBuilder::by(&length, 6),
326 ))
327 .update(|_| "updated_value".to_string())
328 .expect("Update failed");
329
330 assert_eq!(updated_records.len(), 1);
331 assert_eq!(updated_records[0].data, "updated_value");
332 }
333
334 #[test]
335 fn query_builder_delete() {
336 let db = TinyBase::new(None, true);
337 let table: Table<String> = db.open_table("test_table").unwrap();
338
339 table.insert("value1".to_string()).unwrap();
341 table.insert("value2".to_string()).unwrap();
342
343 let index = table
345 .create_index("name", |value| value.to_owned())
346 .unwrap();
347
348 let deleted_records = QueryBuilder::new(&table)
349 .with_condition(ConditionBuilder::by(&index, "value1".to_string()))
350 .delete()
351 .expect("Update failed");
352
353 assert_eq!(deleted_records.len(), 1);
354
355 let records = index.select(&"value1".to_string()).expect("Select failed");
357 assert_eq!(records.len(), 0);
358 }
359}