Skip to content

Commit

Permalink
Add operators for filter expression (#178)
Browse files Browse the repository at this point in the history
* add "or" condition for filter expression

* add operators for filter expression

* add test cases for filter expression
  • Loading branch information
reiishikawa1208 authored May 20, 2022
1 parent ab12e97 commit d830e37
Show file tree
Hide file tree
Showing 7 changed files with 398 additions and 0 deletions.
105 changes: 105 additions & 0 deletions raiden/src/filter_expression/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use crate::IntoAttribute;

pub type FilterExpressionString = String;

// note: The syntax for a filter expression is identical to that of a key condition expression.
Expand All @@ -10,6 +12,11 @@ pub enum FilterExpressionOperator {
super::AttributeNames,
super::AttributeValues,
),
Or(
FilterExpressionString,
super::AttributeNames,
super::AttributeValues,
),
}

#[allow(clippy::large_enum_variant)]
Expand All @@ -28,6 +35,10 @@ pub enum FilterExpressionTypes {
super::AttributeValue,
),
BeginsWith(super::Placeholder, super::AttributeValue),
AttributeExists(),
AttributeNotExists(),
AttributeType(super::Placeholder, super::AttributeType),
Contains(super::Placeholder, super::AttributeValue),
}

pub trait FilterExpressionBuilder<T> {
Expand Down Expand Up @@ -71,6 +82,15 @@ impl<T> FilterExpressionFilledOrWaitOperator<T> {
_token: self._token,
}
}
pub fn or(self, cond: impl FilterExpressionBuilder<T>) -> FilterExpressionFilled<T> {
let (condition_string, attr_names, attr_values) = cond.build();
FilterExpressionFilled {
attr: self.attr,
cond: self.cond,
operator: FilterExpressionOperator::Or(condition_string, attr_names, attr_values),
_token: self._token,
}
}
}

impl<T> FilterExpressionBuilder<T> for FilterExpressionFilledOrWaitOperator<T> {
Expand Down Expand Up @@ -149,6 +169,32 @@ impl<T> FilterExpressionBuilder<T> for FilterExpressionFilledOrWaitOperator<T> {
attr_values,
)
}
FilterExpressionTypes::AttributeExists() => (
format!("attribute_exists(#{})", attr_name),
attr_names,
attr_values,
),
FilterExpressionTypes::AttributeNotExists() => (
format!("attribute_not_exists(#{})", attr_name),
attr_names,
attr_values,
),
FilterExpressionTypes::AttributeType(placeholder, attribute_type) => {
attr_values.insert(placeholder.to_string(), attribute_type.into_attr());
(
format!("attribute_type(#{}, {})", attr_name, placeholder),
attr_names,
attr_values,
)
}
FilterExpressionTypes::Contains(placeholder, value) => {
attr_values.insert(placeholder.to_string(), value);
(
format!("contains(#{}, {})", attr_name, placeholder),
attr_names,
attr_values,
)
}
}
}
}
Expand All @@ -157,6 +203,7 @@ impl<T> FilterExpressionBuilder<T> for FilterExpressionFilled<T> {
fn build(self) -> (String, super::AttributeNames, super::AttributeValues) {
let (right_str, right_names, right_values) = match self.operator {
FilterExpressionOperator::And(s, m, v) => (format!("AND ({})", s), m, v),
FilterExpressionOperator::Or(s, m, v) => (format!("OR ({})", s), m, v),
};

let attr_name = self.attr;
Expand Down Expand Up @@ -201,6 +248,20 @@ impl<T> FilterExpressionBuilder<T> for FilterExpressionFilled<T> {
left_values.insert(placeholder.clone(), value);
format!("begins_with(#{}, {})", attr_name, placeholder)
}
FilterExpressionTypes::AttributeExists() => {
format!("attribute_exists(#{})", attr_name)
}
FilterExpressionTypes::AttributeNotExists() => {
format!("attribute_not_exists(#{})", attr_name)
}
FilterExpressionTypes::AttributeType(placeholder, attribute_type) => {
left_values.insert(placeholder.clone(), attribute_type.into_attr());
format!("attribute_type(#{}, {})", attr_name, placeholder)
}
FilterExpressionTypes::Contains(placeholder, value) => {
left_values.insert(placeholder.clone(), value);
format!("contains(#{}, {})", attr_name, placeholder)
}
};
(
format!("{} {}", left_str, right_str),
Expand Down Expand Up @@ -303,4 +364,48 @@ impl<T> FilterExpression<T> {
_token: std::marker::PhantomData,
}
}

pub fn attribute_exists(self) -> FilterExpressionFilledOrWaitOperator<T> {
let cond = FilterExpressionTypes::AttributeExists();
FilterExpressionFilledOrWaitOperator {
attr: self.attr,
cond,
_token: std::marker::PhantomData,
}
}

pub fn attribute_not_exists(self) -> FilterExpressionFilledOrWaitOperator<T> {
let cond = FilterExpressionTypes::AttributeNotExists();
FilterExpressionFilledOrWaitOperator {
attr: self.attr,
cond,
_token: std::marker::PhantomData,
}
}

pub fn attribute_type(
self,
attribute_type: super::AttributeType,
) -> FilterExpressionFilledOrWaitOperator<T> {
let placeholder = format!(":value{}", super::generate_value_id());
let cond = FilterExpressionTypes::AttributeType(placeholder, attribute_type);
FilterExpressionFilledOrWaitOperator {
attr: self.attr,
cond,
_token: std::marker::PhantomData,
}
}

pub fn contains(
self,
value: impl super::IntoAttribute,
) -> FilterExpressionFilledOrWaitOperator<T> {
let placeholder = format!(":value{}", super::generate_value_id());
let cond = FilterExpressionTypes::Contains(placeholder, value.into_attr());
FilterExpressionFilledOrWaitOperator {
attr: self.attr,
cond,
_token: std::marker::PhantomData,
}
}
}
9 changes: 9 additions & 0 deletions raiden/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ pub enum AttributeType {
M, // Map
}

