use {
crate::{
recipe::{Ingredient, Method, Recipe},
result::Result,
Column, DatabaseInner, Value,
},
rayon::prelude::*,
serde::{Deserialize, Serialize},
std::{cmp::Ordering, collections::HashMap},
};
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct Index {
pub name: String,
pub column: String,
pub is_unique: bool,
}
#[derive(Clone, Debug)]
pub enum IndexFilter {
LessThan(String, Value), MoreThan(String, Value), Inner(Box<IndexFilter>, Box<IndexFilter>),
Outer(Box<IndexFilter>, Box<IndexFilter>),
}
impl Index {
pub fn new(name: String, column: String, is_unique: bool) -> Self {
Self {
name,
column,
is_unique,
}
}
pub async fn reset(
&self,
storage: &mut DatabaseInner,
table: &str,
columns: &[Column],
) -> Result<()> {
let rows = storage.scan_data(table).await?;
let column_index: usize = columns
.iter()
.enumerate()
.find_map(|(index, def)| (def.name == self.column).then(|| index))
.unwrap();
let mut rows: Vec<(Value, Vec<Value>)> =
rows.into_iter().map(|(key, row)| (key, row.0)).collect();
rows.par_sort_unstable_by(|(_, a_values), (_, b_values)| {
a_values[column_index]
.partial_cmp(&b_values[column_index])
.unwrap_or(Ordering::Equal)
});
let keys = rows
.into_iter()
.map(|(key, mut values)| (values.swap_remove(column_index), key))
.collect();
storage.update_index(table, &self.name, keys).await
}
}
impl Recipe {
pub fn reduce_by_index_filter(
self,
indexed_columns: HashMap<usize, (String, String)>,
) -> (Self, Option<HashMap<String, IndexFilter>>) {
use IndexFilter::*;
match self {
Recipe::Ingredient(_) => (),
Recipe::Method(ref method) => match *method.clone() {
Method::BinaryOperation(operator, left, right)
if operator as usize == Value::and as usize =>
{
let (left, left_filters) = left.reduce_by_index_filter(indexed_columns.clone());
let (right, right_filters) = right.reduce_by_index_filter(indexed_columns);
return (
Recipe::Method(Box::new(Method::BinaryOperation(operator, left, right))),
match (left_filters, right_filters) {
(Some(filters), None) | (None, Some(filters)) => Some(filters),
(Some(left_filters), Some(mut right_filters)) => Some(
left_filters
.into_iter()
.map(|(table, filter)| {
(
table.clone(),
match right_filters.remove(&table) {
Some(right) => {
Inner(Box::new(filter), Box::new(right))
}
None => filter,
},
)
})
.collect::<HashMap<String, IndexFilter>>()
.into_iter()
.chain(right_filters.into_iter())
.collect::<HashMap<String, IndexFilter>>(),
),
(None, None) => None, },
);
}
Method::BinaryOperation(
operator,
Recipe::Ingredient(Ingredient::Column(column)),
Recipe::Ingredient(Ingredient::Value(value)),
) if operator as usize == Value::eq as usize => {
{
if let Some((table, index)) = indexed_columns.get(&column) {
let mut filters = HashMap::new();
filters.insert(
table.clone(),
Inner(
Box::new(LessThan(index.clone(), value.inc())),
Box::new(MoreThan(index.clone(), value)),
),
); return (Recipe::TRUE, Some(filters));
}
}
}
Method::BinaryOperation(
operator,
Recipe::Ingredient(Ingredient::Column(column)),
Recipe::Ingredient(Ingredient::Value(value)),
) if operator as usize == Value::gt_eq as usize => {
if let Some((table, index)) = indexed_columns.get(&column) {
let mut filters = HashMap::new();
filters.insert(table.clone(), MoreThan(index.clone(), value));
return (Recipe::TRUE, Some(filters));
}
}
Method::BinaryOperation(
operator,
Recipe::Ingredient(Ingredient::Column(column)),
Recipe::Ingredient(Ingredient::Value(value)),
) if operator as usize == Value::gt as usize => {
if let Some((table, index)) = indexed_columns.get(&column) {
let mut filters = HashMap::new();
filters.insert(table.clone(), MoreThan(index.clone(), value.inc()));
return (Recipe::TRUE, Some(filters));
}
}
Method::BinaryOperation(
operator,
Recipe::Ingredient(Ingredient::Column(column)),
Recipe::Ingredient(Ingredient::Value(value)),
) if operator as usize == Value::lt as usize => {
if let Some((table, index)) = indexed_columns.get(&column) {
let mut filters = HashMap::new();
filters.insert(table.clone(), LessThan(index.clone(), value));
return (Recipe::TRUE, Some(filters));
}
}
Method::BinaryOperation(
operator,
Recipe::Ingredient(Ingredient::Column(column)),
Recipe::Ingredient(Ingredient::Value(value)),
) if operator as usize == Value::lt_eq as usize => {
if let Some((table, index)) = indexed_columns.get(&column) {
let mut filters = HashMap::new();
filters.insert(table.clone(), LessThan(index.clone(), value.inc()));
return (Recipe::TRUE, Some(filters));
}
}
_ => (),
},
}
(self, None)
}
}