Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions graph/src/components/store.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,11 +172,17 @@ pub enum EntityFilter {
In(Attribute, Vec<Value>),
NotIn(Attribute, Vec<Value>),
Contains(Attribute, Value),
ContainsStrict(Attribute, Value),
NotContains(Attribute, Value),
NotContainsStrict(Attribute, Value),
StartsWith(Attribute, Value),
StartsWithStrict(Attribute, Value),
NotStartsWith(Attribute, Value),
NotStartsWithStrict(Attribute, Value),
EndsWith(Attribute, Value),
EndsWithStrict(Attribute, Value),
NotEndsWith(Attribute, Value),
NotEndsWithStrict(Attribute, Value),
}

// Define some convenience methods
Expand Down
53 changes: 41 additions & 12 deletions graphql/src/schema/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -418,11 +418,17 @@ fn field_scalar_filter_input_values(
"in",
"not_in",
"contains",
"contains_strict",
"not_contains",
"not_contains_strict",
"starts_with",
"starts_with_strict",
"not_starts_with",
"not_starts_with_strict",
"ends_with",
"ends_with_strict",
"not_ends_with",
"not_ends_with_strict",
],
_ => vec!["", "not"],
}
Expand Down Expand Up @@ -484,18 +490,25 @@ fn field_list_filter_input_values(
};

Some(
vec!["", "not", "contains", "not_contains"]
.into_iter()
.map(|filter_type| {
input_value(
&field.name,
filter_type,
Type::ListType(Box::new(Type::NonNullType(Box::new(
input_field_type.clone(),
)))),
)
})
.collect(),
vec![
"",
"not",
"contains",
"contains_strict",
"not_contains",
"not_contains_strict",
]
.into_iter()
.map(|filter_type| {
input_value(
&field.name,
filter_type,
Type::ListType(Box::new(Type::NonNullType(Box::new(
input_field_type.clone(),
)))),
)
})
.collect(),
)
})
}
Expand Down Expand Up @@ -1007,19 +1020,29 @@ mod tests {
"name_in",
"name_not_in",
"name_contains",
"name_contains_strict",
"name_not_contains",
"name_not_contains_strict",
"name_starts_with",
"name_starts_with_strict",
"name_not_starts_with",
"name_not_starts_with_strict",
"name_ends_with",
"name_ends_with_strict",
"name_not_ends_with",
"name_not_ends_with_strict",
"favoritePetNames",
"favoritePetNames_not",
"favoritePetNames_contains",
"favoritePetNames_contains_strict",
"favoritePetNames_not_contains",
"favoritePetNames_not_contains_strict",
"pets",
"pets_not",
"pets_contains",
"pets_contains_strict",
"pets_not_contains",
"pets_not_contains_strict",
"favoriteFurType",
"favoriteFurType_not",
"favoriteFurType_in",
Expand All @@ -1033,11 +1056,17 @@ mod tests {
"favoritePet_in",
"favoritePet_not_in",
"favoritePet_contains",
"favoritePet_contains_strict",
"favoritePet_not_contains",
"favoritePet_not_contains_strict",
"favoritePet_starts_with",
"favoritePet_starts_with_strict",
"favoritePet_not_starts_with",
"favoritePet_not_starts_with_strict",
"favoritePet_ends_with",
"favoritePet_ends_with_strict",
"favoritePet_not_ends_with",
"favoritePet_not_ends_with_strict",
]
.iter()
.map(ToString::to_string)
Expand Down
20 changes: 20 additions & 0 deletions graphql/src/schema/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,17 @@ pub(crate) enum FilterOp {
In,
NotIn,
Contains,
ContainsStrict,
NotContains,
NotContainsStrict,
StartsWith,
StartsWithStrict,
NotStartsWith,
NotStartsWithStrict,
EndsWith,
EndsWithStrict,
NotEndsWith,
NotEndsWithStrict,
Equal,
}

Expand All @@ -41,11 +47,25 @@ pub(crate) fn parse_field_as_filter(key: &str) -> (String, FilterOp) {
k if k.ends_with("_not_in") => ("_not_in", FilterOp::NotIn),
k if k.ends_with("_in") => ("_in", FilterOp::In),
k if k.ends_with("_not_contains") => ("_not_contains", FilterOp::NotContains),
k if k.ends_with("_not_contains_strict") => {
("_not_contains_strict", FilterOp::NotContainsStrict)
}
k if k.ends_with("_contains") => ("_contains", FilterOp::Contains),
k if k.ends_with("_contains_strict") => ("_contains_strict", FilterOp::ContainsStrict),
k if k.ends_with("_not_starts_with") => ("_not_starts_with", FilterOp::NotStartsWith),
k if k.ends_with("_not_starts_with_strict") => {
("_not_starts_with_strict", FilterOp::NotStartsWithStrict)
}
k if k.ends_with("_not_ends_with") => ("_not_ends_with", FilterOp::NotEndsWith),
k if k.ends_with("_not_ends_with_strict") => {
("_not_ends_with_strict", FilterOp::NotEndsWithStrict)
}
k if k.ends_with("_starts_with") => ("_starts_with", FilterOp::StartsWith),
k if k.ends_with("_starts_with_strict") => {
("_starts_with_strict", FilterOp::StartsWithStrict)
}
k if k.ends_with("_ends_with") => ("_ends_with", FilterOp::EndsWith),
k if k.ends_with("_ends_with_strict") => ("_ends_with_strict", FilterOp::EndsWithStrict),
_ => ("", FilterOp::Equal),
};

Expand Down
8 changes: 8 additions & 0 deletions graphql/src/store/query.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,11 +173,19 @@ fn build_filter_from_object(
In => EntityFilter::In(field_name, list_values(store_value, "_in")?),
NotIn => EntityFilter::NotIn(field_name, list_values(store_value, "_not_in")?),
Contains => EntityFilter::Contains(field_name, store_value),
ContainsStrict => EntityFilter::ContainsStrict(field_name, store_value),
NotContains => EntityFilter::NotContains(field_name, store_value),
NotContainsStrict => EntityFilter::NotContainsStrict(field_name, store_value),
StartsWith => EntityFilter::StartsWith(field_name, store_value),
StartsWithStrict => EntityFilter::StartsWithStrict(field_name, store_value),
NotStartsWith => EntityFilter::NotStartsWith(field_name, store_value),
NotStartsWithStrict => {
EntityFilter::NotStartsWithStrict(field_name, store_value)
}
EndsWith => EntityFilter::EndsWith(field_name, store_value),
EndsWithStrict => EntityFilter::EndsWithStrict(field_name, store_value),
NotEndsWith => EntityFilter::NotEndsWith(field_name, store_value),
NotEndsWithStrict => EntityFilter::NotEndsWithStrict(field_name, store_value),
Equal => EntityFilter::Equal(field_name, store_value),
})
})
Expand Down
40 changes: 31 additions & 9 deletions store/postgres/src/relational_queries.rs
Original file line number Diff line number Diff line change
Expand Up @@ -806,7 +806,9 @@ impl<'a> QueryFilter<'a> {
}

