Invalid result on second attribute check #33

This commit is contained in:
freestrings 2020-02-05 23:24:55 +09:00
parent 636618e4ac
commit ad39c9e668
3 changed files with 218 additions and 104 deletions

View File

@ -4,9 +4,12 @@
<option name="command" value="test --package jsonpath_lib" /> <option name="command" value="test --package jsonpath_lib" />
<option name="allFeatures" value="false" /> <option name="allFeatures" value="false" />
<option name="nocapture" value="true" /> <option name="nocapture" value="true" />
<option name="backtrace" value="SHORT" /> <option name="emulateTerminal" value="false" />
<option name="backtrace" value="NO" />
<option name="workingDirectory" value="file://$PROJECT_DIR$" /> <option name="workingDirectory" value="file://$PROJECT_DIR$" />
<envs /> <envs />
<method v="2" /> <method v="2">
<option name="CARGO.BUILD_TASK_PROVIDER" enabled="true" />
</method>
</configuration> </configuration>
</component> </component>

View File

@ -272,7 +272,15 @@ impl<'a> ExprTerm<'a> {
}) })
.cloned() .cloned()
.collect(), .collect(),
ExprTerm::Json(_, _, vec2) => cmp_fn.cmp_json(vec1, vec2), ExprTerm::Json(parent, _, vec2) => {
if let Some(vec1) = rel {
cmp_fn.cmp_json(vec1, vec2)
} else if let Some(vec2) = parent {
cmp_fn.cmp_json(vec1, vec2)
} else {
cmp_fn.cmp_json(vec1, vec2)
}
}
}; };
if ret.is_empty() { if ret.is_empty() {
@ -398,12 +406,12 @@ fn walk_all<'a>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>) {
} }
fn walk<'a, F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F) fn walk<'a, F>(vec: &[&'a Value], tmp: &mut Vec<&'a Value>, fun: &F)
where
F: Fn(&Value) -> Option<Vec<&Value>>,
{
fn _walk<'a, F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F)
where where
F: Fn(&Value) -> Option<Vec<&Value>>, F: Fn(&Value) -> Option<Vec<&Value>>,
{
fn _walk<'a, F>(v: &'a Value, tmp: &mut Vec<&'a Value>, fun: &F)
where
F: Fn(&Value) -> Option<Vec<&Value>>,
{ {
if let Some(mut ret) = fun(v) { if let Some(mut ret) = fun(v) {
tmp.append(&mut ret); tmp.append(&mut ret);
@ -579,7 +587,20 @@ impl<'a, 'b> Selector<'a, 'b> {
debug!("new_filter_context: {:?}", self.terms); debug!("new_filter_context: {:?}", self.terms);
} }
fn in_filter<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>) -> FilterKey>(&mut self, fun: F) { fn in_filter<F: Fn(&Vec<&'a Value>, &mut Vec<&'a Value>, &mut HashSet<usize>) -> FilterKey>(&mut self, fun: F) {
fn get_parent<'a>(prev: Option<Vec<&'a Value>>, current_value: &Vec<&'a Value>, not_matched: HashSet<usize>) -> Option<Vec<&'a Value>> {
if prev.is_some() {
return prev;
}
let filtered: Vec<&Value> = current_value.iter().enumerate().filter(|(idx, _)| !not_matched.contains(idx))
.map(|(_, v)| *v)
.collect();
Some(filtered)
}
if let Some(peek) = self.terms.pop() { if let Some(peek) = self.terms.pop() {
match peek { match peek {
Some(v) => { Some(v) => {
@ -588,25 +609,19 @@ impl<'a, 'b> Selector<'a, 'b> {
match v { match v {
ExprTerm::Json(rel, fk, vec) => { ExprTerm::Json(rel, fk, vec) => {
let mut tmp = Vec::new(); let mut tmp = Vec::new();
let mut not_matched = HashSet::new();
let filter_key = if let Some(FilterKey::String(key)) = fk { let filter_key = if let Some(FilterKey::String(key)) = fk {
fun( let key_contained = &vec.iter().map(|v| match v {
&vec.iter() Value::Object(map) if map.contains_key(&key) => map.get(&key).unwrap(),
.map(|v| match v { _ => v,
Value::Object(map) if map.contains_key(&key) => { }).collect();
map.get(&key).unwrap() fun(key_contained, &mut tmp, &mut not_matched)
}
_ => v,
})
.collect(),
&mut tmp,
)
} else { } else {
fun(&vec, &mut tmp) fun(&vec, &mut tmp, &mut not_matched)
}; };
let parent = if rel.is_some() { rel } else { Some(vec) }; let parent = get_parent(rel, &vec, not_matched);
self.terms self.terms.push(Some(ExprTerm::Json(parent, Some(filter_key), tmp)));
.push(Some(ExprTerm::Json(parent, Some(filter_key), tmp)));
} }
_ => unreachable!(), _ => unreachable!(),
}; };
@ -616,9 +631,9 @@ impl<'a, 'b> Selector<'a, 'b> {
if let Some(current) = &self.current { if let Some(current) = &self.current {
let mut tmp = Vec::new(); let mut tmp = Vec::new();
let filter_key = fun(current, &mut tmp); let mut not_matched = HashSet::new();
self.terms let filter_key = fun(current, &mut tmp, &mut not_matched);
.push(Some(ExprTerm::Json(None, Some(filter_key), tmp))); self.terms.push(Some(ExprTerm::Json(None, Some(filter_key), tmp)));
} }
} }
} }
@ -626,7 +641,7 @@ impl<'a, 'b> Selector<'a, 'b> {
} }
fn all_in_filter_with_str(&mut self, key: &str) { fn all_in_filter_with_str(&mut self, key: &str) {
self.in_filter(|vec, tmp| { self.in_filter(|vec, tmp, _| {
walk_all_with_str(&vec, tmp, key, true); walk_all_with_str(&vec, tmp, key, true);
FilterKey::All FilterKey::All
}); });
@ -640,6 +655,7 @@ impl<'a, 'b> Selector<'a, 'b> {
tmp: &mut Vec<&'a Value>, tmp: &mut Vec<&'a Value>,
key: &str, key: &str,
visited: &mut HashSet<*const Value>, visited: &mut HashSet<*const Value>,
not_matched: &mut HashSet<usize>,
) { ) {
match v { match v {
Value::Object(map) => { Value::Object(map) => {
@ -653,18 +669,40 @@ impl<'a, 'b> Selector<'a, 'b> {
} }
Value::Array(vec) => { Value::Array(vec) => {
for v in vec { for v in vec {
_collect(v, tmp, key, visited); _collect(v, tmp, key, visited, not_matched);
} }
} }
_ => {} _ => {}
} }
} }
self.in_filter(|vec, tmp| { self.in_filter(|vec, tmp, not_matched| {
let mut visited = HashSet::new(); let mut visited = HashSet::new();
for v in vec { for (idx, v) in vec.iter().enumerate() {
_collect(v, tmp, key, &mut visited); match v {
Value::Object(map) => {
if map.contains_key(key) {
let ptr = *v as *const Value;
if !visited.contains(&ptr) {
visited.insert(ptr);
tmp.push(v)
}
} else {
not_matched.insert(idx);
}
}
Value::Array(vec) => {
not_matched.insert(idx);
for v in vec {
_collect(v, tmp, key, &mut visited, not_matched);
}
}
_ => {
not_matched.insert(idx);
}
}
} }
FilterKey::String(key.to_owned()) FilterKey::String(key.to_owned())
}); });

View File

@ -141,78 +141,78 @@ fn return_type() {
fn op_default() { fn op_default() {
setup(); setup();
select_and_then_compare( // select_and_then_compare(
"$.school[?(@.friends == @.friends)]", // "$.school[?(@.friends == @.friends)]",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([{ // json!([{
"friends": [ // "friends": [
{"id": 0, "name": "Millicent Norman"}, // {"id": 0, "name": "Millicent Norman"},
{"id": 1, "name": "Vincent Cannon" }, // {"id": 1, "name": "Vincent Cannon" },
{"id": 2, "name": "Gray Berry"} // {"id": 2, "name": "Gray Berry"}
] // ]
}]), // }]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$.friends[?(@.name)]", // "$.friends[?(@.name)]",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([ // json!([
{ "id" : 1, "name" : "Vincent Cannon" }, // { "id" : 1, "name" : "Vincent Cannon" },
{ "id" : 2, "name" : "Gray Berry" } // { "id" : 2, "name" : "Gray Berry" }
]), // ]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$.friends[?(@.id >= 2)]", // "$.friends[?(@.id >= 2)]",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([ // json!([
{ "id" : 2, "name" : "Gray Berry" } // { "id" : 2, "name" : "Gray Berry" }
]), // ]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$.friends[?(@.id >= 2 || @.id == 1)]", // "$.friends[?(@.id >= 2 || @.id == 1)]",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([ // json!([
{ "id" : 2, "name" : "Gray Berry" }, // { "id" : 2, "name" : "Gray Berry" },
{ "id" : 1, "name" : "Vincent Cannon" } // { "id" : 1, "name" : "Vincent Cannon" }
]), // ]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]", // "$.friends[?( (@.id >= 2 || @.id == 1) && @.id == 0)]",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([Value::Null]), // json!([Value::Null]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$..friends[?(@.id == $.index)].id", // "$..friends[?(@.id == $.index)].id",
read_json("./benchmark/data_obj.json"), // read_json("./benchmark/data_obj.json"),
json!([0, 0]), // json!([0, 0]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$..book[?($.store.bicycle.price < @.price)].price", // "$..book[?($.store.bicycle.price < @.price)].price",
read_json("./benchmark/example.json"), // read_json("./benchmark/example.json"),
json!([22.99]), // json!([22.99]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price", // "$..book[?( (@.price == 12.99 || @.category == 'reference') && @.price > 10)].price",
read_json("./benchmark/example.json"), // read_json("./benchmark/example.json"),
json!([12.99]), // json!([12.99]),
); // );
//
select_and_then_compare( // select_and_then_compare(
"$..[?(@.age > 40)]", // "$..[?(@.age > 40)]",
json!([ // json!([
{ "name": "이름1", "age": 40, "phone": "+33 12341234" }, // { "name": "이름1", "age": 40, "phone": "+33 12341234" },
{ "name": "이름2", "age": 42, "phone": "++44 12341234" } // { "name": "이름2", "age": 42, "phone": "++44 12341234" }
]), // ]),
json!([ // json!([
{ "name" : "이름2", "age" : 42, "phone" : "++44 12341234" } // { "name" : "이름2", "age" : 42, "phone" : "++44 12341234" }
]), // ]),
); // );
select_and_then_compare( select_and_then_compare(
"$..[?(@.age >= 30)]", "$..[?(@.age >= 30)]",
@ -353,7 +353,7 @@ fn op_compare() {
r#"$[?(true == 1)]"#, r#"$[?(true == 1)]"#,
r#"$[?(@ == 1)]"#, r#"$[?(@ == 1)]"#,
] ]
.iter() .iter()
{ {
select_and_then_compare(path, json!({}), json!([Value::Null])); select_and_then_compare(path, json!({}), json!([Value::Null]));
} }
@ -702,4 +702,77 @@ fn current_path() {
} }
]), ]),
); );
}
#[test]
fn bugs33() {
setup();
select_and_then_compare(
"$..[?(@.first.second)]",
json!({
"foo": {
"first": { "second": "value" }
},
"foo2": {
"first": {}
},
"foo3": {
}
}),
json!([
{
"first": {
"second": "value"
}
}
]),
);
select_and_then_compare(
"$..[?(@.first && @.first.second)]",
json!({
"foo": {
"first": { "second": "value" }
},
"foo2": {
"first": {}
},
"foo3": {
}
}),
json!([
{
"first": {
"second": "value"
}
}
]),
);
select_and_then_compare(
"$..[?(@.b.c.d && @.b)]",
json!({
"a": {
"b": {
"c": {
"d" : {
"e" : 1
}
}
}
}
}),
json!([
{
"b" : {
"c" : {
"d" : {
"e" : 1
}
}
}
}
]),
);
} }