coalesce function

main
Ziyang Hu 2 years ago
parent 17f52713a1
commit 59aab748ee

@ -300,7 +300,7 @@ fn aggregation_count() {
fn aggregation_filter() {
TEST_DB
.run_script(
"?[age, count(age)] := *user{age}, coalesce(age, 0) >= 18",
"?[age, count(age)] := *user{age}, age ~ 0 >= 18",
Default::default(),
)
.unwrap();
@ -331,7 +331,7 @@ fn expansion_1_filter() {
let i = rng.gen_range(1..SIZES.0);
TEST_DB
.run_script(
"?[to] := *friends{fr: $id, to}, *user{uid: to, age}, coalesce(age, 0) >= 18",
"?[to] := *friends{fr: $id, to}, *user{uid: to, age}, age ~ 0 >= 18",
BTreeMap::from([("id".to_string(), json!(i))]),
)
.unwrap();
@ -353,7 +353,7 @@ fn expansion_2_filter() {
let i = rng.gen_range(1..SIZES.0);
TEST_DB
.run_script(
"?[to] := *friends{fr: $id, to: a}, *friends{fr: a, to}, *user{uid: to, age}, coalesce(age, 0) >= 18",
"?[to] := *friends{fr: $id, to: a}, *friends{fr: a, to}, *user{uid: to, age}, age ~ 0 >= 18",
BTreeMap::from([("id".to_string(), json!(i))]),
)
.unwrap();
@ -381,7 +381,7 @@ fn expansion_3_filter() {
r#"
l1[to] := *friends{fr: $id, to}
l2[to] := l1[fr], *friends{fr, to}
?[to] := l2[fr], *friends{fr, to}, *user{uid: to, age}, coalesce(age, 0) >= 18
?[to] := l2[fr], *friends{fr, to}, *user{uid: to, age}, age ~ 0 >= 18
"#,
BTreeMap::from([("id".to_string(), json!(i))]),
)
@ -440,8 +440,8 @@ fn neighbours_2_filter() {
.run_script(
r#"
l1[to] := *friends{fr: $id, to}
?[to] := l1[to], *user{uid: to, age}, coalesce(age, 0) >= 18
?[to] := l1[fr], *friends{fr, to}, *user{uid: to, age}, coalesce(age, 0) >= 18
?[to] := l1[to], *user{uid: to, age}, age ~ 0 >= 18
?[to] := l1[fr], *friends{fr, to}, *user{uid: to, age}, age ~ 0 >= 18
"#,
BTreeMap::from([("id".to_string(), json!(i))]),
)
@ -470,8 +470,8 @@ fn neighbours_2_filter_data() {
.run_script(
r#"
l1[to] := *friends{fr: $id, to}
?[to] := l1[to], *user{uid: to, age, cmpl_pct, gender}, coalesce(age, 0) >= 18
?[to] := l1[fr], *friends{fr, to}, *user{uid: to, age, cmpl_pct, gender}, coalesce(age, 0) >= 18
?[to] := l1[to], *user{uid: to, age, cmpl_pct, gender}, age ~ 0 >= 18
?[to] := l1[fr], *friends{fr, to}, *user{uid: to, age, cmpl_pct, gender}, age ~ 0 >= 18
"#,
BTreeMap::from([("id".to_string(), json!(i))]),
)

@ -80,7 +80,7 @@ grouped = _{"(" ~ rule_body ~ ")"}
expr = {unary_op* ~ term ~ (operation ~ unary_op* ~ term)*}
operation = _{ (op_and | op_or | op_pow | op_concat | op_add | op_sub | op_mul | op_div | op_mod |
op_ge | op_le | op_gt | op_lt | op_eq | op_ne)}
op_ge | op_le | op_gt | op_lt | op_eq | op_ne | op_coalesce )}
op_or = { "||" }
op_and = { "&&" }
op_concat = { "++" }
@ -96,6 +96,7 @@ op_lt = { "<" }
op_ge = { ">=" }
op_le = { "<=" }
op_pow = { "^" }
op_coalesce = { "~" }
unary_op = _{ minus | negate }
minus = { "-" }
negate = { "!" }

@ -716,51 +716,6 @@ impl MeetAggrObj for MeetAggrMax {
}
}
define_aggr!(AGGR_CHOICE_LAST, true);
pub(crate) struct AggrChoiceLast {
found: DataValue,
}
impl Default for AggrChoiceLast {
fn default() -> Self {
Self {
found: DataValue::Null,
}
}
}
impl NormalAggrObj for AggrChoiceLast {
fn set(&mut self, value: &DataValue) -> Result<()> {
self.found = value.clone();
Ok(())
}
fn get(&self) -> Result<DataValue> {
Ok(self.found.clone())
}
}
pub(crate) struct MeetAggrChoiceLast;
impl MeetAggrObj for MeetAggrChoiceLast {
fn init_val(&self) -> DataValue {
DataValue::Null
}
fn update(&self, left: &mut DataValue, right: &DataValue) -> Result<bool> {
if *right == DataValue::Null {
return Ok(false);
}
Ok(if *left == *right {
false
} else {
*left = right.clone();
true
})
}
}
define_aggr!(AGGR_LATEST_BY, false);
pub(crate) struct AggrLatestBy {
@ -1178,7 +1133,6 @@ pub(crate) fn parse_aggr(name: &str) -> Option<&'static Aggregation> {
"max" => &AGGR_MAX,
"mean" => &AGGR_MEAN,
"choice" => &AGGR_CHOICE,
"choice_last" => &AGGR_CHOICE_LAST,
"collect" => &AGGR_COLLECT,
"shortest" => &AGGR_SHORTEST,
"min_cost" => &AGGR_MIN_COST,
@ -1199,7 +1153,6 @@ impl Aggregation {
name if name == AGGR_MIN.name => Box::new(MeetAggrMin),
name if name == AGGR_MAX.name => Box::new(MeetAggrMax),
name if name == AGGR_CHOICE.name => Box::new(MeetAggrChoice),
name if name == AGGR_CHOICE_LAST.name => Box::new(MeetAggrChoiceLast),
name if name == AGGR_BIT_AND.name => Box::new(MeetAggrBitAnd),
name if name == AGGR_BIT_OR.name => Box::new(MeetAggrBitOr),
name if name == AGGR_UNION.name => Box::new(MeetAggrUnion),
@ -1225,7 +1178,6 @@ impl Aggregation {
name if name == AGGR_VARIANCE.name => Box::new(AggrVariance::default()),
name if name == AGGR_STD_DEV.name => Box::new(AggrStdDev::default()),
name if name == AGGR_CHOICE.name => Box::new(AggrChoice::default()),
name if name == AGGR_CHOICE_LAST.name => Box::new(AggrChoiceLast::default()),
name if name == AGGR_BIT_AND.name => Box::new(AggrBitAnd::default()),
name if name == AGGR_BIT_OR.name => Box::new(AggrBitOr::default()),
name if name == AGGR_BIT_XOR.name => Box::new(AggrBitXor::default()),

@ -361,26 +361,6 @@ fn test_max() {
assert_eq!(v, DataValue::from(10));
}
#[test]
fn test_choice_last() {
let mut aggr = parse_aggr("choice_last").unwrap().clone();
aggr.normal_init(&[]).unwrap();
aggr.meet_init(&[]).unwrap();
let mut choice_aggr = aggr.normal_op.unwrap();
choice_aggr.set(&DataValue::from(1)).unwrap();
choice_aggr.set(&DataValue::from(2)).unwrap();
choice_aggr.set(&DataValue::from(3)).unwrap();
assert_eq!(choice_aggr.get().unwrap(), DataValue::from(3));
let m_choice_aggr = aggr.meet_op.unwrap();
let mut v = DataValue::from(5);
m_choice_aggr.update(&mut v, &DataValue::from(1)).unwrap();
m_choice_aggr.update(&mut v, &DataValue::from(2)).unwrap();
m_choice_aggr.update(&mut v, &DataValue::from(3)).unwrap();
assert_eq!(v, DataValue::from(3));
}
#[test]
fn test_choice_rand() {
let mut aggr = parse_aggr("choice_rand").unwrap().clone();

@ -9,10 +9,12 @@
use approx::AbsDiffEq;
use num_traits::FloatConst;
use regex::Regex;
use serde_json::json;
use smartstring::SmartString;
use crate::data::functions::*;
use crate::data::value::{DataValue, RegexWrapper};
use crate::new_cozo_mem;
#[test]
fn test_add() {
@ -1439,3 +1441,14 @@ fn test_to_bool() {
DataValue::Bool(true)
);
}
#[test]
fn test_coalesce() {
let db = new_cozo_mem().unwrap();
let res = db.run_script("?[a] := a = null ~ 1 ~ 2", Default::default()).unwrap().rows;
assert_eq!(res[0][0], json!(1));
let res = db.run_script("?[a] := a = null ~ null ~ null", Default::default()).unwrap().rows;
assert_eq!(res[0][0], json!(null));
let res = db.run_script("?[a] := a = 2 ~ null ~ 1", Default::default()).unwrap().rows;
assert_eq!(res[0][0], json!(2));
}

@ -17,8 +17,8 @@ use thiserror::Error;
use crate::data::expr::{get_op, Expr};
use crate::data::functions::{
OP_ADD, OP_AND, OP_CONCAT, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LIST, OP_LT, OP_MINUS,
OP_MOD, OP_MUL, OP_NEGATE, OP_NEQ, OP_OR, OP_POW, OP_SUB,
OP_ADD, OP_AND, OP_COALESCE, OP_CONCAT, OP_DIV, OP_EQ, OP_GE, OP_GT, OP_LE, OP_LIST, OP_LT,
OP_MINUS, OP_MOD, OP_MUL, OP_NEGATE, OP_NEQ, OP_OR, OP_POW, OP_SUB,
};
use crate::data::symb::Symbol;
use crate::data::value::DataValue;
@ -42,6 +42,7 @@ lazy_static! {
| Op::infix(Rule::op_concat, Left))
.op(Op::infix(Rule::op_mul, Left) | Op::infix(Rule::op_div, Left))
.op(Op::infix(Rule::op_pow, Right))
.op(Op::infix(Rule::op_coalesce, Left))
.op(Op::prefix(Rule::minus))
.op(Op::prefix(Rule::negate))
};
@ -99,6 +100,7 @@ fn build_expr_infix(lhs: Result<Expr>, op: Pair<'_>, rhs: Result<Expr>) -> Resul
Rule::op_concat => &OP_CONCAT,
Rule::op_or => &OP_OR,
Rule::op_and => &OP_AND,
Rule::op_coalesce => &OP_COALESCE,
_ => unreachable!(),
};
let start = args[0].span().0;

Loading…
Cancel
Save