use super::{
operand::{
MultipleValuesComparisonOperand, MultipleValuesWithIndexOperand,
SingleValueComparisonOperand, SingleValueWithIndexOperand,
},
BinaryArithmeticKind, MultipleComparisonKind, SingleComparisonKind, SingleKindWithIndex,
UnaryArithmeticKind,
};
use crate::{
errors::{MedRecordError, MedRecordResult},
medrecord::{
datatypes::{
Abs, Ceil, Contains, DataType, EndsWith, Floor, Lowercase, Mod, Pow, Round, Slice,
Sqrt, StartsWith, Trim, TrimEnd, TrimStart, Uppercase,
},
querying::{
tee_grouped_iterator,
values::{
operand::{MultipleValuesWithoutIndexOperand, SingleValueWithoutIndexOperand},
SingleKindWithoutIndex,
},
BoxedIterator, DeepClone, EvaluateForward, EvaluateForwardGrouped, GroupedIterator,
RootOperand,
},
MedRecordValue, Wrapper,
},
MedRecord,
};
use itertools::Itertools;
use medmodels_utils::{aliases::MrHashSet, traits::ReadWriteOrPanic};
use rand::{rng, seq::IteratorRandom};
use std::{
cmp::Ordering,
ops::{Add, Div, Mul, Range, Sub},
};
macro_rules! get_median {
($values:ident, $variant:ident) => {
if $values.len() % 2 == 0 {
let middle = $values.len() / 2;
let first = $values.get(middle - 1).unwrap();
let second = $values.get(middle).unwrap();
let first = MedRecordValue::$variant(*first);
let second = MedRecordValue::$variant(*second);
first.add(second).unwrap().div(MedRecordValue::Int(2))
} else {
let middle = $values.len() / 2;
Ok(MedRecordValue::$variant(
$values.get(middle).unwrap().clone(),
))
}
};
}
#[derive(Debug, Clone)]
pub enum MultipleValuesWithIndexOperation<O: RootOperand> {
ValueWithIndexOperation {
operand: Wrapper<SingleValueWithIndexOperand<O>>,
},
ValueWithoutIndexOperation {
operand: Wrapper<SingleValueWithoutIndexOperand<O>>,
},
SingleValueComparisonOperation {
operand: SingleValueComparisonOperand,
kind: SingleComparisonKind,
},
MultipleValuesComparisonOperation {
operand: MultipleValuesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleValueComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsFloat,
IsBool,
IsDateTime,
IsDuration,
IsNull,
IsMax,
IsMin,
EitherOr {
either: Wrapper<MultipleValuesWithIndexOperand<O>>,
or: Wrapper<MultipleValuesWithIndexOperand<O>>,
},
Exclude {
operand: Wrapper<MultipleValuesWithIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleValuesWithIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for MultipleValuesWithIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::ValueWithIndexOperation { operand } => Self::ValueWithIndexOperation {
operand: operand.deep_clone(),
},
Self::ValueWithoutIndexOperation { operand } => Self::ValueWithoutIndexOperation {
operand: operand.deep_clone(),
},
Self::SingleValueComparisonOperation { operand, kind } => {
Self::SingleValueComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::MultipleValuesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::UnaryArithmeticOperation { kind } => {
Self::UnaryArithmeticOperation { kind: kind.clone() }
}
Self::Slice(range) => Self::Slice(range.clone()),
Self::IsString => Self::IsString,
Self::IsInt => Self::IsInt,
Self::IsFloat => Self::IsFloat,
Self::IsBool => Self::IsBool,
Self::IsDateTime => Self::IsDateTime,
Self::IsDuration => Self::IsDuration,
Self::IsNull => Self::IsNull,
Self::IsMax => Self::IsMax,
Self::IsMin => Self::IsMin,
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::Merge { operand } => Self::Merge {
operand: operand.deep_clone(),
},
}
}
}
impl<O: RootOperand> MultipleValuesWithIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
Ok(match self {
Self::ValueWithIndexOperation { operand } => {
Self::evaluate_value_with_index_operation(medrecord, values, operand)?
}
Self::ValueWithoutIndexOperation { operand } => {
Self::evaluate_value_without_index_operation(medrecord, values, operand)?
}
Self::SingleValueComparisonOperation { operand, kind } => {
Self::evaluate_single_value_comparison_operation(medrecord, values, operand, kind)?
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_values_comparison_operation(
medrecord, values, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
Self::evaluate_binary_arithmetic_operation(medrecord, values, operand, kind)?,
),
Self::UnaryArithmeticOperation { kind } => Box::new(
Self::evaluate_unary_arithmetic_operation(values, kind.clone()),
),
Self::Slice(range) => Box::new(Self::evaluate_slice(values, range.clone())),
Self::IsString => Box::new(Self::evaluate_is_string(values)),
Self::IsInt => Box::new(Self::evaluate_is_int(values)),
Self::IsFloat => Box::new(Self::evaluate_is_float(values)),
Self::IsBool => Box::new(Self::evaluate_is_bool(values)),
Self::IsDateTime => Box::new(Self::evaluate_is_datetime(values)),
Self::IsDuration => Box::new(Self::evaluate_is_duration(values)),
Self::IsNull => Box::new(Self::evaluate_is_null(values)),
Self::IsMax => Self::evaluate_is_max(values)?,
Self::IsMin => Self::evaluate_is_min(values)?,
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, values, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, values, operand)?,
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
pub(crate) fn get_max<'a>(
mut values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>> {
let max_value = values.next();
let Some(max_value) = max_value else {
return Ok(None);
};
let max_value = values.try_fold(max_value, |max_value, value| {
match value.1.partial_cmp(&max_value.1) {
Some(Ordering::Greater) => Ok(value),
None => {
let first_dtype = DataType::from(value.1);
let second_dtype = DataType::from(max_value.1);
Err(MedRecordError::QueryError(format!(
"Cannot compare values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_string(), .is_int(), .is_float(), .is_bool(), .is_datetime() or .is_duration()"
)))
}
_ => Ok(max_value),
}
})?;
Ok(Some(max_value))
}
#[inline]
pub(crate) fn get_min<'a>(
mut values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>> {
let min_value = values.next();
let Some(min_value) = min_value else {
return Ok(None);
};
let min_value = values.try_fold(min_value, |min_value, value| {
match value.1.partial_cmp(&min_value.1) {
Some(Ordering::Less) => Ok(value),
None => {
let first_dtype = DataType::from(value.1);
let second_dtype = DataType::from(min_value.1);
Err(MedRecordError::QueryError(format!(
"Cannot compare values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_string(), .is_int(), .is_float(), .is_bool(), .is_datetime() or .is_duration()"
)))
}
_ => Ok(min_value),
}
})?;
Ok(Some(min_value))
}
#[inline]
pub(crate) fn get_random<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> Option<(&'a O::Index, MedRecordValue)> {
values.choose(&mut rng())
}
#[inline]
fn evaluate_value_with_index_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
operand: &Wrapper<SingleValueWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let kind = &operand.0.read_or_panic().kind;
let value = match kind {
SingleKindWithIndex::Max => MultipleValuesWithIndexOperation::<O>::get_max(values_1)?,
SingleKindWithIndex::Min => MultipleValuesWithIndexOperation::<O>::get_min(values_1)?,
SingleKindWithIndex::Random => {
MultipleValuesWithIndexOperation::<O>::get_random(values_1)
}
};
Ok(match operand.evaluate_forward(medrecord, value)? {
Some(_) => Box::new(values_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_value_without_index_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
operand: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let values_1 = values_1.map(|(_, value)| value);
let kind = &operand.0.read_or_panic().kind;
let value = match kind {
SingleKindWithoutIndex::Max => {
MultipleValuesWithoutIndexOperation::<O>::get_max(values_1)?
}
SingleKindWithoutIndex::Min => {
MultipleValuesWithoutIndexOperation::<O>::get_min(values_1)?
}
SingleKindWithoutIndex::Mean => {
MultipleValuesWithoutIndexOperation::<O>::get_mean(values_1)?
}
SingleKindWithoutIndex::Median => {
MultipleValuesWithoutIndexOperation::<O>::get_median(values_1)?
}
SingleKindWithoutIndex::Mode => {
MultipleValuesWithoutIndexOperation::<O>::get_mode(values_1)?
}
SingleKindWithoutIndex::Std => {
MultipleValuesWithoutIndexOperation::<O>::get_std(values_1)?
}
SingleKindWithoutIndex::Var => {
MultipleValuesWithoutIndexOperation::<O>::get_var(values_1)?
}
SingleKindWithoutIndex::Count => Some(
MultipleValuesWithoutIndexOperation::<O>::get_count(values_1),
),
SingleKindWithoutIndex::Sum => {
MultipleValuesWithoutIndexOperation::<O>::get_sum(values_1)?
}
SingleKindWithoutIndex::Random => {
MultipleValuesWithoutIndexOperation::<O>::get_random(values_1)
}
};
Ok(match operand.evaluate_forward(medrecord, value)? {
Some(_) => Box::new(values_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_single_value_comparison_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
comparison_operand: &SingleValueComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>> {
let comparison_value =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => Ok(Box::new(
values.filter(move |(_, value)| value > &comparison_value),
)),
SingleComparisonKind::GreaterThanOrEqualTo => Ok(Box::new(
values.filter(move |(_, value)| value >= &comparison_value),
)),
SingleComparisonKind::LessThan => Ok(Box::new(
values.filter(move |(_, value)| value < &comparison_value),
)),
SingleComparisonKind::LessThanOrEqualTo => Ok(Box::new(
values.filter(move |(_, value)| value <= &comparison_value),
)),
SingleComparisonKind::EqualTo => Ok(Box::new(
values.filter(move |(_, value)| value == &comparison_value),
)),
SingleComparisonKind::NotEqualTo => Ok(Box::new(
values.filter(move |(_, value)| value != &comparison_value),
)),
SingleComparisonKind::StartsWith => {
Ok(Box::new(values.filter(move |(_, value)| {
value.starts_with(&comparison_value)
})))
}
SingleComparisonKind::EndsWith => {
Ok(Box::new(values.filter(move |(_, value)| {
value.ends_with(&comparison_value)
})))
}
SingleComparisonKind::Contains => {
Ok(Box::new(values.filter(move |(_, value)| {
value.contains(&comparison_value)
})))
}
}
}
#[inline]
fn evaluate_multiple_values_comparison_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
comparison_operand: &MultipleValuesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>> {
let comparison_values = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => {
Ok(Box::new(values.filter(move |(_, value)| {
comparison_values.contains(value)
})))
}
MultipleComparisonKind::IsNotIn => {
Ok(Box::new(values.filter(move |(_, value)| {
!comparison_values.contains(value)
})))
}
}
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a>(
medrecord: &MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
operand: &SingleValueComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let arithmetic_value =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
let values = values
.map(move |(t, value)| {
match kind {
BinaryArithmeticKind::Add => value.add(arithmetic_value.clone()),
BinaryArithmeticKind::Sub => value.sub(arithmetic_value.clone()),
BinaryArithmeticKind::Mul => {
value.clone().mul(arithmetic_value.clone())
}
BinaryArithmeticKind::Div => {
value.clone().div(arithmetic_value.clone())
}
BinaryArithmeticKind::Pow => {
value.clone().pow(arithmetic_value.clone())
}
BinaryArithmeticKind::Mod => {
value.clone().r#mod(arithmetic_value.clone())
}
}
.map_err(|_| {
MedRecordError::QueryError(format!(
"Failed arithmetic operation {kind}. Consider narrowing down the values using .is_int() or .is_float()",
))
}).map(|result| (t, result))
});
Ok(values.collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
kind: UnaryArithmeticKind,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.map(move |(t, value)| {
let value = match kind {
UnaryArithmeticKind::Round => value.round(),
UnaryArithmeticKind::Ceil => value.ceil(),
UnaryArithmeticKind::Floor => value.floor(),
UnaryArithmeticKind::Abs => value.abs(),
UnaryArithmeticKind::Sqrt => value.sqrt(),
UnaryArithmeticKind::Trim => value.trim(),
UnaryArithmeticKind::TrimStart => value.trim_start(),
UnaryArithmeticKind::TrimEnd => value.trim_end(),
UnaryArithmeticKind::Lowercase => value.lowercase(),
UnaryArithmeticKind::Uppercase => value.uppercase(),
};
(t, value)
})
}
#[inline]
fn evaluate_slice<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
range: Range<usize>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.map(move |(t, value)| (t, value.slice(range.clone())))
}
#[inline]
fn evaluate_is_string<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::String(_)))
}
#[inline]
fn evaluate_is_int<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::Int(_)))
}
#[inline]
fn evaluate_is_float<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::Float(_)))
}
#[inline]
fn evaluate_is_bool<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::Bool(_)))
}
#[inline]
fn evaluate_is_datetime<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::DateTime(_)))
}
#[inline]
fn evaluate_is_duration<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::Duration(_)))
}
#[inline]
fn evaluate_is_null<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordValue)>
where
O: 'a,
{
values.filter(|(_, value)| matches!(value, MedRecordValue::Null))
}
#[inline]
fn evaluate_is_max<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let max_value = Self::get_max(values_1)?;
let Some(max_value) = max_value else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(
values_2.filter(move |(_, value)| *value == max_value.1),
))
}
#[inline]
fn evaluate_is_min<'a>(
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let min_value = Self::get_min(values_1)?;
let Some(min_value) = min_value else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(
values_2.filter(move |(_, value)| *value == min_value.1),
))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
either: &Wrapper<MultipleValuesWithIndexOperand<O>>,
or: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let either_values = either.evaluate_forward(medrecord, Box::new(values_1))?;
let or_values = or.evaluate_forward(medrecord, Box::new(values_2))?;
Ok(Box::new(
either_values
.chain(or_values)
.unique_by(|value| value.0.clone()),
))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = (&'a O::Index, MedRecordValue)> + 'a,
operand: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(values_1))?
.map(|(t, _)| t)
.collect();
Ok(Box::new(values_2.filter(move |(t, _)| !result.contains(t))))
}
}
impl<O: RootOperand> MultipleValuesWithIndexOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
values: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
Ok(match self {
Self::ValueWithIndexOperation { operand } => Box::new(
Self::evaluate_value_with_index_operation_grouped(medrecord, values, operand)?,
),
Self::ValueWithoutIndexOperation { operand } => Box::new(
Self::evaluate_value_without_index_operation_grouped(medrecord, values, operand)?,
),
Self::SingleValueComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, values)| {
Ok((
key,
Box::new(Self::evaluate_single_value_comparison_operation(
medrecord, values, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleValuesComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, values)| {
Ok((
key,
Box::new(Self::evaluate_multiple_values_comparison_operation(
medrecord, values, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
values
.map(move |(key, values)| {
Ok((
key,
Box::new(Self::evaluate_binary_arithmetic_operation(
medrecord, values, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_unary_arithmetic_operation(
values,
kind.clone(),
)) as BoxedIterator<_>,
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_slice(values, range.clone())) as BoxedIterator<_>,
)
}))
}
Self::IsString => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_string(values)) as BoxedIterator<_>,
)
})),
Self::IsInt => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_int(values)) as BoxedIterator<_>,
)
})),
Self::IsFloat => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_float(values)) as BoxedIterator<_>,
)
})),
Self::IsBool => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_bool(values)) as BoxedIterator<_>,
)
})),
Self::IsDateTime => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_datetime(values)) as BoxedIterator<_>,
)
})),
Self::IsDuration => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_duration(values)) as BoxedIterator<_>,
)
})),
Self::IsNull => Box::new(values.map(move |(key, values)| {
(
key,
Box::new(Self::evaluate_is_null(values)) as BoxedIterator<_>,
)
})),
Self::IsMax => Box::new(
values
.map(move |(key, values)| {
Ok((
key,
Box::new(Self::evaluate_is_max(values)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::IsMin => Box::new(
values
.map(move |(key, values)| {
Ok((
key,
Box::new(Self::evaluate_is_min(values)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, values, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, values, operand)?
}
Self::Merge { operand } => {
let (values_1, values_2) = tee_grouped_iterator(values);
let values_1 = values_1.flat_map(|(_, value)| value);
let values_1: Vec<_> = operand
.evaluate_forward(medrecord, Box::new(values_1))?
.collect();
Box::new(values_2.map(move |(key, values)| {
let values: Vec<_> = values.filter(|value| values_1.contains(value)).collect();
let values: BoxedIterator<_> = Box::new(values.into_iter());
(key, values)
}))
}
})
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_value_with_index_operation_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>,
operand: &Wrapper<SingleValueWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = tee_grouped_iterator(values);
let mut values_2 = values_2.collect::<Vec<_>>();
let kind = &operand.0.read_or_panic().kind;
let values_1: Vec<_> = values_1
.map(|(key, values)| {
let value = match kind {
SingleKindWithIndex::Max => {
MultipleValuesWithIndexOperation::<O>::get_max(values)?
}
SingleKindWithIndex::Min => {
MultipleValuesWithIndexOperation::<O>::get_min(values)?
}
SingleKindWithIndex::Random => {
MultipleValuesWithIndexOperation::<O>::get_random(values)
}
};
Ok((key, value))
})
.collect::<MedRecordResult<_>>()?;
let values_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(values_1.into_iter()))?;
Ok(Box::new(values_1.map(move |(key, value)| match value {
Some(_) => {
let values_position = values_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
values_2.remove(values_position)
}
None => (key, Box::new(std::iter::empty()) as BoxedIterator<_>),
})))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_value_without_index_operation_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>,
operand: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = tee_grouped_iterator(values);
let mut values_2: Vec<_> = values_2.collect();
let kind = &operand.0.read_or_panic().kind;
let values_1: Vec<_> = values_1
.map(|(key, values)| {
let values = values.map(|(_, value)| value);
let value = match kind {
SingleKindWithoutIndex::Max => {
MultipleValuesWithoutIndexOperation::<O>::get_max(values)?
}
SingleKindWithoutIndex::Min => {
MultipleValuesWithoutIndexOperation::<O>::get_min(values)?
}
SingleKindWithoutIndex::Mean => {
MultipleValuesWithoutIndexOperation::<O>::get_mean(values)?
}
SingleKindWithoutIndex::Median => {
MultipleValuesWithoutIndexOperation::<O>::get_median(values)?
}
SingleKindWithoutIndex::Mode => {
MultipleValuesWithoutIndexOperation::<O>::get_mode(values)?
}
SingleKindWithoutIndex::Std => {
MultipleValuesWithoutIndexOperation::<O>::get_std(values)?
}
SingleKindWithoutIndex::Var => {
MultipleValuesWithoutIndexOperation::<O>::get_var(values)?
}
SingleKindWithoutIndex::Count => {
Some(MultipleValuesWithoutIndexOperation::<O>::get_count(values))
}
SingleKindWithoutIndex::Sum => {
MultipleValuesWithoutIndexOperation::<O>::get_sum(values)?
}
SingleKindWithoutIndex::Random => {
MultipleValuesWithoutIndexOperation::<O>::get_random(values)
}
};
Ok((key, value))
})
.collect::<MedRecordResult<_>>()?;
let values_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(values_1.into_iter()))?;
Ok(Box::new(values_1.map(move |(key, value)| match value {
Some(_) => {
let values_position = values_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
values_2.remove(values_position)
}
None => (key, Box::new(std::iter::empty()) as BoxedIterator<_>),
})))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>,
either: &Wrapper<MultipleValuesWithIndexOperand<O>>,
or: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = tee_grouped_iterator(values);
let either_values = either.evaluate_forward_grouped(medrecord, values_1)?;
let mut or_values: Vec<_> = or.evaluate_forward_grouped(medrecord, values_2)?.collect();
let values = either_values.map(move |(key, either_values)| {
let values_position = or_values
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_values = or_values.remove(values_position).1;
let values: BoxedIterator<_> = Box::new(
either_values
.chain(or_values)
.unique_by(|value| value.0.clone()),
);
(key, values)
});
Ok(Box::new(values))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>,
operand: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = tee_grouped_iterator(values);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, values_1)?
.collect();
let values = values_2.map(move |(key, values)| {
let values_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_values: Vec<_> = result.remove(values_position).1.collect();
let values: BoxedIterator<_> =
Box::new(values.filter(move |value| !excluded_values.contains(value)));
(key, values)
});
Ok(Box::new(values))
}
}
#[derive(Debug, Clone)]
pub enum MultipleValuesWithoutIndexOperation<O: RootOperand> {
ValueOperation {
operand: Wrapper<SingleValueWithoutIndexOperand<O>>,
},
SingleValueComparisonOperation {
operand: SingleValueComparisonOperand,
kind: SingleComparisonKind,
},
MultipleValuesComparisonOperation {
operand: MultipleValuesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleValueComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsFloat,
IsBool,
IsDateTime,
IsDuration,
IsNull,
IsMax,
IsMin,
EitherOr {
either: Wrapper<MultipleValuesWithoutIndexOperand<O>>,
or: Wrapper<MultipleValuesWithoutIndexOperand<O>>,
},
Exclude {
operand: Wrapper<MultipleValuesWithoutIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for MultipleValuesWithoutIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::ValueOperation { operand } => Self::ValueOperation {
operand: operand.deep_clone(),
},
Self::SingleValueComparisonOperation { operand, kind } => {
Self::SingleValueComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::MultipleValuesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::UnaryArithmeticOperation { kind } => {
Self::UnaryArithmeticOperation { kind: kind.clone() }
}
Self::Slice(range) => Self::Slice(range.clone()),
Self::IsString => Self::IsString,
Self::IsInt => Self::IsInt,
Self::IsFloat => Self::IsFloat,
Self::IsBool => Self::IsBool,
Self::IsDateTime => Self::IsDateTime,
Self::IsDuration => Self::IsDuration,
Self::IsNull => Self::IsNull,
Self::IsMax => Self::IsMax,
Self::IsMin => Self::IsMin,
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
}
}
}
impl<O: RootOperand> MultipleValuesWithoutIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>>
where
O: 'a,
{
Ok(match self {
Self::ValueOperation { operand } => {
Self::evaluate_value_operation(medrecord, values, operand)?
}
Self::SingleValueComparisonOperation { operand, kind } => {
Self::evaluate_single_value_comparison_operation(medrecord, values, operand, kind)?
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_values_comparison_operation(
medrecord, values, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
Self::evaluate_binary_arithmetic_operation(medrecord, values, operand, kind)?,
),
Self::UnaryArithmeticOperation { kind } => Box::new(
Self::evaluate_unary_arithmetic_operation(values, kind.clone()),
),
Self::Slice(range) => Box::new(Self::evaluate_slice(values, range.clone())),
Self::IsString => Box::new(Self::evaluate_is_string(values)),
Self::IsInt => Box::new(Self::evaluate_is_int(values)),
Self::IsFloat => Box::new(Self::evaluate_is_float(values)),
Self::IsBool => Box::new(Self::evaluate_is_bool(values)),
Self::IsDateTime => Box::new(Self::evaluate_is_datetime(values)),
Self::IsDuration => Box::new(Self::evaluate_is_duration(values)),
Self::IsNull => Box::new(Self::evaluate_is_null(values)),
Self::IsMax => Self::evaluate_is_max(values)?,
Self::IsMin => Self::evaluate_is_min(values)?,
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, values, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, values, operand)?,
})
}
#[inline]
pub(crate) fn get_max(
mut values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let max_value = values.next();
let Some(max_value) = max_value else {
return Ok(None);
};
let max_value = values.try_fold(max_value, |max_value, value| {
match value.partial_cmp(&max_value) {
Some(Ordering::Greater) => Ok(value),
None => {
let first_dtype = DataType::from(value);
let second_dtype = DataType::from(max_value);
Err(MedRecordError::QueryError(format!(
"Cannot compare values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_string(), .is_int(), .is_float(), .is_bool(), .is_datetime() or .is_duration()"
)))
}
_ => Ok(max_value),
}
})?;
Ok(Some(max_value))
}
#[inline]
pub(crate) fn get_min(
mut values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let min_value = values.next();
let Some(min_value) = min_value else {
return Ok(None);
};
let min_value = values.try_fold(min_value, |min_value, value| {
match value.partial_cmp(&min_value) {
Some(Ordering::Less) => Ok(value),
None => {
let first_dtype = DataType::from(value);
let second_dtype = DataType::from(min_value);
Err(MedRecordError::QueryError(format!(
"Cannot compare values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_string(), .is_int(), .is_float(), .is_bool(), .is_datetime() or .is_duration()"
)))
}
_ => Ok(min_value),
}
})?;
Ok(Some(min_value))
}
#[inline]
pub(crate) fn get_mean(
mut values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let first_value = values.next();
let Some(first_value) = first_value else {
return Ok(None);
};
let (sum, count) = values.try_fold((first_value, 1), |(sum, count), value| {
let first_dtype = DataType::from(&sum);
let second_dtype = DataType::from(&value);
match sum.add(value) {
Ok(sum) => Ok((sum, count + 1)),
Err(_) => Err(MedRecordError::QueryError(format!(
"Cannot add values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_int(), .is_float(), .is_datetime() or .is_duration()"
))),
}
})?;
Ok(Some(sum.div(MedRecordValue::Int(count as i64))?))
}
#[inline]
pub(crate) fn get_median(
mut values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let first_value = values.next();
let Some(first_value) = first_value else {
return Ok(None);
};
let first_data_type = DataType::from(&first_value);
let median = match first_value {
MedRecordValue::Int(value) => {
let mut values: Vec<_> = values.map(|value| {
let data_type = DataType::from(&value);
match value {
MedRecordValue::Int(value) => Ok(value as f64),
MedRecordValue::Float(value) => Ok(value),
_ => Err(MedRecordError::QueryError(format!(
"Cannot calculate median of mixed data types {first_data_type} and {data_type}. Consider narrowing down the values using .is_int(), .is_float() , .is_datetime() or .is_duration()"
))),
}
}).collect::<MedRecordResult<_>>()?;
values.push(value as f64);
values.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
get_median!(values, Float)
}
MedRecordValue::Float(value) => {
let mut values: Vec<_> = values.map(|value| {
let data_type = DataType::from(&value);
match value {
MedRecordValue::Int(value) => Ok(value as f64),
MedRecordValue::Float(value) => Ok(value),
_ => Err(MedRecordError::QueryError(format!(
"Cannot calculate median of mixed data types {first_data_type} and {data_type}. Consider narrowing down the values using .is_int(), .is_float(), .is_datetime() or .is_duration()"
))),
}
}).collect::<MedRecordResult<_>>()?;
values.push(value);
values.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap());
get_median!(values, Float)
}
MedRecordValue::DateTime(value) => {
let mut values: Vec<_> = values.map(|value| {
let data_type = DataType::from(&value);
match value {
MedRecordValue::DateTime(naive_date_time) => Ok(naive_date_time),
_ => Err(MedRecordError::QueryError(format!(
"Cannot calculate median of mixed data types {first_data_type} and {data_type}. Consider narrowing down the values using .is_int(), .is_float(), .is_datetime() or .is_duration()"
))),
}
}).collect::<MedRecordResult<_>>()?;
values.push(value);
values.sort();
get_median!(values, DateTime)
}
MedRecordValue::Duration(value) => {
let mut values: Vec<_> = values.map(|value| {
let data_type = DataType::from(&value);
match value {
MedRecordValue::Duration(naive_date_time) => Ok(naive_date_time),
_ => Err(MedRecordError::QueryError(format!(
"Cannot calculate median of mixed data types {first_data_type} and {data_type}. Consider narrowing down the values using .is_int(), .is_float(), .is_datetime() or .is_duration()"
))),
}
}).collect::<MedRecordResult<_>>()?;
values.push(value);
values.sort();
get_median!(values, Duration)
}
_ => Err(MedRecordError::QueryError(format!(
"Cannot calculate median of data type {first_data_type}"
)))?,
}?;
Ok(Some(median))
}
#[inline]
pub(crate) fn get_mode(
values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let values: Vec<_> = values.collect();
let most_common_value = values.first();
let Some(most_common_value) = most_common_value else {
return Ok(None);
};
let most_common_value = most_common_value.clone();
let most_common_count = values
.iter()
.filter(|value| **value == most_common_value)
.count();
let (_, most_common_value) = values.clone().into_iter().fold(
(most_common_count, most_common_value),
|acc, value| {
let count = values.iter().filter(|v| **v == value).count();
if count > acc.0 {
(count, value)
} else {
acc
}
},
);
Ok(Some(most_common_value.clone()))
}
#[inline]
pub(crate) fn get_std(
values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let variance = Self::get_var(values)?;
let Some(variance) = variance else {
return Ok(None);
};
let MedRecordValue::Float(variance) = variance else {
unreachable!()
};
Ok(Some(MedRecordValue::Float(variance.sqrt())))
}
#[inline]
pub(crate) fn get_var(
values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let (values_1, values_2) = Itertools::tee(values);
let mean = Self::get_mean(values_1)?;
let Some(mean) = mean else {
return Ok(None);
};
let MedRecordValue::Float(mean) = mean else {
let data_type = DataType::from(mean);
return Err(MedRecordError::QueryError(
format!("Cannot calculate variance of data type {data_type}. Consider narrowing down the values using .is_int() or .is_float()"),
));
};
let values = values_2
.into_iter()
.map(|value| {
let data_type = DataType::from(&value);
match value {
MedRecordValue::Int(value) => Ok(value as f64),
MedRecordValue::Float(value) => Ok(value),
_ => Err(MedRecordError::QueryError(
format!("Cannot calculate variance of data type {data_type}. Consider narrowing down the values using .is_int() or .is_float()"),
)),
}})
.collect::<MedRecordResult<Vec<_>>>()?;
let values_length = values.len();
let variance = values
.into_iter()
.map(|value| (value - mean).powi(2))
.sum::<f64>()
/ values_length as f64;
Ok(Some(MedRecordValue::Float(variance)))
}
#[inline]
pub(crate) fn get_count(values: impl Iterator<Item = MedRecordValue>) -> MedRecordValue {
MedRecordValue::Int(values.count() as i64)
}
#[inline]
pub(crate) fn get_sum(
mut values: impl Iterator<Item = MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let first_value = values.next();
let Some(first_value) = first_value else {
return Ok(None);
};
let sum = values.try_fold(first_value, |sum, value| {
let first_dtype = DataType::from(&sum);
let second_dtype = DataType::from(&value);
sum.add(value).map_err(|_| {
MedRecordError::QueryError(format!(
"Cannot add values of data types {first_dtype} and {second_dtype}. Consider narrowing down the values using .is_string(), .is_int(), .is_float(), .is_bool(), .is_datetime() or .is_duration()"
))
})
})?;
Ok(Some(sum))
}
#[inline]
pub(crate) fn get_random(
values: impl Iterator<Item = MedRecordValue>,
) -> Option<MedRecordValue> {
values.choose(&mut rng())
}
#[inline]
fn evaluate_value_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
operand: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>> {
let (values_1, values_2) = Itertools::tee(values);
let kind = &operand.0.read_or_panic().kind;
let value = match kind {
SingleKindWithoutIndex::Max => {
MultipleValuesWithoutIndexOperation::<O>::get_max(values_1)?
}
SingleKindWithoutIndex::Min => {
MultipleValuesWithoutIndexOperation::<O>::get_min(values_1)?
}
SingleKindWithoutIndex::Mean => {
MultipleValuesWithoutIndexOperation::<O>::get_mean(values_1)?
}
SingleKindWithoutIndex::Median => {
MultipleValuesWithoutIndexOperation::<O>::get_median(values_1)?
}
SingleKindWithoutIndex::Mode => {
MultipleValuesWithoutIndexOperation::<O>::get_mode(values_1)?
}
SingleKindWithoutIndex::Std => {
MultipleValuesWithoutIndexOperation::<O>::get_std(values_1)?
}
SingleKindWithoutIndex::Var => {
MultipleValuesWithoutIndexOperation::<O>::get_var(values_1)?
}
SingleKindWithoutIndex::Count => Some(
MultipleValuesWithoutIndexOperation::<O>::get_count(values_1),
),
SingleKindWithoutIndex::Sum => {
MultipleValuesWithoutIndexOperation::<O>::get_sum(values_1)?
}
SingleKindWithoutIndex::Random => {
MultipleValuesWithoutIndexOperation::<O>::get_random(values_1)
}
};
Ok(match operand.evaluate_forward(medrecord, value)? {
Some(_) => Box::new(values_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_single_value_comparison_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
comparison_operand: &SingleValueComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>> {
let comparison_value =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => Ok(Box::new(
values.filter(move |value| value > &comparison_value),
)),
SingleComparisonKind::GreaterThanOrEqualTo => Ok(Box::new(
values.filter(move |value| value >= &comparison_value),
)),
SingleComparisonKind::LessThan => Ok(Box::new(
values.filter(move |value| value < &comparison_value),
)),
SingleComparisonKind::LessThanOrEqualTo => Ok(Box::new(
values.filter(move |value| value <= &comparison_value),
)),
SingleComparisonKind::EqualTo => Ok(Box::new(
values.filter(move |value| value == &comparison_value),
)),
SingleComparisonKind::NotEqualTo => Ok(Box::new(
values.filter(move |value| value != &comparison_value),
)),
SingleComparisonKind::StartsWith => Ok(Box::new(
values.filter(move |value| value.starts_with(&comparison_value)),
)),
SingleComparisonKind::EndsWith => Ok(Box::new(
values.filter(move |value| value.ends_with(&comparison_value)),
)),
SingleComparisonKind::Contains => Ok(Box::new(
values.filter(move |value| value.contains(&comparison_value)),
)),
}
}
#[inline]
fn evaluate_multiple_values_comparison_operation<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
comparison_operand: &MultipleValuesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>> {
let comparison_values = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => Ok(Box::new(
values.filter(move |value| comparison_values.contains(value)),
)),
MultipleComparisonKind::IsNotIn => Ok(Box::new(
values.filter(move |value| !comparison_values.contains(value)),
)),
}
}
#[inline]
fn evaluate_binary_arithmetic_operation(
medrecord: &MedRecord,
values: impl Iterator<Item = MedRecordValue>,
operand: &SingleValueComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<impl Iterator<Item = MedRecordValue>> {
let arithmetic_value =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
let values = values
.map(move |value| {
match kind {
BinaryArithmeticKind::Add => value.add(arithmetic_value.clone()),
BinaryArithmeticKind::Sub => value.sub(arithmetic_value.clone()),
BinaryArithmeticKind::Mul => {
value.clone().mul(arithmetic_value.clone())
}
BinaryArithmeticKind::Div => {
value.clone().div(arithmetic_value.clone())
}
BinaryArithmeticKind::Pow => {
value.clone().pow(arithmetic_value.clone())
}
BinaryArithmeticKind::Mod => {
value.clone().r#mod(arithmetic_value.clone())
}
}
.map_err(|_| {
MedRecordError::QueryError(format!(
"Failed arithmetic operation {kind}. Consider narrowing down the values using .is_int() or .is_float()",
))
})
});
Ok(values.collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
values: impl Iterator<Item = MedRecordValue>,
kind: UnaryArithmeticKind,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.map(move |value| match kind {
UnaryArithmeticKind::Round => value.round(),
UnaryArithmeticKind::Ceil => value.ceil(),
UnaryArithmeticKind::Floor => value.floor(),
UnaryArithmeticKind::Abs => value.abs(),
UnaryArithmeticKind::Sqrt => value.sqrt(),
UnaryArithmeticKind::Trim => value.trim(),
UnaryArithmeticKind::TrimStart => value.trim_start(),
UnaryArithmeticKind::TrimEnd => value.trim_end(),
UnaryArithmeticKind::Lowercase => value.lowercase(),
UnaryArithmeticKind::Uppercase => value.uppercase(),
})
}
#[inline]
fn evaluate_slice<'a>(
values: impl Iterator<Item = MedRecordValue>,
range: Range<usize>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.map(move |value| value.slice(range.clone()))
}
#[inline]
fn evaluate_is_string<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::String(_)))
}
#[inline]
fn evaluate_is_int<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::Int(_)))
}
#[inline]
fn evaluate_is_float<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::Float(_)))
}
#[inline]
fn evaluate_is_bool<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::Bool(_)))
}
#[inline]
fn evaluate_is_datetime<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::DateTime(_)))
}
#[inline]
fn evaluate_is_duration<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::Duration(_)))
}
#[inline]
fn evaluate_is_null<'a>(
values: impl Iterator<Item = MedRecordValue>,
) -> impl Iterator<Item = MedRecordValue>
where
O: 'a,
{
values.filter(|value| matches!(value, MedRecordValue::Null))
}
#[inline]
fn evaluate_is_max<'a>(
values: impl Iterator<Item = MedRecordValue> + 'a,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let max_value = Self::get_max(values_1)?;
let Some(max_value) = max_value else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(values_2.filter(move |value| *value == max_value)))
}
#[inline]
fn evaluate_is_min<'a>(
values: impl Iterator<Item = MedRecordValue> + 'a,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let min_value = Self::get_min(values_1)?;
let Some(min_value) = min_value else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(values_2.filter(move |value| *value == min_value)))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
either: &Wrapper<MultipleValuesWithoutIndexOperand<O>>,
or: &Wrapper<MultipleValuesWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let either_values = either.evaluate_forward(medrecord, Box::new(values_1))?;
let or_values = or.evaluate_forward(medrecord, Box::new(values_2))?;
Ok(Box::new(either_values.chain(or_values)))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
values: impl Iterator<Item = MedRecordValue> + 'a,
operand: &Wrapper<MultipleValuesWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordValue>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let result: Vec<_> = operand
.evaluate_forward(medrecord, Box::new(values_1))?
.collect();
Ok(Box::new(
values_2.filter(move |value| !result.contains(value)),
))
}
}
#[derive(Debug, Clone)]
pub enum SingleValueWithIndexOperation<O: RootOperand> {
SingleValueComparisonOperation {
operand: SingleValueComparisonOperand,
kind: SingleComparisonKind,
},
MultipleValuesComparisonOperation {
operand: MultipleValuesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleValueComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsFloat,
IsBool,
IsDateTime,
IsDuration,
IsNull,
EitherOr {
either: Wrapper<SingleValueWithIndexOperand<O>>,
or: Wrapper<SingleValueWithIndexOperand<O>>,
},
Exclude {
operand: Wrapper<SingleValueWithIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleValuesWithIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for SingleValueWithIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::SingleValueComparisonOperation { operand, kind } => {
Self::SingleValueComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::MultipleValuesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::UnaryArithmeticOperation { kind } => {
Self::UnaryArithmeticOperation { kind: kind.clone() }
}
Self::Slice(range) => Self::Slice(range.clone()),
Self::IsString => Self::IsString,
Self::IsInt => Self::IsInt,
Self::IsFloat => Self::IsFloat,
Self::IsBool => Self::IsBool,
Self::IsDateTime => Self::IsDateTime,
Self::IsDuration => Self::IsDuration,
Self::IsNull => Self::IsNull,
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::Merge { operand } => Self::Merge {
operand: operand.deep_clone(),
},
}
}
}
impl<O: RootOperand> SingleValueWithIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
value: Option<(&'a O::Index, MedRecordValue)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let Some(value) = value else {
return Ok(None);
};
Ok(match self {
Self::SingleValueComparisonOperation { operand, kind } => {
Self::evaluate_single_value_comparison_operation(medrecord, value, operand, kind)?
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_values_comparison_operation(
medrecord, value, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, value, operand, kind)?
}
Self::UnaryArithmeticOperation { kind } => {
Some(Self::evaluate_unary_arithmetic_operation(value, kind))
}
Self::Slice(range) => Some(Self::evaluate_slice(value, range)),
Self::IsString => Self::evaluate_is_string(value),
Self::IsInt => Self::evaluate_is_int(value),
Self::IsFloat => Self::evaluate_is_float(value),
Self::IsBool => Self::evaluate_is_bool(value),
Self::IsDateTime => Self::evaluate_is_datetime(value),
Self::IsDuration => Self::evaluate_is_duration(value),
Self::IsNull => Self::evaluate_is_null(value),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, value, either, or)?
}
Self::Exclude { operand } => {
match operand.evaluate_forward(medrecord, Some(value.clone()))? {
Some(_) => None,
None => Some(value),
}
}
Self::Merge { operand: _ } => {
unreachable!()
}
})
}
#[inline]
fn evaluate_single_value_comparison_operation<'a>(
medrecord: &MedRecord,
value: (&'a O::Index, MedRecordValue),
comparison_operand: &SingleValueComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>> {
let comparison_value =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
let comparison_result = match kind {
SingleComparisonKind::GreaterThan => value.1 > comparison_value,
SingleComparisonKind::GreaterThanOrEqualTo => value.1 >= comparison_value,
SingleComparisonKind::LessThan => value.1 < comparison_value,
SingleComparisonKind::LessThanOrEqualTo => value.1 <= comparison_value,
SingleComparisonKind::EqualTo => value.1 == comparison_value,
SingleComparisonKind::NotEqualTo => value.1 != comparison_value,
SingleComparisonKind::StartsWith => value.1.starts_with(&comparison_value),
SingleComparisonKind::EndsWith => value.1.ends_with(&comparison_value),
SingleComparisonKind::Contains => value.1.contains(&comparison_value),
};
Ok(if comparison_result { Some(value) } else { None })
}
#[inline]
fn evaluate_multiple_values_comparison_operation<'a>(
medrecord: &MedRecord,
value: (&'a O::Index, MedRecordValue),
comparison_operand: &MultipleValuesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>> {
let comparison_values = comparison_operand.evaluate_backward(medrecord)?;
let comparison_result = match kind {
MultipleComparisonKind::IsIn => comparison_values.contains(&value.1),
MultipleComparisonKind::IsNotIn => !comparison_values.contains(&value.1),
};
Ok(if comparison_result { Some(value) } else { None })
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a>(
medrecord: &MedRecord,
value: (&'a O::Index, MedRecordValue),
operand: &SingleValueComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>> {
let arithmetic_value =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
Ok(Some(match kind {
BinaryArithmeticKind::Add => (value.0, value.1.add(arithmetic_value)?),
BinaryArithmeticKind::Sub => (value.0, value.1.sub(arithmetic_value)?),
BinaryArithmeticKind::Mul => (value.0, value.1.mul(arithmetic_value)?),
BinaryArithmeticKind::Div => (value.0, value.1.div(arithmetic_value)?),
BinaryArithmeticKind::Pow => (value.0, value.1.pow(arithmetic_value)?),
BinaryArithmeticKind::Mod => (value.0, value.1.r#mod(arithmetic_value)?),
}))
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
value: (&'a O::Index, MedRecordValue),
kind: &UnaryArithmeticKind,
) -> (&'a O::Index, MedRecordValue) {
match kind {
UnaryArithmeticKind::Round => (value.0, value.1.round()),
UnaryArithmeticKind::Ceil => (value.0, value.1.ceil()),
UnaryArithmeticKind::Floor => (value.0, value.1.floor()),
UnaryArithmeticKind::Abs => (value.0, value.1.abs()),
UnaryArithmeticKind::Sqrt => (value.0, value.1.sqrt()),
UnaryArithmeticKind::Trim => (value.0, value.1.trim()),
UnaryArithmeticKind::TrimStart => (value.0, value.1.trim_start()),
UnaryArithmeticKind::TrimEnd => (value.0, value.1.trim_end()),
UnaryArithmeticKind::Lowercase => (value.0, value.1.lowercase()),
UnaryArithmeticKind::Uppercase => (value.0, value.1.uppercase()),
}
}
#[inline]
fn evaluate_slice<'a>(
value: (&'a O::Index, MedRecordValue),
range: &Range<usize>,
) -> (&'a O::Index, MedRecordValue) {
(value.0, value.1.slice(range.clone()))
}
#[inline]
fn evaluate_is_string(
value: (&O::Index, MedRecordValue),
) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::String(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_int(value: (&O::Index, MedRecordValue)) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::Int(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_float(
value: (&O::Index, MedRecordValue),
) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::Float(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_bool(value: (&O::Index, MedRecordValue)) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::Bool(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_datetime(
value: (&O::Index, MedRecordValue),
) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::DateTime(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_duration(
value: (&O::Index, MedRecordValue),
) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::Duration(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_null(value: (&O::Index, MedRecordValue)) -> Option<(&O::Index, MedRecordValue)> {
match value.1 {
MedRecordValue::Null => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
value: (&'a O::Index, MedRecordValue),
either: &Wrapper<SingleValueWithIndexOperand<O>>,
or: &Wrapper<SingleValueWithIndexOperand<O>>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
let either_result = either.evaluate_forward(medrecord, Some(value.clone()))?;
let or_result = or.evaluate_forward(medrecord, Some(value))?;
match (either_result, or_result) {
(Some(either_result), _) => Ok(Some(either_result)),
(None, Some(or_result)) => Ok(Some(or_result)),
_ => Ok(None),
}
}
}
impl<O: RootOperand> SingleValueWithIndexOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
Ok(match self {
Self::SingleValueComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_single_value_comparison_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleValuesComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_multiple_values_comparison_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_binary_arithmetic_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(
key,
Some(Self::evaluate_unary_arithmetic_operation(value, &kind)),
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Some(Self::evaluate_slice(value, &range)))
}))
}
Self::IsString => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_string(value))
})),
Self::IsInt => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_int(value))
})),
Self::IsFloat => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_float(value))
})),
Self::IsBool => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_bool(value))
})),
Self::IsDateTime => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_datetime(value))
})),
Self::IsDuration => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_duration(value))
})),
Self::IsNull => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_null(value))
})),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, values, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, values, operand)?
}
Self::Merge { operand } => {
let (values_1, values_2) = Itertools::tee(values);
let values_1 = values_1.filter_map(|(_, value)| value);
let values_1: Vec<_> = operand
.evaluate_forward(medrecord, Box::new(values_1))?
.collect();
Box::new(values_2.map(move |(key, value)| {
let value = value.filter(|value| values_1.contains(value));
(key, value)
}))
}
})
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>,
either: &Wrapper<SingleValueWithIndexOperand<O>>,
or: &Wrapper<SingleValueWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let either_values = either.evaluate_forward_grouped(medrecord, Box::new(values_1))?;
let mut or_values: Vec<_> = or
.evaluate_forward_grouped(medrecord, Box::new(values_2))?
.collect();
let values = either_values.map(move |(key, either_value)| {
let value_position = or_values
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_value = or_values.remove(value_position).1;
let value = match (either_value, or_value) {
(Some(either_result), _) => Some(either_result),
(None, Some(or_result)) => Some(or_result),
_ => None,
};
(key, value)
});
Ok(Box::new(values))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>,
operand: &Wrapper<SingleValueWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordValue)>>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(values_1))?
.collect();
let values = values_2.map(move |(key, value)| {
let value_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_value = result.remove(value_position).1;
let value = match excluded_value {
Some(_) => None,
None => value,
};
(key, value)
});
Ok(Box::new(values))
}
}
#[derive(Debug, Clone)]
pub enum SingleValueWithoutIndexOperation<O: RootOperand> {
SingleValueComparisonOperation {
operand: SingleValueComparisonOperand,
kind: SingleComparisonKind,
},
MultipleValuesComparisonOperation {
operand: MultipleValuesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleValueComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsFloat,
IsBool,
IsDateTime,
IsDuration,
IsNull,
EitherOr {
either: Wrapper<SingleValueWithoutIndexOperand<O>>,
or: Wrapper<SingleValueWithoutIndexOperand<O>>,
},
Exclude {
operand: Wrapper<SingleValueWithoutIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleValuesWithoutIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for SingleValueWithoutIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::SingleValueComparisonOperation { operand, kind } => {
Self::SingleValueComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::MultipleValuesComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::BinaryArithmeticOperation { operand, kind } => Self::BinaryArithmeticOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
},
Self::UnaryArithmeticOperation { kind } => {
Self::UnaryArithmeticOperation { kind: kind.clone() }
}
Self::Slice(range) => Self::Slice(range.clone()),
Self::IsString => Self::IsString,
Self::IsInt => Self::IsInt,
Self::IsFloat => Self::IsFloat,
Self::IsBool => Self::IsBool,
Self::IsDateTime => Self::IsDateTime,
Self::IsDuration => Self::IsDuration,
Self::IsNull => Self::IsNull,
Self::EitherOr { either, or } => Self::EitherOr {
either: either.deep_clone(),
or: or.deep_clone(),
},
Self::Exclude { operand } => Self::Exclude {
operand: operand.deep_clone(),
},
Self::Merge { operand } => Self::Merge {
operand: operand.deep_clone(),
},
}
}
}
impl<O: RootOperand> SingleValueWithoutIndexOperation<O> {
pub(crate) fn evaluate(
&self,
medrecord: &MedRecord,
value: Option<MedRecordValue>,
) -> MedRecordResult<Option<MedRecordValue>> {
let Some(value) = value else {
return Ok(None);
};
match self {
Self::SingleValueComparisonOperation { operand, kind } => {
Self::evaluate_single_value_comparison_operation(medrecord, value, operand, kind)
}
Self::MultipleValuesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_values_comparison_operation(medrecord, value, operand, kind)
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, value, operand, kind)
}
Self::UnaryArithmeticOperation { kind } => {
Ok(Some(Self::evaluate_unary_arithmetic_operation(value, kind)))
}
Self::Slice(range) => Ok(Some(Self::evaluate_slice(value, range))),
Self::IsString => Ok(Self::evaluate_is_string(value)),
Self::IsInt => Ok(Self::evaluate_is_int(value)),
Self::IsFloat => Ok(Self::evaluate_is_float(value)),
Self::IsBool => Ok(Self::evaluate_is_bool(value)),
Self::IsDateTime => Ok(Self::evaluate_is_datetime(value)),
Self::IsDuration => Ok(Self::evaluate_is_duration(value)),
Self::IsNull => Ok(Self::evaluate_is_null(value)),
Self::EitherOr { either, or } => Self::evaluate_either_or(medrecord, value, either, or),
Self::Exclude { operand } => Ok(
match operand.evaluate_forward(medrecord, Some(value.clone()))? {
Some(_) => None,
None => Some(value),
},
),
Self::Merge { operand: _ } => {
unreachable!()
}
}
}
#[inline]
fn evaluate_single_value_comparison_operation(
medrecord: &MedRecord,
value: MedRecordValue,
comparison_operand: &SingleValueComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<Option<MedRecordValue>> {
let comparison_value =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
let comparison_result = match kind {
SingleComparisonKind::GreaterThan => value > comparison_value,
SingleComparisonKind::GreaterThanOrEqualTo => value >= comparison_value,
SingleComparisonKind::LessThan => value < comparison_value,
SingleComparisonKind::LessThanOrEqualTo => value <= comparison_value,
SingleComparisonKind::EqualTo => value == comparison_value,
SingleComparisonKind::NotEqualTo => value != comparison_value,
SingleComparisonKind::StartsWith => value.starts_with(&comparison_value),
SingleComparisonKind::EndsWith => value.ends_with(&comparison_value),
SingleComparisonKind::Contains => value.contains(&comparison_value),
};
Ok(if comparison_result { Some(value) } else { None })
}
#[inline]
fn evaluate_multiple_values_comparison_operation(
medrecord: &MedRecord,
value: MedRecordValue,
comparison_operand: &MultipleValuesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<Option<MedRecordValue>> {
let comparison_values = comparison_operand.evaluate_backward(medrecord)?;
let comparison_result = match kind {
MultipleComparisonKind::IsIn => comparison_values.contains(&value),
MultipleComparisonKind::IsNotIn => !comparison_values.contains(&value),
};
Ok(if comparison_result { Some(value) } else { None })
}
#[inline]
fn evaluate_binary_arithmetic_operation(
medrecord: &MedRecord,
value: MedRecordValue,
operand: &SingleValueComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<Option<MedRecordValue>> {
let arithmetic_value =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No value to compare".to_string(),
))?;
Ok(Some(match kind {
BinaryArithmeticKind::Add => value.add(arithmetic_value)?,
BinaryArithmeticKind::Sub => value.sub(arithmetic_value)?,
BinaryArithmeticKind::Mul => value.mul(arithmetic_value)?,
BinaryArithmeticKind::Div => value.div(arithmetic_value)?,
BinaryArithmeticKind::Pow => value.pow(arithmetic_value)?,
BinaryArithmeticKind::Mod => value.r#mod(arithmetic_value)?,
}))
}
#[inline]
fn evaluate_unary_arithmetic_operation(
value: MedRecordValue,
kind: &UnaryArithmeticKind,
) -> MedRecordValue {
match kind {
UnaryArithmeticKind::Round => value.round(),
UnaryArithmeticKind::Ceil => value.ceil(),
UnaryArithmeticKind::Floor => value.floor(),
UnaryArithmeticKind::Abs => value.abs(),
UnaryArithmeticKind::Sqrt => value.sqrt(),
UnaryArithmeticKind::Trim => value.trim(),
UnaryArithmeticKind::TrimStart => value.trim_start(),
UnaryArithmeticKind::TrimEnd => value.trim_end(),
UnaryArithmeticKind::Lowercase => value.lowercase(),
UnaryArithmeticKind::Uppercase => value.uppercase(),
}
}
#[inline]
fn evaluate_slice(value: MedRecordValue, range: &Range<usize>) -> MedRecordValue {
value.slice(range.clone())
}
#[inline]
fn evaluate_is_string(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::String(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_int(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::Int(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_float(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::Float(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_bool(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::Bool(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_datetime(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::DateTime(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_duration(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::Duration(_) => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_is_null(value: MedRecordValue) -> Option<MedRecordValue> {
match value {
MedRecordValue::Null => Some(value),
_ => None,
}
}
#[inline]
fn evaluate_either_or(
medrecord: &MedRecord,
value: MedRecordValue,
either: &Wrapper<SingleValueWithoutIndexOperand<O>>,
or: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<Option<MedRecordValue>> {
let either_result = either.evaluate_forward(medrecord, Some(value.clone()))?;
let or_result = or.evaluate_forward(medrecord, Some(value))?;
match (either_result, or_result) {
(Some(either_result), _) => Ok(Some(either_result)),
(None, Some(or_result)) => Ok(Some(or_result)),
_ => Ok(None),
}
}
}
impl<O: RootOperand> SingleValueWithoutIndexOperation<O> {
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<MedRecordValue>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordValue>>>
where
O: 'a,
{
Ok(match self {
Self::SingleValueComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_single_value_comparison_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleValuesComparisonOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_multiple_values_comparison_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
values
.map(move |(key, value)| {
let Some(value) = value else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_binary_arithmetic_operation(
medrecord, value, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(
key,
Some(Self::evaluate_unary_arithmetic_operation(value, &kind)),
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Some(Self::evaluate_slice(value, &range)))
}))
}
Self::IsString => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_string(value))
})),
Self::IsInt => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_int(value))
})),
Self::IsFloat => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_float(value))
})),
Self::IsBool => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_bool(value))
})),
Self::IsDateTime => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_datetime(value))
})),
Self::IsDuration => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_duration(value))
})),
Self::IsNull => Box::new(values.map(move |(key, value)| {
let Some(value) = value else {
return (key, None);
};
(key, Self::evaluate_is_null(value))
})),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, values, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, values, operand)?
}
Self::Merge { operand } => {
let (values_1, values_2) = Itertools::tee(values);
let values_1 = values_1.filter_map(|(_, value)| value);
let values_1: Vec<_> = operand
.evaluate_forward(medrecord, Box::new(values_1))?
.collect();
Box::new(values_2.map(move |(key, value)| {
let value = value.filter(|value| values_1.contains(value));
(key, value)
}))
}
})
}
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<MedRecordValue>>,
either: &Wrapper<SingleValueWithoutIndexOperand<O>>,
or: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordValue>>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let either_values = either.evaluate_forward_grouped(medrecord, Box::new(values_1))?;
let mut or_value: Vec<_> = or
.evaluate_forward_grouped(medrecord, Box::new(values_2))?
.collect();
let values = either_values.map(move |(key, either_value)| {
let value_position = or_value
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_value = or_value.remove(value_position).1;
let value = match (either_value, or_value) {
(Some(either_result), _) => Some(either_result),
(None, Some(or_result)) => Some(or_result),
_ => None,
};
(key, value)
});
Ok(Box::new(values))
}
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<MedRecordValue>>,
operand: &Wrapper<SingleValueWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordValue>>>
where
O: 'a,
{
let (values_1, values_2) = Itertools::tee(values);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(values_1))?
.collect();
let values = values_2.map(move |(key, value)| {
let value_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_value = result.remove(value_position).1;
let value = match excluded_value {
Some(_) => None,
None => value,
};
(key, value)
});
Ok(Box::new(values))
}
}