use super::{
operand::{
MultipleAttributesComparisonOperand, MultipleAttributesWithIndexOperand,
SingleAttributeComparisonOperand, SingleAttributeWithIndexOperand,
},
AttributesTreeOperand, BinaryArithmeticKind, GetAttributes, MultipleComparisonKind,
SingleComparisonKind, SingleKindWithIndex, UnaryArithmeticKind,
};
use crate::{
errors::{MedRecordError, MedRecordResult},
medrecord::{
datatypes::{
Abs, Contains, DataType, EndsWith, Lowercase, Mod, Pow, Slice, StartsWith, Trim,
TrimEnd, TrimStart, Uppercase,
},
querying::{
attributes::{
operand::SingleAttributeWithoutIndexOperand, MultipleAttributesWithIndexContext,
MultipleAttributesWithoutIndexOperand, MultipleKind, SingleKindWithoutIndex,
},
tee_grouped_iterator,
values::MultipleValuesWithIndexOperand,
BoxedIterator, DeepClone, EvaluateForward, EvaluateForwardGrouped, GroupedIterator,
RootOperand,
},
MedRecordAttribute, MedRecordValue, Wrapper,
},
MedRecord,
};
use itertools::Itertools;
use medmodels_utils::{
aliases::{MrHashMap, MrHashSet},
traits::ReadWriteOrPanic,
};
use rand::{rng, seq::IteratorRandom};
use std::{
cmp::Ordering,
collections::HashMap,
ops::{Add, Mul, Range, Sub},
};
#[derive(Debug, Clone)]
pub enum AttributesTreeOperation<O: RootOperand> {
AttributesOperation {
operand: Wrapper<MultipleAttributesWithIndexOperand<O>>,
},
SingleAttributeComparisonOperation {
operand: SingleAttributeComparisonOperand,
kind: SingleComparisonKind,
},
MultipleAttributesComparisonOperation {
operand: MultipleAttributesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleAttributeComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsMax,
IsMin,
EitherOr {
either: Wrapper<AttributesTreeOperand<O>>,
or: Wrapper<AttributesTreeOperand<O>>,
},
Exclude {
operand: Wrapper<AttributesTreeOperand<O>>,
},
Merge {
operand: Wrapper<AttributesTreeOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for AttributesTreeOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::AttributesOperation { operand } => Self::AttributesOperation {
operand: operand.deep_clone(),
},
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::SingleAttributeComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::MultipleAttributesComparisonOperation {
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::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> AttributesTreeOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>
where
O: 'a,
{
Ok(match self {
Self::AttributesOperation { operand } => Box::new(Self::evaluate_attributes_operation(
medrecord, attributes, operand,
)?),
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::evaluate_single_attribute_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, attributes, operand, kind)?
}
Self::UnaryArithmeticOperation { kind } => Box::new(
Self::evaluate_unary_arithmetic_operation(attributes, kind.clone()),
),
Self::Slice(range) => Box::new(Self::evaluate_slice(attributes, range.clone())),
Self::IsString => Box::new(Self::evaluate_is_string(attributes)),
Self::IsInt => Box::new(Self::evaluate_is_int(attributes)),
Self::IsMax => Box::new(Self::evaluate_is_max(attributes)?),
Self::IsMin => Box::new(Self::evaluate_is_min(attributes)?),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, attributes, operand)?,
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
pub(crate) fn get_max<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(attributes.map(|(index, attributes)| {
let mut attributes = attributes.into_iter();
let first_attribute = attributes.next().ok_or(MedRecordError::QueryError(
"No attributes to compare".to_string(),
))?;
let attribute = attributes.try_fold(first_attribute, |max, attribute| {
match attribute.partial_cmp(&max) {
Some(Ordering::Greater) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute);
let second_dtype = DataType::from(max);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(max),
}
})?;
Ok((index, attribute))
}).collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
pub(crate) fn get_min<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(attributes.map(|(index, attributes)| {
let mut attributes = attributes.into_iter();
let first_attribute = attributes.next().ok_or(MedRecordError::QueryError(
"No attributes to compare".to_string(),
))?;
let attribute = attributes.try_fold(first_attribute, |max, attribute| {
match attribute.partial_cmp(&max) {
Some(Ordering::Less) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute);
let second_dtype = DataType::from(max);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(max),
}
})?;
Ok((index, attribute))
}).collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
pub(crate) fn get_count<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(attributes
.map(|(index, attribute)| (index, MedRecordAttribute::Int(attribute.len() as i64))))
}
#[inline]
pub(crate) fn get_sum<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(attributes.map(|(index, attributes)| {
let mut attributes = attributes.into_iter();
let first_attribute = attributes.next().ok_or(MedRecordError::QueryError(
"No attributes to compare".to_string(),
))?;
let attribute = attributes.try_fold(first_attribute, |sum, attribute| {
let first_dtype = DataType::from(&sum);
let second_dtype = DataType::from(&attribute);
sum.add(attribute).map_err(|_| {
MedRecordError::QueryError(format!(
"Cannot add attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
))
})
})?;
Ok((index, attribute))
}).collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
pub(crate) fn get_random<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(attributes
.map(|(index, attributes)| {
let first_attribute =
attributes
.into_iter()
.choose(&mut rng())
.ok_or(MedRecordError::QueryError(
"No attributes to compare".to_string(),
))?;
Ok((index, first_attribute))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter())
}
#[inline]
fn evaluate_attributes_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
operand: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let MultipleAttributesWithIndexContext::AttributesTree {
operand: _,
ref kind,
} = operand.0.read_or_panic().context
else {
unreachable!()
};
let multiple_operand_attributes: BoxedIterator<_> = match kind {
MultipleKind::Max => Box::new(AttributesTreeOperation::<O>::get_max(attributes_1)?),
MultipleKind::Min => Box::new(AttributesTreeOperation::<O>::get_min(attributes_1)?),
MultipleKind::Count => Box::new(AttributesTreeOperation::<O>::get_count(attributes_1)?),
MultipleKind::Sum => Box::new(AttributesTreeOperation::<O>::get_sum(attributes_1)?),
MultipleKind::Random => {
Box::new(AttributesTreeOperation::<O>::get_random(attributes_1)?)
}
};
let result = operand.evaluate_forward(medrecord, multiple_operand_attributes)?;
let mut attributes: MrHashMap<_, _> = attributes_2.into_iter().collect();
Ok(result
.map(move |(index, _)| (index, attributes.remove(&index).expect("Index must exist"))))
}
#[inline]
fn evaluate_single_attribute_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
comparison_operand: &SingleAttributeComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>> {
let comparison_attribute =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute > &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::GreaterThanOrEqualTo => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute >= &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::LessThan => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute < &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::LessThanOrEqualTo => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute <= &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::EqualTo => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute == &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::NotEqualTo => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute != &comparison_attribute)
.collect(),
)
})))
}
SingleComparisonKind::StartsWith => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute.starts_with(&comparison_attribute))
.collect(),
)
})))
}
SingleComparisonKind::EndsWith => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute.ends_with(&comparison_attribute))
.collect(),
)
})))
}
SingleComparisonKind::Contains => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| attribute.contains(&comparison_attribute))
.collect(),
)
})))
}
}
}
#[inline]
fn evaluate_multiple_attributes_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
comparison_operand: &MultipleAttributesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>> {
let comparison_attributes = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| comparison_attributes.contains(attribute))
.collect(),
)
})))
}
MultipleComparisonKind::IsNotIn => {
Ok(Box::new(attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.filter(|attribute| !comparison_attributes.contains(attribute))
.collect(),
)
})))
}
}
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a, I: 'a>(
medrecord: &MedRecord,
attributes: impl Iterator<Item = (I, Vec<MedRecordAttribute>)>,
operand: &SingleAttributeComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<BoxedIterator<'a, (I, Vec<MedRecordAttribute>)>> {
let arithmetic_attribute =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
let attributes: Box<dyn Iterator<Item = MedRecordResult<(I, Vec<MedRecordAttribute>)>>> =
match kind {
BinaryArithmeticKind::Add => {
Box::new(attributes.map(move |(index, attributes)| {
Ok((
index,
attributes
.into_iter()
.map(|attribute| attribute.add(arithmetic_attribute.clone()))
.collect::<MedRecordResult<Vec<_>>>()?,
))
}))
}
BinaryArithmeticKind::Sub => {
Box::new(attributes.map(move |(index, attributes)| {
Ok((
index,
attributes
.into_iter()
.map(|attribute| attribute.sub(arithmetic_attribute.clone()))
.collect::<MedRecordResult<Vec<_>>>()?,
))
}))
}
BinaryArithmeticKind::Mul => {
Box::new(attributes.map(move |(index, attributes)| {
Ok((
index,
attributes
.into_iter()
.map(|attribute| attribute.mul(arithmetic_attribute.clone()))
.collect::<MedRecordResult<Vec<_>>>()?,
))
}))
}
BinaryArithmeticKind::Pow => {
Box::new(attributes.map(move |(index, attributes)| {
Ok((
index,
attributes
.into_iter()
.map(|attribute| attribute.pow(arithmetic_attribute.clone()))
.collect::<MedRecordResult<Vec<_>>>()?,
))
}))
}
BinaryArithmeticKind::Mod => {
Box::new(attributes.map(move |(index, attributes)| {
Ok((
index,
attributes
.into_iter()
.map(|attribute| attribute.r#mod(arithmetic_attribute.clone()))
.collect::<MedRecordResult<Vec<_>>>()?,
))
}))
}
};
Ok(Box::new(
attributes.collect::<MedRecordResult<Vec<_>>>()?.into_iter(),
))
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
kind: UnaryArithmeticKind,
) -> impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>
where
O: 'a,
{
attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.map(|attribute| match kind {
UnaryArithmeticKind::Abs => attribute.abs(),
UnaryArithmeticKind::Trim => attribute.trim(),
UnaryArithmeticKind::TrimStart => attribute.trim_start(),
UnaryArithmeticKind::TrimEnd => attribute.trim_end(),
UnaryArithmeticKind::Lowercase => attribute.lowercase(),
UnaryArithmeticKind::Uppercase => attribute.uppercase(),
})
.collect(),
)
})
}
#[inline]
fn evaluate_slice<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
range: Range<usize>,
) -> impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>
where
O: 'a,
{
attributes.map(move |(index, attributes)| {
(
index,
attributes
.into_iter()
.map(|attribute| attribute.slice(range.clone()))
.collect(),
)
})
}
#[inline]
fn evaluate_is_string<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>
where
O: 'a,
{
attributes.map(|(index, attribute)| {
(
index,
attribute
.into_iter()
.filter(|attribute| matches!(attribute, MedRecordAttribute::String(_)))
.collect(),
)
})
}
#[inline]
fn evaluate_is_int<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>
where
O: 'a,
{
attributes.map(|(index, attribute)| {
(
index,
attribute
.into_iter()
.filter(|attribute| matches!(attribute, MedRecordAttribute::Int(_)))
.collect(),
)
})
}
#[inline]
fn evaluate_is_max<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let max_attributes: MrHashMap<_, _> = Self::get_max(attributes_1)?.collect();
Ok(Box::new(attributes_2.map(move |(index, attributes)| {
let max_attribute = max_attributes.get(&index).expect("Index must exist");
(
index,
attributes
.into_iter()
.filter(|attribute| attribute == max_attribute)
.collect(),
)
})))
}
#[inline]
fn evaluate_is_min<'a>(
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let min_attributes: MrHashMap<_, _> = Self::get_min(attributes_1)?.collect();
Ok(Box::new(attributes_2.map(move |(index, attributes)| {
let min_attribute = min_attributes.get(&index).expect("Index must exist");
(
index,
attributes
.into_iter()
.filter(|attribute| attribute == min_attribute)
.collect(),
)
})))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
either: &Wrapper<AttributesTreeOperand<O>>,
or: &Wrapper<AttributesTreeOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let either_attributes = either.evaluate_forward(medrecord, Box::new(attributes_1))?;
let or_attributes = or.evaluate_forward(medrecord, Box::new(attributes_2))?;
Ok(Box::new(
either_attributes
.chain(or_attributes)
.into_group_map_by(|(k, _)| *k)
.into_iter()
.map(|(idx, group)| {
let attrs = group.into_iter().flat_map(|(_, v)| v).unique().collect();
(idx, attrs)
}),
))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, Vec<MedRecordAttribute>)> + 'a,
operand: &Wrapper<AttributesTreeOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let mut result: MrHashMap<_, _> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Ok(Box::new(attributes_2.map(move |(index, attributes)| {
let entry = result.remove(&index).unwrap_or(Vec::new());
(
index,
attributes
.into_iter()
.filter(|attr| !entry.contains(attr))
.collect(),
)
})))
}
}
impl<O: RootOperand> AttributesTreeOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
) -> MedRecordResult<
GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
>
where
O: 'a,
{
Ok(match self {
Self::AttributesOperation { operand } => {
Self::evaluate_attributes_operation_grouped(self, medrecord, attributes, operand)?
}
Self::SingleAttributeComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_single_attribute_comparison_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleAttributesComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_binary_arithmetic_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_unary_arithmetic_operation(
attributes,
kind.clone(),
)) as BoxedIterator<_>,
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_slice(attributes, range.clone()))
as BoxedIterator<_>,
)
}))
}
Self::IsString => Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_is_string(attributes)) as BoxedIterator<_>,
)
})),
Self::IsInt => Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_is_int(attributes)) as BoxedIterator<_>,
)
})),
Self::IsMax => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_is_max(attributes)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::IsMin => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_is_min(attributes)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, attributes, operand)?
}
Self::Merge { operand } => {
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let attributes_1 = attributes_1.flat_map(|(_, attribute)| attribute);
let attributes_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Box::new(attributes_2.map(move |(key, attributes)| {
let attributes: Vec<_> = attributes
.filter(|attributes| attributes_1.contains(attributes))
.collect();
let attributes: BoxedIterator<_> = Box::new(attributes.into_iter());
(key, attributes)
}))
}
})
}
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_attributes_operation_grouped<'a>(
&self,
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
operand: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<
GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut attributes_2: Vec<_> = attributes_2.collect();
let MultipleAttributesWithIndexContext::AttributesTree {
operand: _,
ref kind,
} = operand.0.read_or_panic().context
else {
unreachable!()
};
let attributes_1: Vec<_> = attributes_1
.map(|(key, attributes)| {
let attributes: BoxedIterator<_> = match kind {
MultipleKind::Max => {
Box::new(AttributesTreeOperation::<O>::get_max(attributes)?)
}
MultipleKind::Min => {
Box::new(AttributesTreeOperation::<O>::get_min(attributes)?)
}
MultipleKind::Count => {
Box::new(AttributesTreeOperation::<O>::get_count(attributes)?)
}
MultipleKind::Sum => {
Box::new(AttributesTreeOperation::<O>::get_sum(attributes)?)
}
MultipleKind::Random => {
Box::new(AttributesTreeOperation::<O>::get_random(attributes)?)
}
};
Ok((key, attributes))
})
.collect::<MedRecordResult<_>>()?;
let attibutes_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(attributes_1.into_iter()))?;
let attributes = attibutes_1.map(move |(key, attributes_1)| {
let attributes_position = attributes_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let mut attributes_2 = attributes_2
.remove(attributes_position)
.1
.collect::<HashMap<_, _>>();
let attributes: BoxedIterator<_> = Box::new(attributes_1.map(move |(index, _)| {
(
index,
attributes_2.remove(&index).expect("Attribute must exist"),
)
}));
(key, attributes)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
either: &Wrapper<AttributesTreeOperand<O>>,
or: &Wrapper<AttributesTreeOperand<O>>,
) -> MedRecordResult<
GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let either_attributes = either.evaluate_forward_grouped(medrecord, attributes_1)?;
let mut or_attributes: Vec<_> = or
.evaluate_forward_grouped(medrecord, attributes_2)?
.collect();
let attributes = either_attributes.map(move |(key, either_attributes)| {
let attributes_position = or_attributes
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_attributes = or_attributes.remove(attributes_position).1;
let attributes: BoxedIterator<_> = Box::new(
either_attributes
.chain(or_attributes)
.into_group_map_by(|(k, _)| *k)
.into_iter()
.map(|(idx, group)| {
let attrs = group.into_iter().flat_map(|(_, v)| v).unique().collect();
(idx, attrs)
}),
);
(key, attributes)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
operand: &Wrapper<AttributesTreeOperand<O>>,
) -> MedRecordResult<
GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, Vec<MedRecordAttribute>)>>,
>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, attributes_1)?
.collect();
let attributes = attributes_2.map(move |(key, attributes)| {
let attributes_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let mut excluded_attributes: MrHashMap<_, _> =
result.remove(attributes_position).1.collect();
let attributes: BoxedIterator<_> =
Box::new(attributes.map(move |(index, attributes)| {
let entry = excluded_attributes.remove(&index).unwrap_or(Vec::new());
(
index,
attributes
.into_iter()
.filter(|attr| !entry.contains(attr))
.collect(),
)
}));
(key, attributes)
});
Ok(Box::new(attributes))
}
}
#[derive(Debug, Clone)]
pub enum MultipleAttributesWithIndexOperation<O: RootOperand> {
AttributeWithIndexOperation {
operand: Wrapper<SingleAttributeWithIndexOperand<O>>,
},
AttributeWithoutIndexOperation {
operand: Wrapper<SingleAttributeWithoutIndexOperand<O>>,
},
SingleAttributeComparisonOperation {
operand: SingleAttributeComparisonOperand,
kind: SingleComparisonKind,
},
MultipleAttributesComparisonOperation {
operand: MultipleAttributesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleAttributeComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
ToValues {
operand: Wrapper<MultipleValuesWithIndexOperand<O>>,
},
Slice(Range<usize>),
IsString,
IsInt,
IsMax,
IsMin,
EitherOr {
either: Wrapper<MultipleAttributesWithIndexOperand<O>>,
or: Wrapper<MultipleAttributesWithIndexOperand<O>>,
},
Exclude {
operand: Wrapper<MultipleAttributesWithIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleAttributesWithIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for MultipleAttributesWithIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::AttributeWithIndexOperation { operand } => Self::AttributeWithIndexOperation {
operand: operand.deep_clone(),
},
Self::AttributeWithoutIndexOperation { operand } => {
Self::AttributeWithoutIndexOperation {
operand: operand.deep_clone(),
}
}
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::SingleAttributeComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::MultipleAttributesComparisonOperation {
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::ToValues { operand } => Self::ToValues {
operand: operand.deep_clone(),
},
Self::Slice(range) => Self::Slice(range.clone()),
Self::IsString => Self::IsString,
Self::IsInt => Self::IsInt,
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> MultipleAttributesWithIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
Ok(match self {
Self::AttributeWithIndexOperation { operand } => {
Self::evaluate_attribute_with_index_operation(medrecord, attributes, operand)?
}
Self::AttributeWithoutIndexOperation { operand } => {
Self::evaluate_attribute_without_index_operation(medrecord, attributes, operand)?
}
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::evaluate_single_attribute_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
Self::evaluate_binary_arithmetic_operation(medrecord, attributes, operand, kind)?,
),
Self::UnaryArithmeticOperation { kind } => Box::new(
Self::evaluate_unary_arithmetic_operation(attributes, kind.clone()),
),
Self::ToValues { operand } => {
Box::new(Self::evaluate_to_values(medrecord, attributes, operand)?)
}
Self::Slice(range) => Box::new(Self::evaluate_slice(attributes, range.clone())),
Self::IsString => Box::new(Self::evaluate_is_string(attributes)),
Self::IsInt => Box::new(Self::evaluate_is_int(attributes)),
Self::IsMax => Box::new(Self::evaluate_is_max(attributes)?),
Self::IsMin => Box::new(Self::evaluate_is_min(attributes)?),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, attributes, operand)?,
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
pub(crate) fn get_max<'a>(
mut attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>> {
let max_attribute = attributes.next();
let Some(max_attribute) = max_attribute else {
return Ok(None);
};
let max_attribute = attributes.try_fold(max_attribute, |max_attribute, attribute| {
match attribute.1.partial_cmp(&max_attribute.1) {
Some(Ordering::Greater) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute.1);
let second_dtype = DataType::from(max_attribute.1);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(max_attribute),
}
})?;
Ok(Some(max_attribute))
}
#[inline]
pub(crate) fn get_min<'a>(
mut attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>> {
let min_attribute = attributes.next();
let Some(min_attribute) = min_attribute else {
return Ok(None);
};
let min_attribute = attributes.try_fold(min_attribute, |min_attribute, attribute| {
match attribute.1.partial_cmp(&min_attribute.1) {
Some(Ordering::Less) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute.1);
let second_dtype = DataType::from(min_attribute.1);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(min_attribute),
}
})?;
Ok(Some(min_attribute))
}
#[inline]
pub(crate) fn get_random<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> Option<(&'a O::Index, MedRecordAttribute)> {
attributes.choose(&mut rng())
}
#[inline]
fn evaluate_attribute_with_index_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
operand: &Wrapper<SingleAttributeWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let kind = &operand.0.read_or_panic().kind;
let attribute = match kind {
SingleKindWithIndex::Max => {
MultipleAttributesWithIndexOperation::<O>::get_max(attributes_1)?
}
SingleKindWithIndex::Min => {
MultipleAttributesWithIndexOperation::<O>::get_min(attributes_1)?
}
SingleKindWithIndex::Random => {
MultipleAttributesWithIndexOperation::<O>::get_random(attributes_1)
}
};
Ok(match operand.evaluate_forward(medrecord, attribute)? {
Some(_) => Box::new(attributes_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_attribute_without_index_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
operand: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let attributes_1 = attributes_1.map(|(_, attribute)| attribute);
let kind = &operand.0.read_or_panic().kind;
let attribute = match kind {
SingleKindWithoutIndex::Max => {
MultipleAttributesWithoutIndexOperation::<O>::get_max(attributes_1)?
}
SingleKindWithoutIndex::Min => {
MultipleAttributesWithoutIndexOperation::<O>::get_min(attributes_1)?
}
SingleKindWithoutIndex::Count => Some(
MultipleAttributesWithoutIndexOperation::<O>::get_count(attributes_1),
),
SingleKindWithoutIndex::Sum => {
MultipleAttributesWithoutIndexOperation::<O>::get_sum(attributes_1)?
}
SingleKindWithoutIndex::Random => {
MultipleAttributesWithoutIndexOperation::<O>::get_random(attributes_1)
}
};
Ok(match operand.evaluate_forward(medrecord, attribute)? {
Some(_) => Box::new(attributes_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_single_attribute_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
comparison_operand: &SingleAttributeComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>> {
let comparison_attribute =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute > &comparison_attribute
})))
}
SingleComparisonKind::GreaterThanOrEqualTo => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute >= &comparison_attribute
})))
}
SingleComparisonKind::LessThan => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute < &comparison_attribute
})))
}
SingleComparisonKind::LessThanOrEqualTo => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute <= &comparison_attribute
})))
}
SingleComparisonKind::EqualTo => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute == &comparison_attribute
})))
}
SingleComparisonKind::NotEqualTo => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute != &comparison_attribute
})))
}
SingleComparisonKind::StartsWith => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute.starts_with(&comparison_attribute)
})))
}
SingleComparisonKind::EndsWith => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute.ends_with(&comparison_attribute)
})))
}
SingleComparisonKind::Contains => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
attribute.contains(&comparison_attribute)
})))
}
}
}
#[inline]
fn evaluate_multiple_attributes_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
comparison_operand: &MultipleAttributesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>> {
let comparison_attributes = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
comparison_attributes.contains(attribute)
})))
}
MultipleComparisonKind::IsNotIn => {
Ok(Box::new(attributes.filter(move |(_, attribute)| {
!comparison_attributes.contains(attribute)
})))
}
}
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a>(
medrecord: &MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
operand: &SingleAttributeComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let arithmetic_attribute =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
let attributes = attributes
.map(move |(t, attribute)| {
match kind {
BinaryArithmeticKind::Add => attribute.add(arithmetic_attribute.clone()),
BinaryArithmeticKind::Sub => attribute.sub(arithmetic_attribute.clone()),
BinaryArithmeticKind::Mul => {
attribute.clone().mul(arithmetic_attribute.clone())
}
BinaryArithmeticKind::Pow => {
attribute.clone().pow(arithmetic_attribute.clone())
}
BinaryArithmeticKind::Mod => {
attribute.clone().r#mod(arithmetic_attribute.clone())
}
}
.map_err(|_| {
MedRecordError::QueryError(format!(
"Failed arithmetic operation {kind}. Consider narrowing down the attributes using .is_int() or .is_float()",
))
}).map(|result| (t, result))
});
Ok(attributes.collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
kind: UnaryArithmeticKind,
) -> impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>
where
O: 'a,
{
attributes.map(move |(t, attribute)| {
let attribute = match kind {
UnaryArithmeticKind::Abs => attribute.abs(),
UnaryArithmeticKind::Trim => attribute.trim(),
UnaryArithmeticKind::TrimStart => attribute.trim_start(),
UnaryArithmeticKind::TrimEnd => attribute.trim_end(),
UnaryArithmeticKind::Lowercase => attribute.lowercase(),
UnaryArithmeticKind::Uppercase => attribute.uppercase(),
};
(t, attribute)
})
}
pub(crate) fn get_values<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordValue)>>
where
O: 'a,
{
Ok(attributes
.map(|(index, attribute)| {
let value = index.get_attributes(medrecord)?.get(&attribute).ok_or(
MedRecordError::QueryError(format!(
"Cannot find attribute {attribute} for index {index}"
)),
)?;
Ok((index, value.clone()))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter())
}
#[inline]
fn evaluate_to_values<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
operand: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let values = Self::get_values(medrecord, attributes_1)?;
let mut attributes: HashMap<_, _> = attributes_2.collect();
let values = operand.evaluate_forward(medrecord, Box::new(values.into_iter()))?;
Ok(values.map(move |(index, _)| {
(
index,
attributes.remove(&index).expect("Attribute must exist"),
)
}))
}
#[inline]
fn evaluate_slice<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
range: Range<usize>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>
where
O: 'a,
{
attributes.map(move |(t, attribute)| (t, attribute.slice(range.clone())))
}
#[inline]
fn evaluate_is_string<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>
where
O: 'a,
{
attributes.filter(|(_, attribute)| matches!(attribute, MedRecordAttribute::String(_)))
}
#[inline]
fn evaluate_is_int<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>,
) -> impl Iterator<Item = (&'a O::Index, MedRecordAttribute)>
where
O: 'a,
{
attributes.filter(|(_, attribute)| matches!(attribute, MedRecordAttribute::Int(_)))
}
#[inline]
fn evaluate_is_max<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let max_attribute = Self::get_max(attributes_1)?;
let Some(max_attribute) = max_attribute else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(attributes_2.filter(move |(_, attribute)| {
*attribute == max_attribute.1
})))
}
#[inline]
fn evaluate_is_min<'a>(
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let min_attribute = Self::get_min(attributes_1)?;
let Some(min_attribute) = min_attribute else {
return Ok(Box::new(std::iter::empty()));
};
Ok(Box::new(attributes_2.filter(move |(_, attribute)| {
*attribute == min_attribute.1
})))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
either: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
or: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let either_attributes = either.evaluate_forward(medrecord, Box::new(attributes_1))?;
let or_attributes = or.evaluate_forward(medrecord, Box::new(attributes_2))?;
Ok(Box::new(
either_attributes
.chain(or_attributes)
.unique_by(|attribute| attribute.0.clone()),
))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = (&'a O::Index, MedRecordAttribute)> + 'a,
operand: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.map(|(index, _)| index)
.collect();
Ok(Box::new(
attributes_2.filter(move |(index, _)| !result.contains(index)),
))
}
}
impl<O: RootOperand> MultipleAttributesWithIndexOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
Ok(match self {
Self::AttributeWithIndexOperation { operand } => {
Self::evaluate_attribute_with_index_operation_grouped(
medrecord, attributes, operand,
)?
}
Self::AttributeWithoutIndexOperation { operand } => {
Self::evaluate_attribute_without_index_operation_grouped(
medrecord, attributes, operand,
)?
}
Self::SingleAttributeComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_single_attribute_comparison_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleAttributesComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_binary_arithmetic_operation(
medrecord, attributes, operand, kind,
)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_unary_arithmetic_operation(
attributes,
kind.clone(),
)) as BoxedIterator<_>,
)
}))
}
Self::ToValues { operand } => Box::new(Self::evaluate_to_values_grouped(
medrecord, attributes, operand,
)?),
Self::Slice(range) => {
let range = range.clone();
Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_slice(attributes, range.clone()))
as BoxedIterator<_>,
)
}))
}
Self::IsString => Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_is_string(attributes)) as BoxedIterator<_>,
)
})),
Self::IsInt => Box::new(attributes.map(move |(key, attributes)| {
(
key,
Box::new(Self::evaluate_is_int(attributes)) as BoxedIterator<_>,
)
})),
Self::IsMax => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_is_max(attributes)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::IsMin => Box::new(
attributes
.map(move |(key, attributes)| {
Ok((
key,
Box::new(Self::evaluate_is_min(attributes)?) as BoxedIterator<_>,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, attributes, operand)?
}
Self::Merge { operand } => {
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let attributes_1 = attributes_1.flat_map(|(_, attribute)| attribute);
let attributes_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Box::new(attributes_2.map(move |(key, attributes)| {
let attributes: Vec<_> = attributes
.filter(|attribute| attributes_1.contains(attribute))
.collect();
let attributes: BoxedIterator<_> = Box::new(attributes.into_iter());
(key, attributes)
}))
}
})
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_attribute_with_index_operation_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
operand: &Wrapper<SingleAttributeWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut attributes_2 = attributes_2.collect::<Vec<_>>();
let kind = &operand.0.read_or_panic().kind;
let attributes_1: Vec<_> = attributes_1
.map(|(key, attributes)| {
let attribute = match kind {
SingleKindWithIndex::Max => {
MultipleAttributesWithIndexOperation::<O>::get_max(attributes)?
}
SingleKindWithIndex::Min => {
MultipleAttributesWithIndexOperation::<O>::get_min(attributes)?
}
SingleKindWithIndex::Random => {
MultipleAttributesWithIndexOperation::<O>::get_random(attributes)
}
};
Ok((key, attribute))
})
.collect::<MedRecordResult<_>>()?;
let attributes_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(attributes_1.into_iter()))?;
Ok(Box::new(attributes_1.map(
move |(key, attribute)| match attribute {
Some(_) => {
let attributes_position = attributes_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
attributes_2.remove(attributes_position)
}
None => (key, Box::new(std::iter::empty()) as BoxedIterator<_>),
},
)))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_attribute_without_index_operation_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
operand: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut attributes_2: Vec<_> = attributes_2.collect();
let kind = &operand.0.read_or_panic().kind;
let attributes_1: Vec<_> = attributes_1
.map(|(key, attributes)| {
let attributes = attributes.map(|(_, attribute)| attribute);
let attribute = match kind {
SingleKindWithoutIndex::Max => {
MultipleAttributesWithoutIndexOperation::<O>::get_max(attributes)?
}
SingleKindWithoutIndex::Min => {
MultipleAttributesWithoutIndexOperation::<O>::get_min(attributes)?
}
SingleKindWithoutIndex::Count => Some(
MultipleAttributesWithoutIndexOperation::<O>::get_count(attributes),
),
SingleKindWithoutIndex::Sum => {
MultipleAttributesWithoutIndexOperation::<O>::get_sum(attributes)?
}
SingleKindWithoutIndex::Random => {
MultipleAttributesWithoutIndexOperation::<O>::get_random(attributes)
}
};
Ok((key, attribute))
})
.collect::<MedRecordResult<_>>()?;
let attributes_1 =
operand.evaluate_forward_grouped(medrecord, Box::new(attributes_1.into_iter()))?;
Ok(Box::new(attributes_1.map(
move |(key, attribute)| match attribute {
Some(_) => {
let attributes_position = attributes_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
attributes_2.remove(attributes_position)
}
None => (key, Box::new(std::iter::empty()) as BoxedIterator<_>),
},
)))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_to_values_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
operand: &Wrapper<MultipleValuesWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut attributes_2: Vec<_> = attributes_2.collect();
let values: Vec<_> = attributes_1
.map(|(key, attributes)| {
let values: BoxedIterator<_> = Box::new(Self::get_values(medrecord, attributes)?);
Ok((key, values))
})
.collect::<MedRecordResult<_>>()?;
let values = operand.evaluate_forward_grouped(medrecord, Box::new(values.into_iter()))?;
let attributes = values.map(move |(key, values)| {
let attributes_position = attributes_2
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let mut attributes = attributes_2
.remove(attributes_position)
.1
.collect::<HashMap<_, _>>();
let attributes: BoxedIterator<_> = Box::new(values.map(move |(index, _)| {
(
index,
attributes.remove(&index).expect("Attribute must exist"),
)
}));
(key, attributes)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
either: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
or: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let either_attributes = either.evaluate_forward_grouped(medrecord, attributes_1)?;
let mut or_attributes: Vec<_> = or
.evaluate_forward_grouped(medrecord, attributes_2)?
.collect();
let attributes = either_attributes.map(move |(key, either_attributes)| {
let attributes_position = or_attributes
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_attributes = or_attributes.remove(attributes_position).1;
let attributes: BoxedIterator<_> = Box::new(
either_attributes
.chain(or_attributes)
.unique_by(|attributes| attributes.0.clone()),
);
(key, attributes)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>,
operand: &Wrapper<MultipleAttributesWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, BoxedIterator<'a, (&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = tee_grouped_iterator(attributes);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, attributes_1)?
.collect();
let attributes = attributes_2.map(move |(key, attributes)| {
let attributes_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_attributes: MrHashSet<_> = result.remove(attributes_position).1.collect();
let attributes: BoxedIterator<_> = Box::new(
attributes.filter(move |attributes| !excluded_attributes.contains(attributes)),
);
(key, attributes)
});
Ok(Box::new(attributes))
}
}
#[derive(Debug, Clone)]
pub enum MultipleAttributesWithoutIndexOperation<O: RootOperand> {
AttributeOperation {
operand: Wrapper<SingleAttributeWithoutIndexOperand<O>>,
},
SingleAttributeComparisonOperation {
operand: SingleAttributeComparisonOperand,
kind: SingleComparisonKind,
},
MultipleAttributesComparisonOperation {
operand: MultipleAttributesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleAttributeComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
IsMax,
IsMin,
EitherOr {
either: Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
or: Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
},
Exclude {
operand: Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for MultipleAttributesWithoutIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::AttributeOperation { operand } => Self::AttributeOperation {
operand: operand.deep_clone(),
},
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::SingleAttributeComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::MultipleAttributesComparisonOperation {
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::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> MultipleAttributesWithoutIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>>
where
O: 'a,
{
Ok(match self {
Self::AttributeOperation { operand } => {
Self::evaluate_attribute_operation(medrecord, attributes, operand)?
}
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::evaluate_single_attribute_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attributes, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
Self::evaluate_binary_arithmetic_operation(medrecord, attributes, operand, kind)?,
),
Self::UnaryArithmeticOperation { kind } => Box::new(
Self::evaluate_unary_arithmetic_operation(attributes, kind.clone()),
),
Self::Slice(range) => Box::new(Self::evaluate_slice(attributes, range.clone())),
Self::IsString => Box::new(
attributes.filter(|attribute| matches!(attribute, MedRecordAttribute::String(_))),
),
Self::IsInt => Box::new(
attributes.filter(|attribute| matches!(attribute, MedRecordAttribute::Int(_))),
),
Self::IsMax => {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let max_attribute = Self::get_max(attributes_1)?;
let Some(max_attribute) = max_attribute else {
return Ok(Box::new(std::iter::empty()));
};
Box::new(attributes_2.filter(move |attribute| *attribute == max_attribute))
}
Self::IsMin => {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let min_attribute = Self::get_min(attributes_1)?;
let Some(min_attribute) = min_attribute else {
return Ok(Box::new(std::iter::empty()));
};
Box::new(attributes_2.filter(move |attribute| *attribute == min_attribute))
}
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => Self::evaluate_exclude(medrecord, attributes, operand)?,
})
}
#[inline]
pub(crate) fn get_max(
mut attributes: impl Iterator<Item = MedRecordAttribute>,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let max_attribute = attributes.next();
let Some(max_attribute) = max_attribute else {
return Ok(None);
};
let max_attribute = attributes.try_fold(max_attribute, |max_attribute, attribute| {
match attribute.partial_cmp(&max_attribute) {
Some(Ordering::Greater) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute);
let second_dtype = DataType::from(max_attribute);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(max_attribute),
}
})?;
Ok(Some(max_attribute))
}
#[inline]
pub(crate) fn get_min(
mut attributes: impl Iterator<Item = MedRecordAttribute>,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let min_attribute = attributes.next();
let Some(min_attribute) = min_attribute else {
return Ok(None);
};
let min_attribute = attributes.try_fold(min_attribute, |min_attribute, attribute| {
match attribute.partial_cmp(&min_attribute) {
Some(Ordering::Less) => Ok(attribute),
None => {
let first_dtype = DataType::from(attribute);
let second_dtype = DataType::from(min_attribute);
Err(MedRecordError::QueryError(format!(
"Cannot compare attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
)))
}
_ => Ok(min_attribute),
}
})?;
Ok(Some(min_attribute))
}
#[inline]
pub(crate) fn get_count(
attributes: impl Iterator<Item = MedRecordAttribute>,
) -> MedRecordAttribute {
MedRecordAttribute::Int(attributes.count() as i64)
}
#[inline]
pub(crate) fn get_sum(
mut attributes: impl Iterator<Item = MedRecordAttribute>,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let first_attribute = attributes.next();
let Some(first_attribute) = first_attribute else {
return Ok(None);
};
let sum = attributes.try_fold(first_attribute, |sum, attribute| {
let first_dtype = DataType::from(&sum);
let second_dtype = DataType::from(&attribute);
sum.add(attribute).map_err(|_| {
MedRecordError::QueryError(format!(
"Cannot add attributes of data types {first_dtype} and {second_dtype}. Consider narrowing down the attributes using .is_string() or .is_int()"
))
})
})?;
Ok(Some(sum))
}
#[inline]
pub(crate) fn get_random(
attributes: impl Iterator<Item = MedRecordAttribute>,
) -> Option<MedRecordAttribute> {
attributes.choose(&mut rng())
}
#[inline]
fn evaluate_attribute_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
operand: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>> {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let kind = &operand.0.read_or_panic().kind;
let attribute = match kind {
SingleKindWithoutIndex::Max => Self::get_max(attributes_1)?,
SingleKindWithoutIndex::Min => Self::get_min(attributes_1)?,
SingleKindWithoutIndex::Count => Some(Self::get_count(attributes_1)),
SingleKindWithoutIndex::Sum => Self::get_sum(attributes_1)?,
SingleKindWithoutIndex::Random => Self::get_random(attributes_1),
};
Ok(match operand.evaluate_forward(medrecord, attribute)? {
Some(_) => Box::new(attributes_2),
None => Box::new(std::iter::empty()),
})
}
#[inline]
fn evaluate_single_attribute_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
comparison_operand: &SingleAttributeComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>> {
let comparison_attribute =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
match kind {
SingleComparisonKind::GreaterThan => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute > &comparison_attribute
})))
}
SingleComparisonKind::GreaterThanOrEqualTo => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute >= &comparison_attribute
})))
}
SingleComparisonKind::LessThan => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute < &comparison_attribute
})))
}
SingleComparisonKind::LessThanOrEqualTo => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute <= &comparison_attribute
})))
}
SingleComparisonKind::EqualTo => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute == &comparison_attribute
})))
}
SingleComparisonKind::NotEqualTo => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute != &comparison_attribute
})))
}
SingleComparisonKind::StartsWith => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute.starts_with(&comparison_attribute)
})))
}
SingleComparisonKind::EndsWith => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute.ends_with(&comparison_attribute)
})))
}
SingleComparisonKind::Contains => {
Ok(Box::new(attributes.filter(move |attribute| {
attribute.contains(&comparison_attribute)
})))
}
}
}
#[inline]
fn evaluate_multiple_attributes_comparison_operation<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
comparison_operand: &MultipleAttributesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>> {
let comparison_attributes = comparison_operand.evaluate_backward(medrecord)?;
match kind {
MultipleComparisonKind::IsIn => {
Ok(Box::new(attributes.filter(move |attribute| {
comparison_attributes.contains(attribute)
})))
}
MultipleComparisonKind::IsNotIn => {
Ok(Box::new(attributes.filter(move |attribute| {
!comparison_attributes.contains(attribute)
})))
}
}
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a>(
medrecord: &MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute>,
operand: &SingleAttributeComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<impl Iterator<Item = MedRecordAttribute>>
where
O: 'a,
{
let arithmetic_attribute =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
let attributes = attributes
.map(move |attribute| {
match kind {
BinaryArithmeticKind::Add => attribute.add(arithmetic_attribute.clone()),
BinaryArithmeticKind::Sub => attribute.sub(arithmetic_attribute.clone()),
BinaryArithmeticKind::Mul => {
attribute.clone().mul(arithmetic_attribute.clone())
}
BinaryArithmeticKind::Pow => {
attribute.clone().pow(arithmetic_attribute.clone())
}
BinaryArithmeticKind::Mod => {
attribute.clone().r#mod(arithmetic_attribute.clone())
}
}
.map_err(|_| {
MedRecordError::QueryError(format!(
"Failed arithmetic operation {kind}. Consider narrowing down the attributes using .is_int() or .is_float()",
))
})
});
Ok(attributes.collect::<MedRecordResult<Vec<_>>>()?.into_iter())
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
attributes: impl Iterator<Item = MedRecordAttribute>,
kind: UnaryArithmeticKind,
) -> impl Iterator<Item = MedRecordAttribute>
where
O: 'a,
{
attributes.map(move |attribute| match kind {
UnaryArithmeticKind::Abs => attribute.abs(),
UnaryArithmeticKind::Trim => attribute.trim(),
UnaryArithmeticKind::TrimStart => attribute.trim_start(),
UnaryArithmeticKind::TrimEnd => attribute.trim_end(),
UnaryArithmeticKind::Lowercase => attribute.lowercase(),
UnaryArithmeticKind::Uppercase => attribute.uppercase(),
})
}
#[inline]
fn evaluate_slice(
attributes: impl Iterator<Item = MedRecordAttribute>,
range: Range<usize>,
) -> impl Iterator<Item = MedRecordAttribute> {
attributes.map(move |attribute| attribute.slice(range.clone()))
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
either: &Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
or: &Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let either_attributes = either.evaluate_forward(medrecord, Box::new(attributes_1))?;
let or_attributes = or.evaluate_forward(medrecord, Box::new(attributes_2))?;
Ok(Box::new(
either_attributes
.chain(or_attributes)
.unique_by(|attribute| attribute.clone()),
))
}
#[inline]
fn evaluate_exclude<'a>(
medrecord: &'a MedRecord,
attributes: impl Iterator<Item = MedRecordAttribute> + 'a,
operand: &Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
) -> MedRecordResult<BoxedIterator<'a, MedRecordAttribute>> {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let result: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Ok(Box::new(
attributes_2.filter(move |index| !result.contains(index)),
))
}
}
#[derive(Debug, Clone)]
pub enum SingleAttributeWithIndexOperation<O: RootOperand> {
SingleAttributeComparisonOperation {
operand: SingleAttributeComparisonOperand,
kind: SingleComparisonKind,
},
MultipleAttributesComparisonOperation {
operand: MultipleAttributesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleAttributeComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
EitherOr {
either: Wrapper<SingleAttributeWithIndexOperand<O>>,
or: Wrapper<SingleAttributeWithIndexOperand<O>>,
},
Exclude {
operand: Wrapper<SingleAttributeWithIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleAttributesWithIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for SingleAttributeWithIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::SingleAttributeComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::MultipleAttributesComparisonOperation {
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::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> SingleAttributeWithIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
attribute: Option<(&'a O::Index, MedRecordAttribute)>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let Some(attribute) = attribute else {
return Ok(None);
};
Ok(match self {
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::evaluate_single_attribute_comparison_operation(
medrecord, attribute, operand, kind,
)?
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attribute, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, attribute, operand, kind)?
}
Self::UnaryArithmeticOperation { kind } => {
Some(Self::evaluate_unary_arithmetic_operation(attribute, kind))
}
Self::Slice(range) => Some(Self::evaluate_slice(attribute, range)),
Self::IsString => Self::evaluate_is_string(attribute),
Self::IsInt => Self::evaluate_is_int(attribute),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, attribute, either, or)?
}
Self::Exclude { operand } => {
match operand.evaluate_forward(medrecord, Some(attribute.clone()))? {
Some(_) => None,
None => Some(attribute),
}
}
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
fn evaluate_single_attribute_comparison_operation<'a>(
medrecord: &MedRecord,
attribute: (&'a O::Index, MedRecordAttribute),
comparison_operand: &SingleAttributeComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>> {
let comparison_attribute =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
let comparison_result = match kind {
SingleComparisonKind::GreaterThan => attribute.1 > comparison_attribute,
SingleComparisonKind::GreaterThanOrEqualTo => attribute.1 >= comparison_attribute,
SingleComparisonKind::LessThan => attribute.1 < comparison_attribute,
SingleComparisonKind::LessThanOrEqualTo => attribute.1 <= comparison_attribute,
SingleComparisonKind::EqualTo => attribute.1 == comparison_attribute,
SingleComparisonKind::NotEqualTo => attribute.1 != comparison_attribute,
SingleComparisonKind::StartsWith => attribute.1.starts_with(&comparison_attribute),
SingleComparisonKind::EndsWith => attribute.1.ends_with(&comparison_attribute),
SingleComparisonKind::Contains => attribute.1.contains(&comparison_attribute),
};
Ok(if comparison_result {
Some(attribute)
} else {
None
})
}
#[inline]
fn evaluate_multiple_attributes_comparison_operation<'a>(
medrecord: &MedRecord,
attribute: (&'a O::Index, MedRecordAttribute),
comparison_operand: &MultipleAttributesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>> {
let comparison_attributes = comparison_operand.evaluate_backward(medrecord)?;
let comparison_result = match kind {
MultipleComparisonKind::IsIn => comparison_attributes.contains(&attribute.1),
MultipleComparisonKind::IsNotIn => !comparison_attributes.contains(&attribute.1),
};
Ok(if comparison_result {
Some(attribute)
} else {
None
})
}
#[inline]
fn evaluate_binary_arithmetic_operation<'a>(
medrecord: &MedRecord,
attribute: (&'a O::Index, MedRecordAttribute),
operand: &SingleAttributeComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>> {
let arithmetic_attribute =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
Ok(Some(match kind {
BinaryArithmeticKind::Add => (attribute.0, attribute.1.add(arithmetic_attribute)?),
BinaryArithmeticKind::Sub => (attribute.0, attribute.1.sub(arithmetic_attribute)?),
BinaryArithmeticKind::Mul => (attribute.0, attribute.1.mul(arithmetic_attribute)?),
BinaryArithmeticKind::Pow => (attribute.0, attribute.1.pow(arithmetic_attribute)?),
BinaryArithmeticKind::Mod => (attribute.0, attribute.1.r#mod(arithmetic_attribute)?),
}))
}
#[inline]
fn evaluate_unary_arithmetic_operation<'a>(
attribute: (&'a O::Index, MedRecordAttribute),
kind: &UnaryArithmeticKind,
) -> (&'a O::Index, MedRecordAttribute) {
match kind {
UnaryArithmeticKind::Abs => (attribute.0, attribute.1.abs()),
UnaryArithmeticKind::Trim => (attribute.0, attribute.1.trim()),
UnaryArithmeticKind::TrimStart => (attribute.0, attribute.1.trim_start()),
UnaryArithmeticKind::TrimEnd => (attribute.0, attribute.1.trim_end()),
UnaryArithmeticKind::Lowercase => (attribute.0, attribute.1.lowercase()),
UnaryArithmeticKind::Uppercase => (attribute.0, attribute.1.uppercase()),
}
}
#[inline]
fn evaluate_slice<'a>(
attribute: (&'a O::Index, MedRecordAttribute),
range: &Range<usize>,
) -> (&'a O::Index, MedRecordAttribute) {
(attribute.0, attribute.1.slice(range.clone()))
}
#[inline]
fn evaluate_is_string(
attribute: (&O::Index, MedRecordAttribute),
) -> Option<(&O::Index, MedRecordAttribute)> {
match attribute.1 {
MedRecordAttribute::String(_) => Some(attribute),
_ => None,
}
}
#[inline]
fn evaluate_is_int(
attribute: (&O::Index, MedRecordAttribute),
) -> Option<(&O::Index, MedRecordAttribute)> {
match attribute.1 {
MedRecordAttribute::Int(_) => Some(attribute),
_ => None,
}
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
attribute: (&'a O::Index, MedRecordAttribute),
either: &Wrapper<SingleAttributeWithIndexOperand<O>>,
or: &Wrapper<SingleAttributeWithIndexOperand<O>>,
) -> MedRecordResult<Option<(&'a O::Index, MedRecordAttribute)>>
where
O: 'a,
{
let either_result = either.evaluate_forward(medrecord, Some(attribute.clone()))?;
let or_result = or.evaluate_forward(medrecord, Some(attribute))?;
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> SingleAttributeWithIndexOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
Ok(match self {
Self::SingleAttributeComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_single_attribute_comparison_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleAttributesComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_binary_arithmetic_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(
key,
Some(Self::evaluate_unary_arithmetic_operation(attribute, &kind)),
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Some(Self::evaluate_slice(attribute, &range)))
}))
}
Self::IsString => Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Self::evaluate_is_string(attribute))
})),
Self::IsInt => Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Self::evaluate_is_int(attribute))
})),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, attributes, operand)?
}
Self::Merge { operand } => {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let attributes_1 = attributes_1.filter_map(|(_, value)| value);
let attributes_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Box::new(attributes_2.map(move |(key, attribute)| {
let attribute = attribute.filter(|value| attributes_1.contains(value));
(key, attribute)
}))
}
})
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>,
either: &Wrapper<SingleAttributeWithIndexOperand<O>>,
or: &Wrapper<SingleAttributeWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let either_attributes =
either.evaluate_forward_grouped(medrecord, Box::new(attributes_1))?;
let mut or_attributes: Vec<_> = or
.evaluate_forward_grouped(medrecord, Box::new(attributes_2))?
.collect();
let attributes = either_attributes.map(move |(key, either_attribute)| {
let attribute_position = or_attributes
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_attribute = or_attributes.remove(attribute_position).1;
let attribute = match (either_attribute, or_attribute) {
(Some(either_result), _) => Some(either_result),
(None, Some(or_result)) => Some(or_result),
_ => None,
};
(key, attribute)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>,
operand: &Wrapper<SingleAttributeWithIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<(&'a O::Index, MedRecordAttribute)>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(values);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(attributes_1))?
.collect();
let attributes = attributes_2.map(move |(key, attribute)| {
let attribute_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_attribute = result.remove(attribute_position).1;
let attribute = match excluded_attribute {
Some(_) => None,
None => attribute,
};
(key, attribute)
});
Ok(Box::new(attributes))
}
}
#[derive(Debug, Clone)]
pub enum SingleAttributeWithoutIndexOperation<O: RootOperand> {
SingleAttributeComparisonOperation {
operand: SingleAttributeComparisonOperand,
kind: SingleComparisonKind,
},
MultipleAttributesComparisonOperation {
operand: MultipleAttributesComparisonOperand,
kind: MultipleComparisonKind,
},
BinaryArithmeticOperation {
operand: SingleAttributeComparisonOperand,
kind: BinaryArithmeticKind,
},
UnaryArithmeticOperation {
kind: UnaryArithmeticKind,
},
Slice(Range<usize>),
IsString,
IsInt,
EitherOr {
either: Wrapper<SingleAttributeWithoutIndexOperand<O>>,
or: Wrapper<SingleAttributeWithoutIndexOperand<O>>,
},
Exclude {
operand: Wrapper<SingleAttributeWithoutIndexOperand<O>>,
},
Merge {
operand: Wrapper<MultipleAttributesWithoutIndexOperand<O>>,
},
}
impl<O: RootOperand> DeepClone for SingleAttributeWithoutIndexOperation<O> {
fn deep_clone(&self) -> Self {
match self {
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::SingleAttributeComparisonOperation {
operand: operand.deep_clone(),
kind: kind.clone(),
}
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::MultipleAttributesComparisonOperation {
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::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> SingleAttributeWithoutIndexOperation<O> {
pub(crate) fn evaluate<'a>(
&self,
medrecord: &'a MedRecord,
attribute: Option<MedRecordAttribute>,
) -> MedRecordResult<Option<MedRecordAttribute>>
where
O: 'a,
{
let Some(attribute) = attribute else {
return Ok(None);
};
Ok(match self {
Self::SingleAttributeComparisonOperation { operand, kind } => {
Self::evaluate_single_attribute_comparison_operation(
medrecord, attribute, operand, kind,
)?
}
Self::MultipleAttributesComparisonOperation { operand, kind } => {
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attribute, operand, kind,
)?
}
Self::BinaryArithmeticOperation { operand, kind } => {
Self::evaluate_binary_arithmetic_operation(medrecord, attribute, operand, kind)?
}
Self::UnaryArithmeticOperation { kind } => {
Some(Self::evaluate_unary_arithmetic_operation(attribute, kind))
}
Self::Slice(range) => Some(Self::evaluate_slice(attribute, range)),
Self::IsString => Self::evaluate_is_string(attribute),
Self::IsInt => Self::evaluate_is_int(attribute),
Self::EitherOr { either, or } => {
Self::evaluate_either_or(medrecord, attribute, either, or)?
}
Self::Exclude { operand } => {
match operand.evaluate_forward(medrecord, Some(attribute.clone()))? {
Some(_) => None,
None => Some(attribute),
}
}
Self::Merge { operand: _ } => unreachable!(),
})
}
#[inline]
fn evaluate_single_attribute_comparison_operation(
medrecord: &MedRecord,
attribute: MedRecordAttribute,
comparison_operand: &SingleAttributeComparisonOperand,
kind: &SingleComparisonKind,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let comparison_attribute =
comparison_operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
let comparison_result = match kind {
SingleComparisonKind::GreaterThan => attribute > comparison_attribute,
SingleComparisonKind::GreaterThanOrEqualTo => attribute >= comparison_attribute,
SingleComparisonKind::LessThan => attribute < comparison_attribute,
SingleComparisonKind::LessThanOrEqualTo => attribute <= comparison_attribute,
SingleComparisonKind::EqualTo => attribute == comparison_attribute,
SingleComparisonKind::NotEqualTo => attribute != comparison_attribute,
SingleComparisonKind::StartsWith => attribute.starts_with(&comparison_attribute),
SingleComparisonKind::EndsWith => attribute.ends_with(&comparison_attribute),
SingleComparisonKind::Contains => attribute.contains(&comparison_attribute),
};
Ok(if comparison_result {
Some(attribute)
} else {
None
})
}
#[inline]
fn evaluate_multiple_attributes_comparison_operation(
medrecord: &MedRecord,
attribute: MedRecordAttribute,
comparison_operand: &MultipleAttributesComparisonOperand,
kind: &MultipleComparisonKind,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let comparison_attributes = comparison_operand.evaluate_backward(medrecord)?;
let comparison_result = match kind {
MultipleComparisonKind::IsIn => comparison_attributes.contains(&attribute),
MultipleComparisonKind::IsNotIn => !comparison_attributes.contains(&attribute),
};
Ok(if comparison_result {
Some(attribute)
} else {
None
})
}
#[inline]
fn evaluate_binary_arithmetic_operation(
medrecord: &MedRecord,
attribute: MedRecordAttribute,
operand: &SingleAttributeComparisonOperand,
kind: &BinaryArithmeticKind,
) -> MedRecordResult<Option<MedRecordAttribute>> {
let arithmetic_attribute =
operand
.evaluate_backward(medrecord)?
.ok_or(MedRecordError::QueryError(
"No attribute to compare".to_string(),
))?;
Ok(Some(match kind {
BinaryArithmeticKind::Add => attribute.add(arithmetic_attribute)?,
BinaryArithmeticKind::Sub => attribute.sub(arithmetic_attribute)?,
BinaryArithmeticKind::Mul => attribute.mul(arithmetic_attribute)?,
BinaryArithmeticKind::Pow => attribute.pow(arithmetic_attribute)?,
BinaryArithmeticKind::Mod => attribute.r#mod(arithmetic_attribute)?,
}))
}
#[inline]
fn evaluate_unary_arithmetic_operation(
attribute: MedRecordAttribute,
kind: &UnaryArithmeticKind,
) -> MedRecordAttribute {
match kind {
UnaryArithmeticKind::Abs => attribute.abs(),
UnaryArithmeticKind::Trim => attribute.trim(),
UnaryArithmeticKind::TrimStart => attribute.trim_start(),
UnaryArithmeticKind::TrimEnd => attribute.trim_end(),
UnaryArithmeticKind::Lowercase => attribute.lowercase(),
UnaryArithmeticKind::Uppercase => attribute.uppercase(),
}
}
#[inline]
fn evaluate_slice(attribute: MedRecordAttribute, range: &Range<usize>) -> MedRecordAttribute {
attribute.slice(range.clone())
}
#[inline]
fn evaluate_is_string(attribute: MedRecordAttribute) -> Option<MedRecordAttribute> {
match attribute {
MedRecordAttribute::String(_) => Some(attribute),
_ => None,
}
}
#[inline]
fn evaluate_is_int(attribute: MedRecordAttribute) -> Option<MedRecordAttribute> {
match attribute {
MedRecordAttribute::Int(_) => Some(attribute),
_ => None,
}
}
#[inline]
fn evaluate_either_or<'a>(
medrecord: &'a MedRecord,
attribute: MedRecordAttribute,
either: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
or: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<Option<MedRecordAttribute>>
where
O: 'a,
{
let either_result = either.evaluate_forward(medrecord, Some(attribute.clone()))?;
let or_result = or.evaluate_forward(medrecord, Some(attribute))?;
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> SingleAttributeWithoutIndexOperation<O> {
#[allow(clippy::type_complexity)]
pub(crate) fn evaluate_grouped<'a>(
&self,
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, Option<MedRecordAttribute>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordAttribute>>>
where
O: 'a,
{
Ok(match self {
Self::SingleAttributeComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_single_attribute_comparison_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::MultipleAttributesComparisonOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_multiple_attributes_comparison_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::BinaryArithmeticOperation { operand, kind } => Box::new(
attributes
.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return Ok((key, None));
};
Ok((
key,
Self::evaluate_binary_arithmetic_operation(
medrecord, attribute, operand, kind,
)?,
))
})
.collect::<MedRecordResult<Vec<_>>>()?
.into_iter(),
),
Self::UnaryArithmeticOperation { kind } => {
let kind = kind.clone();
Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(
key,
Some(Self::evaluate_unary_arithmetic_operation(attribute, &kind)),
)
}))
}
Self::Slice(range) => {
let range = range.clone();
Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Some(Self::evaluate_slice(attribute, &range)))
}))
}
Self::IsString => Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Self::evaluate_is_string(attribute))
})),
Self::IsInt => Box::new(attributes.map(move |(key, attribute)| {
let Some(attribute) = attribute else {
return (key, None);
};
(key, Self::evaluate_is_int(attribute))
})),
Self::EitherOr { either, or } => {
Self::evaluate_either_or_grouped(medrecord, attributes, either, or)?
}
Self::Exclude { operand } => {
Self::evaluate_exclude_grouped(medrecord, attributes, operand)?
}
Self::Merge { operand } => {
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let attributes_1 = attributes_1.filter_map(|(_, attribute)| attribute);
let attributes_1: MrHashSet<_> = operand
.evaluate_forward(medrecord, Box::new(attributes_1))?
.collect();
Box::new(attributes_2.map(move |(key, attribute)| {
let attribute = attribute.filter(|attribute| attributes_1.contains(attribute));
(key, attribute)
}))
}
})
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_either_or_grouped<'a>(
medrecord: &'a MedRecord,
attributes: GroupedIterator<'a, Option<MedRecordAttribute>>,
either: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
or: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordAttribute>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(attributes);
let either_attributes =
either.evaluate_forward_grouped(medrecord, Box::new(attributes_1))?;
let mut or_attributes: Vec<_> = or
.evaluate_forward_grouped(medrecord, Box::new(attributes_2))?
.collect();
let attributes = either_attributes.map(move |(key, either_attribute)| {
let attribute_position = or_attributes
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let or_attribute = or_attributes.remove(attribute_position).1;
let attribute = match (either_attribute, or_attribute) {
(Some(either_result), _) => Some(either_result),
(None, Some(or_result)) => Some(or_result),
_ => None,
};
(key, attribute)
});
Ok(Box::new(attributes))
}
#[allow(clippy::type_complexity)]
#[inline]
fn evaluate_exclude_grouped<'a>(
medrecord: &'a MedRecord,
values: GroupedIterator<'a, Option<MedRecordAttribute>>,
operand: &Wrapper<SingleAttributeWithoutIndexOperand<O>>,
) -> MedRecordResult<GroupedIterator<'a, Option<MedRecordAttribute>>>
where
O: 'a,
{
let (attributes_1, attributes_2) = Itertools::tee(values);
let mut result: Vec<_> = operand
.evaluate_forward_grouped(medrecord, Box::new(attributes_1))?
.collect();
let attributes = attributes_2.map(move |(key, attribute)| {
let attribute_position = result
.iter()
.position(|(k, _)| k == &key)
.expect("Entry must exist");
let excluded_attribute = result.remove(attribute_position).1;
let attribute = match excluded_attribute {
Some(_) => None,
None => attribute,
};
(key, attribute)
});
Ok(Box::new(attributes))
}
}