diff --git a/README.md b/README.md index e0cf86b..2d7f1cd 100644 --- a/README.md +++ b/README.md @@ -120,10 +120,8 @@ json 데이터 *(참고 사이트: https://github.com/json-path/JsonPath)* | $..book[2:] | Book number two from tail | | $..book[?(@.isbn)] | All books with an ISBN number | | $.store.book[?(@.price < 10)] | All books in store cheaper than 10 | -| $..book[?(@.price <= $['expensive'])] *(not yet supported)* | ~~All books in store that are not "expensive"~~ | -| $..book[?(@.author =~ /.*REES/i)] *(not yet supported)* | ~~All books matching regex (ignore case)~~ | -| $..* | Give me every thing -| $..book.length() *(not yet supported)* | ~~The number of books~~ | +| $..* | Give me every thing +| $..book[?((@.price == 12.99 | | $.store.bicycle.price < @.price) || @.category == "reference")] | Complex filter ## With Rust (as library) diff --git a/src/filter/cmp.rs b/src/filter/cmp.rs index 0785f60..a83ee16 100644 --- a/src/filter/cmp.rs +++ b/src/filter/cmp.rs @@ -1,3 +1,4 @@ +#[derive(Debug)] pub enum CmpType { Eq, Ne, @@ -7,6 +8,7 @@ pub enum CmpType { Le, } +#[derive(Debug)] pub enum CmpCondType { And, Or, diff --git a/src/filter/mod.rs b/src/filter/mod.rs index 4d116a0..493a667 100644 --- a/src/filter/mod.rs +++ b/src/filter/mod.rs @@ -195,6 +195,18 @@ mod tests { let jf = do_filter("$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", "./benches/data_obj.json"); assert_eq!(&Value::Null, jf.current_value()); + + let jf = do_filter("$..friends[?(@.id == $.index)].id", "./benches/data_obj.json"); + let friends = json!([0, 0]); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$..book[?($.store.bicycle.price < @.price)].price", "./benches/example.json"); + let friends = json!([22.99]); + assert_eq!(&friends, jf.current_value()); + + let jf = do_filter("$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", "./benches/example.json"); + let friends = json!([12.99]); + assert_eq!(&friends, jf.current_value()); } #[test] diff --git a/src/filter/term.rs b/src/filter/term.rs index b9e4827..d738bc6 100644 --- a/src/filter/term.rs +++ b/src/filter/term.rs @@ -1,6 +1,6 @@ use super::cmp::*; -use super::value_wrapper::*; use super::value_filter::ValueFilterKey; +use super::value_wrapper::*; #[derive(Debug)] pub enum TermContext { @@ -17,17 +17,34 @@ impl TermContext { TermContext::Constants(ExprTerm::Bool(et.cmp(oet, cmp_fn, default))) } TermContext::Json(key, v) => { - TermContext::Json(None, v.take_with(key, et, cmp_fn)) + TermContext::Json(None, v.take_with(key, et, cmp_fn, true)) } } } TermContext::Json(key, v) => { match other { - TermContext::Json(_, ov) => { - v.cmp(ov, cmp_fn.into_type()) + TermContext::Json(key_other, ov) => { + + fn is_json(t: &TermContext) -> bool { + match t { + TermContext::Json(_, _) => true, + _ => false + } + } + + let mut v = v.filter(key); + let mut ov = ov.filter(key_other); + let mut c = v.into_term(key); + let mut oc = ov.into_term(key_other); + + if is_json(&c) && is_json(&oc) { + v.cmp(&mut ov, cmp_fn.into_type()) + } else { + c.cmp(&mut oc, cmp_fn, default) + } } TermContext::Constants(et) => { - TermContext::Json(None, v.take_with(key, et, cmp_fn)) + TermContext::Json(None, v.take_with(key, et, cmp_fn, false)) } } } diff --git a/src/filter/value_filter.rs b/src/filter/value_filter.rs index 383c715..977af2d 100644 --- a/src/filter/value_filter.rs +++ b/src/filter/value_filter.rs @@ -48,7 +48,7 @@ impl ArrayIndex for usize { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub enum ValueFilterKey { Num(usize), String(String), @@ -241,7 +241,7 @@ impl ValueFilter { self.last_key = Some(ValueFilterKey::String(key.clone())); self.vw.replace(v); - trace!("step_in_string - after: {:?}", self.vw.get_val()); + trace!("step_in_string - after: {},{},{:?}", self.vw.is_leaves(), self.filter_mode, self.vw.get_val()); &self.vw } } @@ -410,7 +410,9 @@ impl JsonValueFilter { Some(ParseToken::Leaves) => { vf.step_leaves_string(&key); } - _ => {} + _ => { + self.term_stack.push(TermContext::Constants(ExprTerm::String(key))); + } } } _ => {} diff --git a/src/filter/value_wrapper.rs b/src/filter/value_wrapper.rs index 6291d16..82175ac 100644 --- a/src/filter/value_wrapper.rs +++ b/src/filter/value_wrapper.rs @@ -1,6 +1,7 @@ -use serde_json::Value; use indexmap::map::IndexMap; +use serde_json::Value; + use super::cmp::*; use super::term::*; use super::value_filter::*; @@ -38,18 +39,18 @@ impl ValueWrapper { } } - fn cmp_with_term(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool) -> bool { + fn cmp_with_term(val: &Value, et: &ExprTerm, cmp_fn: &F, default: bool, reverse: bool) -> bool { match val { Value::Bool(ref v1) => { match et { - ExprTerm::Bool(v2) => cmp_fn.cmp_bool(v1, v2), + ExprTerm::Bool(v2) => if reverse { cmp_fn.cmp_bool(v2, v1) } else { cmp_fn.cmp_bool(v1, v2) }, _ => default } } Value::Number(ref v1) => match v1.as_f64() { Some(ref v1) => { match et { - ExprTerm::Number(v2) => cmp_fn.cmp_f64(v1, v2), + ExprTerm::Number(v2) => if reverse { cmp_fn.cmp_f64(v2, v1) } else { cmp_fn.cmp_f64(v1, v2) }, _ => default } } @@ -57,7 +58,7 @@ impl ValueWrapper { }, Value::String(ref v1) => { match et { - ExprTerm::String(v2) => cmp_fn.cmp_string(v1, v2), + ExprTerm::String(v2) => if reverse { cmp_fn.cmp_string(v2, v1) } else { cmp_fn.cmp_string(v1, v2) }, _ => default } } @@ -65,7 +66,7 @@ impl ValueWrapper { } } - fn take_object_in_array(&mut self, key: &String, et: &ExprTerm, cmp: &F) -> Option { + fn take_object_in_array(&mut self, key: &String, et: &ExprTerm, cmp: &F, reverse: bool) -> Option { fn _filter_with_object bool>(v: &&mut Value, key: &String, fun: F) -> bool { match &v { Value::Object(map) => { @@ -82,7 +83,7 @@ impl ValueWrapper { Value::Array(mut vec) => { let mut ret: Vec = vec.iter_mut() .filter(|v| { - _filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false)) + _filter_with_object(v, key, |vv| Self::cmp_with_term(vv, et, cmp, false, reverse)) }) .map(|v| v.take()) .collect(); @@ -92,29 +93,29 @@ impl ValueWrapper { } } - fn take_with_key_type(&mut self, key: &Option, et: &ExprTerm, cmp: &F) -> Option { + fn take_with_key_type(&mut self, key: &Option, et: &ExprTerm, cmp: &F, reverse: bool) -> Option { match key { Some(ValueFilterKey::String(key)) => { - self.take_object_in_array(key, et, cmp) + self.take_object_in_array(key, et, cmp, reverse) } _ => None } } - pub fn take_with(&mut self, key: &Option, et: &ExprTerm, cmp: F) -> Self { - match self.take_with_key_type(key, et, &cmp) { + pub fn take_with(&mut self, key: &Option, et: &ExprTerm, cmp: F, reverse: bool) -> Self { + match self.take_with_key_type(key, et, &cmp, reverse) { Some(vw) => vw, _ => { match self.val.take() { Value::Array(mut vec) => { let mut ret = vec.iter_mut() - .filter(|v| Self::cmp_with_term(&v, et, &cmp, false)) + .filter(|v| Self::cmp_with_term(&v, et, &cmp, false, reverse)) .map(|v| v.take()) .collect(); ValueWrapper::new(Value::Array(ret), false) } other => { - if Self::cmp_with_term(&other, et, &cmp, false) { + if Self::cmp_with_term(&other, et, &cmp, false, reverse) { ValueWrapper::new(other, false) } else { ValueWrapper::new(Value::Null, false) @@ -257,4 +258,45 @@ impl ValueWrapper { vw.replace(Value::Array(list)); vw } + + pub fn into_term(&mut self, key: &mut Option) -> TermContext { + match self.val.take() { + Value::String(s) => TermContext::Constants(ExprTerm::String(s)), + Value::Number(n) => TermContext::Constants(ExprTerm::Number(n.as_f64().unwrap())), + Value::Bool(b) => TermContext::Constants(ExprTerm::Bool(b)), + other => TermContext::Json(match key { + Some(vk) => Some(vk.clone()), + _ => None + }, ValueWrapper::new(other, false)) + } + } + + pub fn filter(&mut self, key: &mut Option) -> Self { + let v = match &mut self.val { + Value::Array(vec) => { + let ret = vec.iter_mut() + .filter(|v| match key { + Some(ValueFilterKey::String(val_key)) => { + v.get(val_key.as_str()).is_some() + } + _ => false + }) + .map(|v| v.take()) + .collect(); + Value::Array(ret) + } + Value::Object(map) => { + match key { + Some(ValueFilterKey::String(val_key)) => match map.get_mut(val_key) { + Some(v) => v.take(), + _ => Value::Null + }, + _ => Value::Null + } + } + other => other.take() + }; + + ValueWrapper::new(v, false) + } }