Skip to content

Commit

Permalink
New predicate support (#2086)
Browse files Browse the repository at this point in the history
* support for start/end_with in condition filter
  • Loading branch information
waruto210 committed Oct 8, 2022
1 parent eb393da commit b6d68bc
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 70 deletions.
Expand Up @@ -633,8 +633,7 @@ fn encode_storage_row_filter_condition(
let condition = if let Some(filter) = row_filter { filter.as_ref().try_into() } else { Ok(None) };
// gremlin test in ci will compile use debug mode
// panic so that developer will know convert failed
// TODO: support Text.StartsWith etc. and reopen debug_assert
// debug_assert!(condition.is_ok());
debug_assert!(condition.is_ok());
match condition {
Ok(cond) => (cond, false),
Err(e) => {
Expand All @@ -653,8 +652,7 @@ fn extract_needed_columns(
filter: Option<&Arc<PEvaluator>>, out_columns: Option<&Vec<PropId>>,
) -> GraphProxyResult<Option<Vec<PropId>>> {
use ahash::HashSet;

use crate::utils::expr::eval_pred::zip_option_vecs;
use super::translation::zip_option_vecs;

// Some(vec[]) means need all props, so can't merge it with props needed in filter
if let Some(out_columns) = out_columns {
Expand Down
Expand Up @@ -14,6 +14,18 @@ use crate::utils::expr::eval::Operand;
use crate::utils::expr::eval_pred::{PEvaluator, Predicate, Predicates};
use crate::{GraphProxyError, GraphProxyResult};

pub(crate) fn zip_option_vecs<T>(left: Option<Vec<T>>, right: Option<Vec<T>>) -> Option<Vec<T>> {
match (left, right) {
(Some(mut left), Some(mut right)) => {
left.append(&mut right);
Some(left)
}
(None, Some(right)) => Some(right),
(Some(left), None) => Some(left),
(None, None) => None,
}
}

impl Operand {
/// only get the PropId, else None
pub(crate) fn get_var_prop_id(&self) -> GraphProxyResult<PropId> {
Expand Down Expand Up @@ -44,6 +56,52 @@ impl Operand {
}
}

impl Predicate {
fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
let left = self.left.get_var_prop_id();
let right = self.right.get_var_prop_id();
match (left, right) {
(Ok(left), Ok(right)) => Some(vec![left, right]),
(Ok(left), _) => Some(vec![left]),
(_, Ok(right)) => Some(vec![right]),
_ => None,
}
}
}

impl Predicates {
pub(crate) fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
match self {
Predicates::Init => None,
Predicates::SingleItem(operand) => operand
.get_var_prop_id()
.map(|id| Some(vec![id]))
.unwrap_or(None),
Predicates::Predicate(pred) => pred.extract_prop_ids(),
Predicates::Not(pred) => pred.extract_prop_ids(),
Predicates::And((left, right)) => {
let left = left.extract_prop_ids();
let right = right.extract_prop_ids();
zip_option_vecs(left, right)
}
Predicates::Or((left, right)) => {
let left = left.extract_prop_ids();
let right = right.extract_prop_ids();
zip_option_vecs(left, right)
}
}
}
}

impl PEvaluator {
pub(crate) fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
match self {
PEvaluator::Predicates(preds) => preds.extract_prop_ids(),
_ => None,
}
}
}

impl TryFrom<&Predicate> for StorePredCondition {
type Error = GraphProxyError;

Expand Down Expand Up @@ -72,6 +130,12 @@ impl TryFrom<&Predicate> for StorePredCondition {
common_pb::Logical::Without => {
StorePredCondition::new_predicate(left, StoreOprator::WithOut, right)
}
common_pb::Logical::Startswith => {
StorePredCondition::new_predicate(left, StoreOprator::StartWith, right)
}
common_pb::Logical::Endswith => {
StorePredCondition::new_predicate(left, StoreOprator::EndWith, right)
}
_ => {
return Err(GraphProxyError::FilterPushDownError(format!(
"op {:?} shouldn't appear",
Expand Down Expand Up @@ -163,15 +227,17 @@ mod test {
use super::*;
use crate::apis::PropKey;
use crate::utils::expr::eval::Operand;

#[test]
fn test_predicates_to_condition() {
// test empty Predicates
fn test_empty_predicates_to_condition() {
let pred = &Predicates::Init;
let cond: Result<Option<Condition>, GraphProxyError> = pred.try_into();
assert!(cond.is_ok());
assert_eq!(cond.unwrap(), None);
}

// test SingleItem Predicates
#[test]
fn test_singleitem_predicates_to_condition() {
let oprand = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let pred = &Predicates::SingleItem(oprand);
let target = ConditionBuilder::new()
Expand All @@ -181,8 +247,10 @@ mod test {
assert!(cond.is_ok());
let cond = cond.unwrap();
assert_eq!(cond, target);
}

// test Predicates
#[test]
fn test_single_op_predicates_to_condition() {
let left = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let right = Operand::Const(Object::Primitive(Primitives::Integer(10)));
let cmp = common_pb::Logical::Eq;
Expand All @@ -201,7 +269,26 @@ mod test {
let cond = cond.unwrap();
assert_eq!(cond, target);

// test not Predicates
let left = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let right = Operand::Const(Object::String("hello world".to_owned()));
let cmp = common_pb::Logical::Startswith;

let pred = &Predicates::Predicate(Predicate { left, cmp, right });

let target = ConditionBuilder::new()
.and(Condition::Pred(StorePredCondition::new_predicate(
StoreOperand::PropId(1),
StoreOprator::StartWith,
StoreOperand::Const(StoreProperty::String("hello world".to_owned())),
)))
.build();
let cond: Result<Option<Condition>, GraphProxyError> = pred.try_into();
assert!(cond.is_ok());
let cond = cond.unwrap();
assert_eq!(cond, target);
}
#[test]
fn test_not_predicates_to_condition() {
let oprand = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let pred = &Predicates::Not(Box::new(Predicates::SingleItem(oprand)));
let target = ConditionBuilder::new()
Expand All @@ -212,8 +299,10 @@ mod test {
assert!(cond.is_ok());
let cond = cond.unwrap();
assert_eq!(cond, target);
}

// test and Predicates
#[test]
fn test_and_predicates_to_condition() {
let left = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let right = Operand::Const(Object::Primitive(Primitives::Integer(10)));
let cmp = common_pb::Logical::Ge;
Expand Down Expand Up @@ -244,8 +333,10 @@ mod test {
assert!(cond.is_ok());
let cond = cond.unwrap();
assert_eq!(cond, target);
}

// test or Predicates
#[test]
fn test_or_predicates_to_condition() {
let left = Operand::Var { tag: None, prop_key: Some(PropKey::Key(NameOrId::Id(1))) };
let right = Operand::Const(Object::Primitive(Primitives::Integer(10)));
let cmp = common_pb::Logical::Ge;
Expand Down
Expand Up @@ -17,7 +17,6 @@
use std::convert::{TryFrom, TryInto};

use dyn_type::{BorrowObject, Object};
use global_query::store_api::PropId;
use ir_common::error::ParsePbError;
use ir_common::expr_parse::error::{ExprError, ExprResult};
use ir_common::generated::algebra as pb;
Expand All @@ -39,19 +38,6 @@ pub struct Predicate {
pub(crate) right: Operand,
}

impl Predicate {
pub(crate) fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
let left = self.left.get_var_prop_id();
let right = self.right.get_var_prop_id();
match (left, right) {
(Ok(left), Ok(right)) => Some(vec![left, right]),
(Ok(left), _) => Some(vec![left]),
(_, Ok(right)) => Some(vec![right]),
_ => None,
}
}
}

#[allow(dead_code)]
#[derive(Debug, Clone)]
enum Partial {
Expand Down Expand Up @@ -162,42 +148,6 @@ pub enum Predicates {
Or((Box<Predicates>, Box<Predicates>)),
}

pub(crate) fn zip_option_vecs<T>(left: Option<Vec<T>>, right: Option<Vec<T>>) -> Option<Vec<T>> {
match (left, right) {
(Some(mut left), Some(mut right)) => {
left.append(&mut right);
Some(left)
}
(None, Some(right)) => Some(right),
(Some(left), None) => Some(left),
(None, None) => None,
}
}

impl Predicates {
pub fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
match self {
Predicates::Init => None,
Predicates::SingleItem(operand) => operand
.get_var_prop_id()
.map(|id| Some(vec![id]))
.unwrap_or(None),
Predicates::Predicate(pred) => pred.extract_prop_ids(),
Predicates::Not(pred) => pred.extract_prop_ids(),
Predicates::And((left, right)) => {
let left = left.extract_prop_ids();
let right = right.extract_prop_ids();
zip_option_vecs(left, right)
}
Predicates::Or((left, right)) => {
let left = left.extract_prop_ids();
let right = right.extract_prop_ids();
zip_option_vecs(left, right)
}
}
}
}

impl Default for Predicates {
fn default() -> Self {
Self::Init
Expand Down Expand Up @@ -531,15 +481,6 @@ pub enum PEvaluator {
General(Evaluator),
}

impl PEvaluator {
pub fn extract_prop_ids(&self) -> Option<Vec<PropId>> {
match self {
PEvaluator::Predicates(preds) => preds.extract_prop_ids(),
_ => None,
}
}
}

impl EvalPred for PEvaluator {
fn eval_bool<E: Element, C: Context<E>>(&self, context: Option<&C>) -> ExprEvalResult<bool> {
let result = match self {
Expand Down
Expand Up @@ -68,6 +68,8 @@ pub enum CmpOperator {
GreaterEqual,
WithIn,
WithOut,
StartWith,
EndWith,
}

impl CmpOperator {
Expand All @@ -81,6 +83,8 @@ impl CmpOperator {
CmpOperator::GreaterEqual => Ok(left >= right),
CmpOperator::WithIn => right.contains(left),
CmpOperator::WithOut => right.contains(left).map(|ret| !ret),
CmpOperator::StartWith => left.start_with(right),
CmpOperator::EndWith => left.end_with(right),
}
}
}
Expand Down
49 changes: 49 additions & 0 deletions interactive_engine/executor/store/groot/src/api/condition/test.rs
Expand Up @@ -349,6 +349,55 @@ fn test_condition_without_operation() {
);
}

#[test]
fn test_condition_start_end_with_operation() {
let entites = prepare_entites().collect::<Vec<LocalEntity>>();
let predicate = PredCondition::new_predicate(
Operand::PropId(2),
CmpOperator::StartWith,
Operand::Const(Property::String("Astr".to_owned())),
);
let e1 = entites
.clone()
.into_iter()
.filter(|v| predicate.filter_vertex(v).unwrap_or(false))
.collect::<Vec<LocalEntity>>();
assert_eq!(1, e1.len());
assert_eq!(e1[0].get_id(), 1);

let e1 = entites
.clone()
.into_iter()
.filter(|v| predicate.filter_edge(v).unwrap_or(false))
.collect::<Vec<LocalEntity>>();
assert_eq!(1, e1.len());
assert_eq!(e1[0].get_id(), 1);

let predicate = PredCondition::new_predicate(
Operand::PropId(2),
CmpOperator::EndWith,
Operand::Const(Property::String("engine".to_owned())),
);
assert_eq!(
2,
entites
.clone()
.into_iter()
.filter(|v| predicate.filter_vertex(v).unwrap_or(false))
.collect::<Vec<LocalEntity>>()
.len()
);
assert_eq!(
2,
entites
.clone()
.into_iter()
.filter(|v| predicate.filter_edge(v).unwrap_or(false))
.collect::<Vec<LocalEntity>>()
.len()
);
}

#[test]
fn test_condition_cmp_operation() {
let entites = prepare_entites().collect::<Vec<LocalEntity>>();
Expand Down
14 changes: 14 additions & 0 deletions interactive_engine/executor/store/groot/src/api/property.rs
Expand Up @@ -185,6 +185,20 @@ impl Property {
_ => Ok(false),
}
}

// only work for string property
pub(crate) fn start_with(&self, rhs: &Self) -> GraphResult<bool> {
let left = self.get_string()?;
let right = rhs.get_string()?;
Ok(left.starts_with(right))
}

// only work for string property
pub(crate) fn end_with(&self, rhs: &Self) -> GraphResult<bool> {
let left = self.get_string()?;
let right = rhs.get_string()?;
Ok(left.ends_with(right))
}
}

fn objects_to_list_property(v: &[Object]) -> GraphResult<Property> {
Expand Down

0 comments on commit b6d68bc

Please sign in to comment.