impl IntoAttribute for AttributeType {
fn into_attr(self) -> AttributeValue {
AttributeValue {
s: Some(self.to_string()),
..AttributeValue::default()
}
}
}

impl std::fmt::Display for AttributeType {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "{:?}", self)
Expand Down
99 changes: 99 additions & 0 deletions raiden/tests/all/filter_expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,36 @@ mod tests {
assert_eq!(attribute_values, expected_values);
}

#[test]
fn test_two_or_filter_expression() {
reset_value_id();

let cond = User::filter_expression(User::name())
.eq("bokuweb")
.or(User::filter_expression(User::year())
.eq(1999)
.or(User::filter_expression(User::num()).eq(100)));

let (filter_expression, attribute_names, attribute_values) = cond.build();
let mut expected_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
expected_names.insert("#name".to_owned(), "name".to_owned());
expected_names.insert("#year".to_owned(), "year".to_owned());
expected_names.insert("#num".to_owned(), "num".to_owned());
let mut expected_values: std::collections::HashMap<String, AttributeValue> =
std::collections::HashMap::new();
expected_values.insert(":value0".to_owned(), "bokuweb".into_attr());
expected_values.insert(":value1".to_owned(), 1999.into_attr());
expected_values.insert(":value2".to_owned(), 100.into_attr());

assert_eq!(
filter_expression,
"#name = :value0 OR (#year = :value1 OR (#num = :value2))".to_owned(),
);
assert_eq!(attribute_names, expected_names);
assert_eq!(attribute_values, expected_values);
}

#[test]
fn test_begins_with_filter_expression() {
reset_value_id();
Expand Down Expand Up @@ -111,4 +141,73 @@ mod tests {
"#id <> :value0 AND (begins_with(#year, :value1))".to_owned(),
);
}

#[test]
fn test_attribute_exists_filter_expression() {
reset_value_id();

let cond = User::filter_expression(User::name()).attribute_exists();
let (filter_expression, attribute_names, attribute_values) = cond.build();
let mut expected_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
expected_names.insert("#name".to_owned(), "name".to_owned());
let expected_values: std::collections::HashMap<String, AttributeValue> =
std::collections::HashMap::new();
assert_eq!(filter_expression, "attribute_exists(#name)".to_owned(),);
assert_eq!(attribute_names, expected_names);
assert_eq!(attribute_values, expected_values);
}

#[test]
fn test_attribute_not_exists_filter_expression() {
reset_value_id();

let cond = User::filter_expression(User::name()).attribute_not_exists();
let (filter_expression, attribute_names, attribute_values) = cond.build();
let mut expected_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
expected_names.insert("#name".to_owned(), "name".to_owned());
let expected_values: std::collections::HashMap<String, AttributeValue> =
std::collections::HashMap::new();
assert_eq!(filter_expression, "attribute_not_exists(#name)".to_owned(),);
assert_eq!(attribute_names, expected_names);
assert_eq!(attribute_values, expected_values);
}

#[test]
fn test_attribute_type_filter_expression() {
reset_value_id();

let cond = User::filter_expression(User::name()).attribute_type(raiden::AttributeType::S);
let (filter_expression, attribute_names, attribute_values) = cond.build();
let mut expected_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
expected_names.insert("#name".to_owned(), "name".to_owned());
let mut expected_values: std::collections::HashMap<String, AttributeValue> =
std::collections::HashMap::new();
expected_values.insert(":value0".to_owned(), "S".into_attr());
assert_eq!(
filter_expression,
"attribute_type(#name, :value0)".to_owned(),
);
assert_eq!(attribute_names, expected_names);
assert_eq!(attribute_values, expected_values);
}

#[test]
fn test_contains_filter_expression() {
reset_value_id();

let cond = User::filter_expression(User::name()).contains("boku");
let (filter_expression, attribute_names, attribute_values) = cond.build();
let mut expected_names: std::collections::HashMap<String, String> =
std::collections::HashMap::new();
expected_names.insert("#name".to_owned(), "name".to_owned());
let mut expected_values: std::collections::HashMap<String, AttributeValue> =
std::collections::HashMap::new();
expected_values.insert(":value0".to_owned(), "boku".into_attr());
assert_eq!(filter_expression, "contains(#name, :value0)".to_owned(),);
assert_eq!(attribute_names, expected_names);
assert_eq!(attribute_values, expected_values);
}
}
Loading

0 comments on commit d830e37

Please sign in to comment.