Contains(attr, _)
| ContainsStrict(attr, _)
| NotContains(attr, _)
| NotContainsStrict(attr, _)
| Equal(attr, _)
| Not(attr, _)
| GreaterThan(attr, _)
Expand All @@ -816,9 +818,13 @@ impl<'a> QueryFilter<'a> {
| In(attr, _)
| NotIn(attr, _)
| StartsWith(attr, _)
| StartsWithStrict(attr, _)
| NotStartsWith(attr, _)
| NotStartsWithStrict(attr, _)
| EndsWith(attr, _)
| NotEndsWith(attr, _) => {
| EndsWithStrict(attr, _)
| NotEndsWith(attr, _)
| NotEndsWithStrict(attr, _) => {
table.column_for_field(attr)?;
}
}
Expand Down Expand Up @@ -865,18 +871,20 @@ impl<'a> QueryFilter<'a> {
attribute: &Attribute,
value: &Value,
negated: bool,
strict: bool,
mut out: AstPass<Pg>,
) -> QueryResult<()> {
let column = self.column(attribute);

let operation = match (strict, negated) {
(true, true) => " not like ",
(true, false) => " like ",
(false, true) => " not ilike ",
(false, false) => " ilike ",
};
match value {
Value::String(s) => {
out.push_identifier(column.name.as_str())?;
if negated {
out.push_sql(" not like ");
} else {
out.push_sql(" like ")
};
out.push_sql(operation);
if s.starts_with('%') || s.ends_with('%') {
out.push_bind_param::<Text, _>(s)?;
} else {
Expand Down Expand Up @@ -1134,8 +1142,10 @@ impl<'a> QueryFragment<Pg> for QueryFilter<'a> {
And(filters) => self.binary_op(filters, " and ", " true ", out)?,
Or(filters) => self.binary_op(filters, " or ", " false ", out)?,

Contains(attr, value) => self.contains(attr, value, false, out)?,
NotContains(attr, value) => self.contains(attr, value, true, out)?,
Contains(attr, value) => self.contains(attr, value, false, false, out)?,
ContainsStrict(attr, value) => self.contains(attr, value, false, true, out)?,
NotContains(attr, value) => self.contains(attr, value, true, false, out)?,
NotContainsStrict(attr, value) => self.contains(attr, value, true, true, out)?,

Equal(attr, value) => self.equals(attr, value, c::Equal, out)?,
Not(attr, value) => self.equals(attr, value, c::NotEqual, out)?,
Expand All @@ -1151,13 +1161,25 @@ impl<'a> QueryFragment<Pg> for QueryFilter<'a> {
StartsWith(attr, value) => {
self.starts_or_ends_with(attr, value, " like ", true, out)?
}
StartsWithStrict(attr, value) => {
self.starts_or_ends_with(attr, value, " like ", true, out)?
}
NotStartsWith(attr, value) => {
self.starts_or_ends_with(attr, value, " not like ", true, out)?
}
NotStartsWithStrict(attr, value) => {
self.starts_or_ends_with(attr, value, " not like ", true, out)?
}
EndsWith(attr, value) => self.starts_or_ends_with(attr, value, " like ", false, out)?,
EndsWithStrict(attr, value) => {
self.starts_or_ends_with(attr, value, " like ", false, out)?
}
NotEndsWith(attr, value) => {
self.starts_or_ends_with(attr, value, " not like ", false, out)?
}
NotEndsWithStrict(attr, value) => {
self.starts_or_ends_with(attr, value, " not like ", false, out)?
}
}
Ok(())
}
Expand Down