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)
+ }
}