From 74fdc266c4f3cfe2b91f20e520680c054e87c52f Mon Sep 17 00:00:00 2001 From: Jakob Kraus Date: Thu, 17 Oct 2024 15:12:14 +0200 Subject: [PATCH] feat: implement exclude operation --- .../medrecord/querying/attributes/operand.rs | 59 +++++++++++++++ .../querying/attributes/operation.rs | 68 ++++++++++++++++- .../src/medrecord/querying/edges/operand.rs | 56 ++++++++++++++ .../src/medrecord/querying/edges/operation.rs | 41 +++++++++++ .../src/medrecord/querying/nodes/operand.rs | 56 ++++++++++++++ .../src/medrecord/querying/nodes/operation.rs | 49 ++++++++++++- .../src/medrecord/querying/values/operand.rs | 40 ++++++++++ .../medrecord/querying/values/operation.rs | 36 +++++++++ medmodels/_medmodels.pyi | 11 +++ medmodels/medrecord/querying.py | 73 +++++++++++++++++++ .../src/medrecord/querying/attributes.rs | 24 ++++++ rustmodels/src/medrecord/querying/edges.rs | 24 ++++++ rustmodels/src/medrecord/querying/nodes.rs | 24 ++++++ rustmodels/src/medrecord/querying/values.rs | 16 ++++ 14 files changed, 573 insertions(+), 4 deletions(-) diff --git a/crates/medmodels-core/src/medrecord/querying/attributes/operand.rs b/crates/medmodels-core/src/medrecord/querying/attributes/operand.rs index 83af4393..f53b287d 100644 --- a/crates/medmodels-core/src/medrecord/querying/attributes/operand.rs +++ b/crates/medmodels-core/src/medrecord/querying/attributes/operand.rs @@ -321,6 +321,18 @@ impl AttributesTreeOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(self.context.clone()); + + query(&mut operand); + + self.operations + .push(AttributesTreeOperation::Exclude { operand }); + } } impl Wrapper { @@ -407,6 +419,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query) + } } #[derive(Debug, Clone)] @@ -575,6 +594,19 @@ impl MultipleAttributesOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = + Wrapper::::new(self.context.clone(), self.kind.clone()); + + query(&mut operand); + + self.operations + .push(MultipleAttributesOperation::Exclude { operand }); + } } impl Wrapper { @@ -663,6 +695,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query) + } } #[derive(Debug, Clone)] @@ -794,6 +833,19 @@ impl SingleAttributeOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = + Wrapper::::new(self.context.clone(), self.kind.clone()); + + query(&mut operand); + + self.operations + .push(SingleAttributeOperation::Exclude { operand }); + } } impl Wrapper { @@ -871,4 +923,11 @@ impl Wrapper { { self.0.write_or_panic().eiter_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } diff --git a/crates/medmodels-core/src/medrecord/querying/attributes/operation.rs b/crates/medmodels-core/src/medrecord/querying/attributes/operation.rs index d1c55f7e..f498d570 100644 --- a/crates/medmodels-core/src/medrecord/querying/attributes/operation.rs +++ b/crates/medmodels-core/src/medrecord/querying/attributes/operation.rs @@ -26,7 +26,7 @@ use crate::{ use itertools::Itertools; use std::{ cmp::Ordering, - collections::HashMap, + collections::{HashMap, HashSet}, fmt::Display, hash::Hash, ops::{Add, Mul, Range, Sub}, @@ -119,6 +119,9 @@ pub enum AttributesTreeOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for AttributesTreeOperation { @@ -155,6 +158,9 @@ impl DeepClone for AttributesTreeOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -221,6 +227,7 @@ impl AttributesTreeOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, attributes, either, or) } + Self::Exclude { operand } => Self::evaluate_exclude(medrecord, attributes, operand), } } @@ -670,6 +677,26 @@ impl AttributesTreeOperation { .unique_by(|attribute| attribute.0), )) } + + #[inline] + fn evaluate_exclude<'a, T: 'a + Eq + Hash + GetAttributes + Display>( + medrecord: &'a MedRecord, + attributes: impl Iterator)>, + operand: &Wrapper, + ) -> MedRecordResult)>> { + let attributes = attributes.collect::>(); + + let result = operand + .evaluate(medrecord, attributes.clone().into_iter())? + .map(|(index, _)| index) + .collect::>(); + + Ok(Box::new( + attributes + .into_iter() + .filter(move |(index, _)| !result.contains(index)), + )) + } } #[derive(Debug, Clone)] @@ -709,6 +736,9 @@ pub enum MultipleAttributesOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for MultipleAttributesOperation { @@ -748,6 +778,9 @@ impl DeepClone for MultipleAttributesOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -805,6 +838,7 @@ impl MultipleAttributesOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, attributes, either, or) } + Self::Exclude { operand } => Self::evaluate_exclude(medrecord, attributes, operand), } } @@ -1151,6 +1185,26 @@ impl MultipleAttributesOperation { .unique_by(|attribute| attribute.0), )) } + + #[inline] + fn evaluate_exclude<'a, T: 'a + Eq + Hash + GetAttributes + Display>( + medrecord: &'a MedRecord, + attributes: impl Iterator, + operand: &Wrapper, + ) -> MedRecordResult> { + let attributes = attributes.collect::>(); + + let result = operand + .evaluate(medrecord, attributes.clone().into_iter())? + .map(|(index, _)| index) + .collect::>(); + + Ok(Box::new( + attributes + .into_iter() + .filter(move |(index, _)| !result.contains(index)), + )) + } } #[derive(Debug, Clone)] @@ -1180,6 +1234,9 @@ pub enum SingleAttributeOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for SingleAttributeOperation { @@ -1211,6 +1268,9 @@ impl DeepClone for SingleAttributeOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -1255,6 +1315,12 @@ impl SingleAttributeOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, attribute, either, or) } + Self::Exclude { operand } => { + Ok(match operand.evaluate(medrecord, attribute.clone())? { + Some(_) => None, + None => Some(attribute), + }) + } } } diff --git a/crates/medmodels-core/src/medrecord/querying/edges/operand.rs b/crates/medmodels-core/src/medrecord/querying/edges/operand.rs index f2c50513..26583415 100644 --- a/crates/medmodels-core/src/medrecord/querying/edges/operand.rs +++ b/crates/medmodels-core/src/medrecord/querying/edges/operand.rs @@ -145,6 +145,17 @@ impl EdgeOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(); + + query(&mut operand); + + self.operations.push(EdgeOperation::Exclude { operand }); + } } impl Wrapper { @@ -203,6 +214,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } macro_rules! implement_index_operation { @@ -450,6 +468,18 @@ impl EdgeIndicesOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(self.context.clone()); + + query(&mut operand); + + self.operations + .push(EdgeIndicesOperation::Exclude { operand }); + } } impl Wrapper { @@ -505,6 +535,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } #[derive(Debug, Clone)] @@ -607,6 +644,18 @@ impl EdgeIndexOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(self.context.clone(), self.kind.clone()); + + query(&mut operand); + + self.operations + .push(EdgeIndexOperation::Exclude { operand }); + } } impl Wrapper { @@ -652,4 +701,11 @@ impl Wrapper { { self.0.write_or_panic().eiter_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } diff --git a/crates/medmodels-core/src/medrecord/querying/edges/operation.rs b/crates/medmodels-core/src/medrecord/querying/edges/operation.rs index 9c710e43..db743221 100644 --- a/crates/medmodels-core/src/medrecord/querying/edges/operation.rs +++ b/crates/medmodels-core/src/medrecord/querying/edges/operation.rs @@ -58,6 +58,9 @@ pub enum EdgeOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for EdgeOperation { @@ -88,6 +91,9 @@ impl DeepClone for EdgeOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -137,6 +143,11 @@ impl EdgeOperation { Self::EitherOr { either, or } => { Box::new(Self::evaluate_either_or(medrecord, either, or)?) } + Self::Exclude { operand } => { + let result = operand.evaluate(medrecord)?.collect::>(); + + Box::new(edge_indices.filter(move |node_index| !result.contains(node_index))) + } }) } @@ -371,6 +382,9 @@ pub enum EdgeIndicesOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for EdgeIndicesOperation { @@ -401,6 +415,9 @@ impl DeepClone for EdgeIndicesOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -442,6 +459,19 @@ impl EdgeIndicesOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, indices, either, or) } + Self::Exclude { operand } => { + let edge_indices = indices.collect::>(); + + let result = operand + .evaluate(medrecord, edge_indices.clone().into_iter())? + .collect::>(); + + Ok(Box::new( + edge_indices + .into_iter() + .filter(move |index| !result.contains(index)), + )) + } } } @@ -629,6 +659,9 @@ pub enum EdgeIndexOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for EdgeIndexOperation { @@ -654,6 +687,9 @@ impl DeepClone for EdgeIndexOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -675,6 +711,11 @@ impl EdgeIndexOperation { Self::evaluate_binary_arithmetic_operation(medrecord, index, operand, kind) } Self::EitherOr { either, or } => Self::evaluate_either_or(medrecord, index, either, or), + Self::Exclude { operand } => { + let result = operand.evaluate(medrecord, index.clone())?.is_some(); + + Ok(if result { None } else { Some(index) }) + } } } diff --git a/crates/medmodels-core/src/medrecord/querying/nodes/operand.rs b/crates/medmodels-core/src/medrecord/querying/nodes/operand.rs index 17eccb6d..329dc8f7 100644 --- a/crates/medmodels-core/src/medrecord/querying/nodes/operand.rs +++ b/crates/medmodels-core/src/medrecord/querying/nodes/operand.rs @@ -157,6 +157,17 @@ impl NodeOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(); + + query(&mut operand); + + self.operations.push(NodeOperation::Exclude { operand }); + } } impl Wrapper { @@ -219,6 +230,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } macro_rules! implement_index_operation { @@ -490,6 +508,18 @@ impl NodeIndicesOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(self.context.clone()); + + query(&mut operand); + + self.operations + .push(NodeIndicesOperation::Exclude { operand }); + } } impl Wrapper { @@ -558,6 +588,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } #[derive(Debug, Clone)] @@ -673,6 +710,18 @@ impl NodeIndexOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = Wrapper::::new(self.context.clone(), self.kind.clone()); + + query(&mut operand); + + self.operations + .push(NodeIndexOperation::Exclude { operand }); + } } impl Wrapper { @@ -732,4 +781,11 @@ impl Wrapper { { self.0.write_or_panic().eiter_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } diff --git a/crates/medmodels-core/src/medrecord/querying/nodes/operation.rs b/crates/medmodels-core/src/medrecord/querying/nodes/operation.rs index ce214f43..7db9544b 100644 --- a/crates/medmodels-core/src/medrecord/querying/nodes/operation.rs +++ b/crates/medmodels-core/src/medrecord/querying/nodes/operation.rs @@ -74,6 +74,9 @@ pub enum NodeOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for NodeOperation { @@ -111,6 +114,9 @@ impl DeepClone for NodeOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -168,10 +174,17 @@ impl NodeOperation { )?), Self::EitherOr { either, or } => { // TODO: This is a temporary solution. It should be optimized. - let either_result = either.evaluate(medrecord)?.collect::>(); - let or_result = or.evaluate(medrecord)?.collect::>(); + let either_result = either.evaluate(medrecord)?.collect::>(); + let or_result = or.evaluate(medrecord)?.collect::>(); + + Box::new(node_indices.filter(move |node_index| { + either_result.contains(node_index) || or_result.contains(node_index) + })) + } + Self::Exclude { operand } => { + let result = operand.evaluate(medrecord)?.collect::>(); - Box::new(either_result.into_iter().chain(or_result).unique()) + Box::new(node_indices.filter(move |node_index| !result.contains(node_index))) } }) } @@ -439,6 +452,9 @@ pub enum NodeIndicesOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for NodeIndicesOperation { @@ -475,6 +491,9 @@ impl DeepClone for NodeIndicesOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -530,6 +549,19 @@ impl NodeIndicesOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, indices, either, or) } + Self::Exclude { operand } => { + let node_indices = indices.collect::>(); + + let result = operand + .evaluate(medrecord, node_indices.clone().into_iter())? + .collect::>(); + + Ok(Box::new( + node_indices + .into_iter() + .filter(move |index| !result.contains(index)), + )) + } } } @@ -816,6 +848,9 @@ pub enum NodeIndexOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for NodeIndexOperation { @@ -847,6 +882,9 @@ impl DeepClone for NodeIndexOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -885,6 +923,11 @@ impl NodeIndexOperation { _ => None, }), Self::EitherOr { either, or } => Self::evaluate_either_or(medrecord, index, either, or), + Self::Exclude { operand } => { + let result = operand.evaluate(medrecord, index.clone())?.is_some(); + + Ok(if result { None } else { Some(index) }) + } } } diff --git a/crates/medmodels-core/src/medrecord/querying/values/operand.rs b/crates/medmodels-core/src/medrecord/querying/values/operand.rs index 01796ed7..087b3261 100644 --- a/crates/medmodels-core/src/medrecord/querying/values/operand.rs +++ b/crates/medmodels-core/src/medrecord/querying/values/operand.rs @@ -311,6 +311,19 @@ impl MultipleValuesOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = + Wrapper::::new(self.context.clone(), self.attribute.clone()); + + query(&mut operand); + + self.operations + .push(MultipleValuesOperation::Exclude { operand }); + } } impl Wrapper { @@ -393,6 +406,13 @@ impl Wrapper { { self.0.write_or_panic().either_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } #[derive(Debug, Clone)] @@ -519,6 +539,19 @@ impl SingleValueOperand { or: or_operand, }); } + + pub fn exclude(&mut self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + let mut operand = + Wrapper::::new(self.context.clone(), self.kind.clone()); + + query(&mut operand); + + self.operations + .push(SingleValueOperation::Exclude { operand }); + } } impl Wrapper { @@ -587,4 +620,11 @@ impl Wrapper { { self.0.write_or_panic().eiter_or(either_query, or_query); } + + pub fn exclude(&self, query: Q) + where + Q: FnOnce(&mut Wrapper), + { + self.0.write_or_panic().exclude(query); + } } diff --git a/crates/medmodels-core/src/medrecord/querying/values/operation.rs b/crates/medmodels-core/src/medrecord/querying/values/operation.rs index fd7409c0..80732c60 100644 --- a/crates/medmodels-core/src/medrecord/querying/values/operation.rs +++ b/crates/medmodels-core/src/medrecord/querying/values/operation.rs @@ -24,6 +24,7 @@ use crate::{ use itertools::Itertools; use std::{ cmp::Ordering, + collections::HashSet, hash::Hash, ops::{Add, Div, Mul, Range, Sub}, }; @@ -128,6 +129,9 @@ pub enum MultipleValuesOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for MultipleValuesOperation { @@ -168,6 +172,9 @@ impl DeepClone for MultipleValuesOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -240,6 +247,7 @@ impl MultipleValuesOperation { Self::EitherOr { either, or } => { Self::evaluate_either_or(medrecord, values, either, or) } + Self::Exclude { operand } => Self::evaluate_exclude(medrecord, values, operand), } } @@ -722,6 +730,24 @@ impl MultipleValuesOperation { either_values.chain(or_values).unique_by(|value| value.0), )) } + + #[inline] + fn evaluate_exclude<'a, T: 'a + Eq + Hash>( + medrecord: &'a MedRecord, + values: impl Iterator, + operand: &Wrapper, + ) -> MedRecordResult> { + let values = values.collect::>(); + + let result = operand + .evaluate(medrecord, values.clone().into_iter())? + .map(|(t, _)| t) + .collect::>(); + + Ok(Box::new( + values.into_iter().filter(move |(t, _)| !result.contains(t)), + )) + } } #[derive(Debug, Clone)] @@ -755,6 +781,9 @@ pub enum SingleValueOperation { either: Wrapper, or: Wrapper, }, + Exclude { + operand: Wrapper, + }, } impl DeepClone for SingleValueOperation { @@ -790,6 +819,9 @@ impl DeepClone for SingleValueOperation { either: either.deep_clone(), or: or.deep_clone(), }, + Self::Exclude { operand } => Self::Exclude { + operand: operand.deep_clone(), + }, } } } @@ -848,6 +880,10 @@ impl SingleValueOperation { _ => None, }), Self::EitherOr { either, or } => Self::evaluate_either_or(medrecord, value, either, or), + Self::Exclude { operand } => Ok(match operand.evaluate(medrecord, value.clone())? { + Some(_) => None, + None => Some(value), + }), } } diff --git a/medmodels/_medmodels.pyi b/medmodels/_medmodels.pyi index b4b4dd06..55f090bb 100644 --- a/medmodels/_medmodels.pyi +++ b/medmodels/_medmodels.pyi @@ -256,6 +256,7 @@ class PyNodeOperand: either: Callable[[PyNodeOperand], None], or_: Callable[[PyNodeOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyNodeOperand], None]) -> None: ... def deep_clone(self) -> PyNodeOperand: ... PyNodeIndexComparisonOperand: TypeAlias = Union[NodeIndex, PyNodeIndexOperand] @@ -301,6 +302,7 @@ class PyNodeIndicesOperand: either: Callable[[PyNodeIndicesOperand], None], or_: Callable[[PyNodeIndicesOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyNodeIndicesOperand], None]) -> None: ... def deep_clone(self) -> PyNodeIndicesOperand: ... class PyNodeIndexOperand: @@ -334,6 +336,7 @@ class PyNodeIndexOperand: either: Callable[[PyNodeIndexOperand], None], or_: Callable[[PyNodeIndexOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyNodeIndexOperand], None]) -> None: ... def deep_clone(self) -> PyNodeIndexOperand: ... class PyEdgeOperand: @@ -351,6 +354,7 @@ class PyEdgeOperand: either: Callable[[PyEdgeOperand], None], or_: Callable[[PyEdgeOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyEdgeOperand], None]) -> None: ... def deep_clone(self) -> PyEdgeOperand: ... PyEdgeIndexComparisonOperand: TypeAlias = Union[EdgeIndex, PyEdgeIndexOperand] @@ -387,6 +391,7 @@ class PyEdgeIndicesOperand: either: Callable[[PyEdgeIndicesOperand], None], or_: Callable[[PyEdgeIndicesOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyEdgeIndicesOperand], None]) -> None: ... def deep_clone(self) -> PyEdgeIndicesOperand: ... class PyEdgeIndexOperand: @@ -411,6 +416,7 @@ class PyEdgeIndexOperand: either: Callable[[PyEdgeIndexOperand], None], or_: Callable[[PyEdgeIndexOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyEdgeIndexOperand], None]) -> None: ... def deep_clone(self) -> PyEdgeIndexOperand: ... PySingleValueComparisonOperand: TypeAlias = Union[MedRecordValue, PySingleValueOperand] @@ -474,6 +480,7 @@ class PyMultipleValuesOperand: either: Callable[[PyMultipleValuesOperand], None], or_: Callable[[PyMultipleValuesOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyMultipleValuesOperand], None]) -> None: ... def deep_clone(self) -> PyMultipleValuesOperand: ... class PySingleValueOperand: @@ -518,6 +525,7 @@ class PySingleValueOperand: either: Callable[[PySingleValueOperand], None], or_: Callable[[PySingleValueOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PySingleValueOperand], None]) -> None: ... def deep_clone(self) -> PySingleValueOperand: ... PySingleAttributeComparisonOperand: TypeAlias = Union[ @@ -571,6 +579,7 @@ class PyAttributesTreeOperand: either: Callable[[PyAttributesTreeOperand], None], or_: Callable[[PyAttributesTreeOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyAttributesTreeOperand], None]) -> None: ... def deep_clone(self) -> PyAttributesTreeOperand: ... class PyMultipleAttributesOperand: @@ -617,6 +626,7 @@ class PyMultipleAttributesOperand: either: Callable[[PyMultipleAttributesOperand], None], or_: Callable[[PyMultipleAttributesOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PyMultipleAttributesOperand], None]) -> None: ... def deep_clone(self) -> PyMultipleAttributesOperand: ... class PySingleAttributeOperand: @@ -654,4 +664,5 @@ class PySingleAttributeOperand: either: Callable[[PySingleAttributeOperand], None], or_: Callable[[PySingleAttributeOperand], None], ) -> None: ... + def exclude(self, query: Callable[[PySingleAttributeOperand], None]) -> None: ... def deep_clone(self) -> PySingleAttributeOperand: ... diff --git a/medmodels/medrecord/querying.py b/medmodels/medrecord/querying.py index 5794c6b2..f274dd52 100644 --- a/medmodels/medrecord/querying.py +++ b/medmodels/medrecord/querying.py @@ -192,6 +192,11 @@ def either_or(self, either: NodeQuery, or_: NodeQuery) -> None: lambda node: or_(NodeOperand._from_py_node_operand(node)), ) + def exclude(self, query: NodeQuery) -> None: + self._node_operand.exclude( + lambda node: query(NodeOperand._from_py_node_operand(node)) + ) + def clone(self) -> NodeOperand: return NodeOperand._from_py_node_operand(self._node_operand.deep_clone()) @@ -238,6 +243,11 @@ def either_or(self, either: EdgeQuery, or_: EdgeQuery) -> None: lambda edge: or_(EdgeOperand._from_py_edge_operand(edge)), ) + def exclude(self, query: EdgeQuery) -> None: + self._edge_operand.exclude( + lambda edge: query(EdgeOperand._from_py_edge_operand(edge)) + ) + def clone(self) -> EdgeOperand: return EdgeOperand._from_py_edge_operand(self._edge_operand.deep_clone()) @@ -496,6 +506,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[MultipleValuesOperand], None]) -> None: + self._multiple_values_operand.exclude( + lambda values: query( + MultipleValuesOperand._from_py_multiple_values_operand(values) + ) + ) + def clone(self) -> MultipleValuesOperand: return MultipleValuesOperand._from_py_multiple_values_operand( self._multiple_values_operand.deep_clone() @@ -688,6 +705,11 @@ def either_or( lambda value: or_(SingleValueOperand._from_py_single_value_operand(value)), ) + def exclude(self, query: Callable[[SingleValueOperand], None]) -> None: + self._single_value_operand.exclude( + lambda value: query(SingleValueOperand._from_py_single_value_operand(value)) + ) + def clone(self) -> SingleValueOperand: return SingleValueOperand._from_py_single_value_operand( self._single_value_operand.deep_clone() @@ -898,6 +920,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[AttributesTreeOperand], None]) -> None: + self._attributes_tree_operand.exclude( + lambda attributes: query( + AttributesTreeOperand._from_py_attributes_tree_operand(attributes) + ) + ) + def clone(self) -> AttributesTreeOperand: return AttributesTreeOperand._from_py_attributes_tree_operand( self._attributes_tree_operand.deep_clone() @@ -1117,6 +1146,15 @@ def either_or( ), ) + def exclude(self, query: Callable[[MultipleAttributesOperand], None]) -> None: + self._multiple_attributes_operand.exclude( + lambda attributes: query( + MultipleAttributesOperand._from_py_multiple_attributes_operand( + attributes + ) + ) + ) + def clone(self) -> MultipleAttributesOperand: return MultipleAttributesOperand._from_py_multiple_attributes_operand( self._multiple_attributes_operand.deep_clone() @@ -1293,6 +1331,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[SingleAttributeOperand], None]) -> None: + self._single_attribute_operand.exclude( + lambda attribute: query( + SingleAttributeOperand._from_py_single_attribute_operand(attribute) + ) + ) + def clone(self) -> SingleAttributeOperand: return SingleAttributeOperand._from_py_single_attribute_operand( self._single_attribute_operand.deep_clone() @@ -1459,6 +1504,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[NodeIndicesOperand], None]) -> None: + self._node_indices_operand.exclude( + lambda node_indices: query( + NodeIndicesOperand._from_py_node_indices_operand(node_indices) + ) + ) + def clone(self) -> NodeIndicesOperand: return NodeIndicesOperand._from_py_node_indices_operand( self._node_indices_operand.deep_clone() @@ -1595,6 +1647,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[NodeIndexOperand], None]) -> None: + self._node_index_operand.exclude( + lambda node_index: query( + NodeIndexOperand._from_py_node_index_operand(node_index) + ) + ) + def clone(self) -> NodeIndexOperand: return NodeIndexOperand._from_py_node_index_operand( self._node_index_operand.deep_clone() @@ -1740,6 +1799,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[EdgeIndicesOperand], None]) -> None: + self._edge_indices_operand.exclude( + lambda edge_indices: query( + EdgeIndicesOperand._from_edge_indices_operand(edge_indices) + ) + ) + def clone(self) -> EdgeIndicesOperand: return EdgeIndicesOperand._from_edge_indices_operand( self._edge_indices_operand.deep_clone() @@ -1855,6 +1921,13 @@ def either_or( ), ) + def exclude(self, query: Callable[[EdgeIndexOperand], None]) -> None: + self._edge_index_operand.exclude( + lambda edge_index: query( + EdgeIndexOperand._from_py_edge_index_operand(edge_index) + ) + ) + def clone(self) -> EdgeIndexOperand: return EdgeIndexOperand._from_py_edge_index_operand( self._edge_index_operand.deep_clone() diff --git a/rustmodels/src/medrecord/querying/attributes.rs b/rustmodels/src/medrecord/querying/attributes.rs index 4ca611db..49fc07f2 100644 --- a/rustmodels/src/medrecord/querying/attributes.rs +++ b/rustmodels/src/medrecord/querying/attributes.rs @@ -247,6 +247,14 @@ impl PyAttributesTreeOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyAttributesTreeOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyAttributesTreeOperand { self.0.deep_clone().into() } @@ -421,6 +429,14 @@ impl PyMultipleAttributesOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyMultipleAttributesOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyMultipleAttributesOperand { self.0.deep_clone().into() } @@ -559,6 +575,14 @@ impl PySingleAttributeOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PySingleAttributeOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PySingleAttributeOperand { self.0.deep_clone().into() } diff --git a/rustmodels/src/medrecord/querying/edges.rs b/rustmodels/src/medrecord/querying/edges.rs index 0eabf281..d0d86e22 100644 --- a/rustmodels/src/medrecord/querying/edges.rs +++ b/rustmodels/src/medrecord/querying/edges.rs @@ -76,6 +76,14 @@ impl PyEdgeOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyEdgeOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyEdgeOperand { self.0.deep_clone().into() } @@ -276,6 +284,14 @@ impl PyEdgeIndicesOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyEdgeIndicesOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyEdgeIndicesOperand { self.0.deep_clone().into() } @@ -378,6 +394,14 @@ impl PyEdgeIndexOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyEdgeIndexOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyEdgeIndexOperand { self.0.deep_clone().into() } diff --git a/rustmodels/src/medrecord/querying/nodes.rs b/rustmodels/src/medrecord/querying/nodes.rs index 9577342e..ec4120f9 100644 --- a/rustmodels/src/medrecord/querying/nodes.rs +++ b/rustmodels/src/medrecord/querying/nodes.rs @@ -108,6 +108,14 @@ impl PyNodeOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyNodeOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> Self { self.0.deep_clone().into() } @@ -347,6 +355,14 @@ impl PyNodeIndicesOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyNodeIndicesOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> Self { self.0.deep_clone().into() } @@ -485,6 +501,14 @@ impl PyNodeIndexOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyNodeIndexOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> Self { self.0.deep_clone().into() } diff --git a/rustmodels/src/medrecord/querying/values.rs b/rustmodels/src/medrecord/querying/values.rs index 31a7f802..af99a6ea 100644 --- a/rustmodels/src/medrecord/querying/values.rs +++ b/rustmodels/src/medrecord/querying/values.rs @@ -302,6 +302,14 @@ impl PyMultipleValuesOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PyMultipleValuesOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PyMultipleValuesOperand { self.0.deep_clone().into() } @@ -476,6 +484,14 @@ impl PySingleValueOperand { ); } + pub fn exclude(&mut self, query: &Bound<'_, PyFunction>) { + self.0.exclude(|operand| { + query + .call1((PySingleValueOperand::from(operand.clone()),)) + .expect("Call must succeed"); + }); + } + pub fn deep_clone(&self) -> PySingleValueOperand { self.0.deep_clone().into() }