Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New predicate support #2086

Merged
merged 15 commits into from
Oct 8, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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
Original file line number Diff line number Diff line change
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