diff --git a/serde_json_path/src/path.rs b/serde_json_path/src/path.rs index c957370..f8f98a6 100644 --- a/serde_json_path/src/path.rs +++ b/serde_json_path/src/path.rs @@ -4,7 +4,7 @@ use serde::{de::Visitor, Deserialize, Serialize}; use serde_json::Value; use serde_json_path_core::{ node::NodeList, - spec::query::{Query, Queryable}, + spec::query::{Query, QueryResult, Queryable}, }; use crate::{parser::parse_query_main, ParseError}; @@ -73,7 +73,33 @@ impl JsonPath { /// # } /// ``` pub fn query<'b>(&self, value: &'b Value) -> NodeList<'b> { - self.0.query(value, value).into() + self.0 + .query(value, value, vec![]) + .into_iter() + .map(|(_path, value)| value) + .collect::>() + .into() + } + + /// Query a [`serde_json_path_core::spec::query::QueryResult`] with each + /// match having a path as [`Vec`] and value as + /// [`serde_json::Value`] + /// + /// # Example + /// ```rust + /// # use serde_json::json; + /// # use serde_json_path::JsonPath; + /// # fn main() -> Result<(), serde_json_path::ParseError> { + /// let path = JsonPath::parse("$.foo[::2]")?; + /// let value = json!({"foo": [1, 2, 3, 4]}); + /// let result = path.query_path_and_value(&value); + /// assert_eq!(result[1].0, vec!["foo", "2"]); + /// assert_eq!(result[1].1, 3); + /// # Ok(()) + /// # } + /// ``` + pub fn query_path_and_value<'b>(&self, value: &'b Value) -> QueryResult<'b> { + self.0.query(value, value, vec![]) } } diff --git a/serde_json_path_core/src/spec/functions.rs b/serde_json_path_core/src/spec/functions.rs index 009e969..01cec6f 100644 --- a/serde_json_path_core/src/spec/functions.rs +++ b/serde_json_path_core/src/spec/functions.rs @@ -581,11 +581,17 @@ impl FunctionExprArg { fn evaluate<'a, 'b: 'a>(&'a self, current: &'b Value, root: &'b Value) -> JsonPathValue<'a> { match self { FunctionExprArg::Literal(lit) => lit.into(), - FunctionExprArg::SingularQuery(q) => match q.eval_query(current, root) { - Some(n) => JsonPathValue::Node(n), + FunctionExprArg::SingularQuery(q) => match q.eval_query(current, root, vec![]) { + Some(n) => JsonPathValue::Node(n.1), None => JsonPathValue::Nothing, }, - FunctionExprArg::FilterQuery(q) => JsonPathValue::Nodes(q.query(current, root).into()), + FunctionExprArg::FilterQuery(q) => JsonPathValue::Nodes( + q.query(current, root, vec![]) + .into_iter() + .map(|(_, nodes)| nodes) + .collect::>() + .into(), + ), FunctionExprArg::LogicalExpr(l) => match l.test_filter(current, root) { true => JsonPathValue::Logical(LogicalType::True), false => JsonPathValue::Logical(LogicalType::False), diff --git a/serde_json_path_core/src/spec/query.rs b/serde_json_path_core/src/spec/query.rs index 5ee171a..b5c98d1 100644 --- a/serde_json_path_core/src/spec/query.rs +++ b/serde_json_path_core/src/spec/query.rs @@ -29,10 +29,21 @@ mod sealed { impl Sealed for SingularQuery {} } +/// Traversed path up to current node +pub type TraversedPath = Vec; + +/// A query result with each match having a path and value +pub type QueryResult<'a> = Vec<(Vec, &'a Value)>; + /// A type that is query-able pub trait Queryable: sealed::Sealed { /// Query `self` using a current node, and the root node - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value>; + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b>; } /// Represents a JSONPath expression @@ -83,15 +94,20 @@ pub enum QueryKind { impl Queryable for Query { #[cfg_attr(feature = "trace", tracing::instrument(name = "Main Query", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { let mut query = match self.kind { - QueryKind::Root => vec![root], - QueryKind::Current => vec![current], + QueryKind::Root => vec![(traversed_path.to_vec(), root)], + QueryKind::Current => vec![(traversed_path.to_vec(), current)], }; for segment in &self.segments { let mut new_query = Vec::new(); - for q in &query { - new_query.append(&mut segment.query(q, root)); + for (traversed_path, q) in &query { + new_query.append(&mut segment.query(q, root, traversed_path.clone())); } query = new_query; } diff --git a/serde_json_path_core/src/spec/segment.rs b/serde_json_path_core/src/spec/segment.rs index 5ce1b0b..137f3bc 100644 --- a/serde_json_path_core/src/spec/segment.rs +++ b/serde_json_path_core/src/spec/segment.rs @@ -1,7 +1,10 @@ //! Types representing segments in JSONPath use serde_json::Value; -use super::{query::Queryable, selector::Selector}; +use super::{ + query::{QueryResult, Queryable, TraversedPath}, + selector::Selector, +}; /// A segment of a JSONPath query #[derive(Debug, PartialEq, Eq, Clone)] @@ -48,25 +51,35 @@ pub enum QuerySegmentKind { impl Queryable for QuerySegment { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Path Segment", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { - let mut query = self.segment.query(current, root); + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { + let mut query = self.segment.query(current, root, traversed_path.clone()); if matches!(self.kind, QuerySegmentKind::Descendant) { - query.append(&mut descend(self, current, root)); + query.append(&mut descend(self, current, root, traversed_path)); } query } } #[cfg_attr(feature = "trace", tracing::instrument(name = "Descend", level = "trace", parent = None, ret))] -fn descend<'b>(segment: &QuerySegment, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { +fn descend<'b>( + segment: &QuerySegment, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, +) -> QueryResult<'b> { let mut query = Vec::new(); if let Some(list) = current.as_array() { for v in list { - query.append(&mut segment.query(v, root)); + query.append(&mut segment.query(v, root, traversed_path.clone())); } } else if let Some(obj) = current.as_object() { for (_, v) in obj { - query.append(&mut segment.query(v, root)); + query.append(&mut segment.query(v, root, traversed_path.clone())); } } query @@ -145,29 +158,37 @@ impl std::fmt::Display for Segment { impl Queryable for Segment { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Segment", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { let mut query = Vec::new(); match self { Segment::LongHand(selectors) => { for selector in selectors { - query.append(&mut selector.query(current, root)); + query.append(&mut selector.query(current, root, traversed_path.clone())); } } Segment::DotName(key) => { if let Some(obj) = current.as_object() { if let Some(v) = obj.get(key) { - query.push(v); + query.push(([traversed_path.as_slice(), &[key.clone()]].concat(), v)); } } } Segment::Wildcard => { if let Some(list) = current.as_array() { - for v in list { - query.push(v); + for (index, v) in list.iter().enumerate() { + query.push(( + [traversed_path.as_slice(), &[index.to_string()]].concat(), + v, + )); } } else if let Some(obj) = current.as_object() { - for (_, v) in obj { - query.push(v); + for (key, v) in obj { + query.push(([traversed_path.as_slice(), &[key.clone()]].concat(), v)); } } } diff --git a/serde_json_path_core/src/spec/selector/filter.rs b/serde_json_path_core/src/spec/selector/filter.rs index 563a535..f93a931 100644 --- a/serde_json_path_core/src/spec/selector/filter.rs +++ b/serde_json_path_core/src/spec/selector/filter.rs @@ -3,7 +3,7 @@ use serde_json::{Number, Value}; use crate::spec::{ functions::{FunctionExpr, JsonPathValue, Validated}, - query::{Query, QueryKind, Queryable}, + query::{Query, QueryKind, QueryResult, Queryable, TraversedPath}, segment::{QuerySegment, Segment}, }; @@ -55,8 +55,13 @@ impl std::fmt::Display for Filter { impl Queryable for Filter { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Filter", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { - if let Some(list) = current.as_array() { + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { + let value = if let Some(list) = current.as_array() { list.iter() .filter(|v| self.0.test_filter(v, root)) .collect() @@ -67,7 +72,12 @@ impl Queryable for Filter { .collect() } else { vec![] - } + }; + + value + .into_iter() + .map(|v| (traversed_path.to_vec(), v)) + .collect() } } @@ -197,7 +207,7 @@ impl std::fmt::Display for ExistExpr { impl TestFilter for ExistExpr { #[cfg_attr(feature = "trace", tracing::instrument(name = "Test Exists Expr", level = "trace", parent = None, ret))] fn test_filter<'b>(&self, current: &'b Value, root: &'b Value) -> bool { - !self.0.query(current, root).is_empty() + !self.0.query(current, root, vec![]).is_empty() } } @@ -393,8 +403,8 @@ impl Comparable { ) -> JsonPathValue<'a> { match self { Comparable::Literal(lit) => lit.into(), - Comparable::SingularQuery(sp) => match sp.eval_query(current, root) { - Some(v) => JsonPathValue::Node(v), + Comparable::SingularQuery(sp) => match sp.eval_query(current, root, vec![]) { + Some(v) => JsonPathValue::Node(v.1), None => JsonPathValue::Nothing, }, Comparable::FunctionExpr(expr) => expr.evaluate(current, root), @@ -514,26 +524,38 @@ pub struct SingularQuery { impl SingularQuery { /// Evaluate the singular query #[cfg_attr(feature = "trace", tracing::instrument(name = "SingularQuery::eval_query", level = "trace", parent = None, ret))] - pub fn eval_query<'b>(&self, current: &'b Value, root: &'b Value) -> Option<&'b Value> { + pub fn eval_query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> Option<(Vec, &'b Value)> { let mut target = match self.kind { - SingularQueryKind::Absolute => root, - SingularQueryKind::Relative => current, + SingularQueryKind::Absolute => (traversed_path.to_vec(), root), + SingularQueryKind::Relative => (traversed_path.to_vec(), current), }; for segment in &self.segments { match segment { SingularQuerySegment::Name(name) => { - if let Some(t) = target.as_object().and_then(|o| o.get(name.as_str())) { - target = t; + if let Some(t) = target.1.as_object().and_then(|o| o.get(name.as_str())) { + target = ( + [traversed_path.as_slice(), &[name.as_str().to_owned()]].concat(), + t, + ); } else { return None; } } SingularQuerySegment::Index(index) => { - if let Some(t) = target - .as_array() - .and_then(|l| usize::try_from(index.0).ok().and_then(|i| l.get(i))) - { - target = t; + if let Some((index, t)) = target.1.as_array().and_then(|l| { + usize::try_from(index.0) + .ok() + .and_then(|i| l.get(i).map(|v| (i, v))) + }) { + target = ( + [traversed_path.as_slice(), &[index.to_string()]].concat(), + t, + ); } else { return None; } @@ -559,8 +581,13 @@ impl TryFrom for SingularQuery { } impl Queryable for SingularQuery { - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { - match self.eval_query(current, root) { + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { + match self.eval_query(current, root, traversed_path) { Some(v) => vec![v], None => vec![], } diff --git a/serde_json_path_core/src/spec/selector/index.rs b/serde_json_path_core/src/spec/selector/index.rs index ab369df..239e3dd 100644 --- a/serde_json_path_core/src/spec/selector/index.rs +++ b/serde_json_path_core/src/spec/selector/index.rs @@ -1,7 +1,7 @@ //! Index selectors in JSONPath use serde_json::Value; -use crate::spec::query::Queryable; +use crate::spec::query::{QueryResult, Queryable, TraversedPath}; /// For selecting array elements by their index /// @@ -17,8 +17,13 @@ impl std::fmt::Display for Index { impl Queryable for Index { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Index", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, _root: &'b Value) -> Vec<&'b Value> { - if let Some(list) = current.as_array() { + fn query<'b>( + &self, + current: &'b Value, + _root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { + let values = if let Some(list) = current.as_array() { if self.0 < 0 { self.0 .checked_abs() @@ -36,7 +41,17 @@ impl Queryable for Index { } } else { vec![] - } + }; + + values + .into_iter() + .map(|v| { + ( + [traversed_path.as_slice(), &[self.0.to_string()]].concat(), + v, + ) + }) + .collect() } } diff --git a/serde_json_path_core/src/spec/selector/mod.rs b/serde_json_path_core/src/spec/selector/mod.rs index b6bf82e..c406b0a 100644 --- a/serde_json_path_core/src/spec/selector/mod.rs +++ b/serde_json_path_core/src/spec/selector/mod.rs @@ -8,7 +8,7 @@ use serde_json::Value; use self::{filter::Filter, index::Index, name::Name, slice::Slice}; -use super::query::Queryable; +use super::query::{QueryResult, Queryable, TraversedPath}; /// A JSONPath selector #[derive(Debug, PartialEq, Eq, Clone)] @@ -49,24 +49,36 @@ impl std::fmt::Display for Selector { impl Queryable for Selector { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Selector", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, root: &'b Value) -> Vec<&'b Value> { + fn query<'b>( + &self, + current: &'b Value, + root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { let mut query = Vec::new(); match self { - Selector::Name(name) => query.append(&mut name.query(current, root)), + Selector::Name(name) => query.append(&mut name.query(current, root, traversed_path)), Selector::Wildcard => { if let Some(list) = current.as_array() { - for v in list { - query.push(v); + for (index, v) in list.iter().enumerate() { + query.push(( + [traversed_path.as_slice(), &[index.to_string()]].concat(), + v, + )); } } else if let Some(obj) = current.as_object() { - for (_, v) in obj { - query.push(v); + for (key, v) in obj { + query.push(([traversed_path.as_slice(), &[key.clone()]].concat(), v)); } } } - Selector::Index(index) => query.append(&mut index.query(current, root)), - Selector::ArraySlice(slice) => query.append(&mut slice.query(current, root)), - Selector::Filter(filter) => query.append(&mut filter.query(current, root)), + Selector::Index(index) => query.append(&mut index.query(current, root, traversed_path)), + Selector::ArraySlice(slice) => { + query.append(&mut slice.query(current, root, traversed_path)) + } + Selector::Filter(filter) => { + query.append(&mut filter.query(current, root, traversed_path)) + } } query } diff --git a/serde_json_path_core/src/spec/selector/name.rs b/serde_json_path_core/src/spec/selector/name.rs index de2c350..6b3c674 100644 --- a/serde_json_path_core/src/spec/selector/name.rs +++ b/serde_json_path_core/src/spec/selector/name.rs @@ -1,7 +1,7 @@ //! Name selector for selecting object keys in JSONPath use serde_json::Value; -use crate::spec::query::Queryable; +use crate::spec::query::{QueryResult, Queryable, TraversedPath}; /// Select a single JSON object key #[derive(Debug, PartialEq, Eq, Clone)] @@ -22,12 +22,27 @@ impl std::fmt::Display for Name { impl Queryable for Name { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Name", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, _root: &'b Value) -> Vec<&'b Value> { - if let Some(obj) = current.as_object() { + fn query<'b>( + &self, + current: &'b Value, + _root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { + let values = if let Some(obj) = current.as_object() { obj.get(&self.0).into_iter().collect() } else { vec![] - } + }; + + values + .into_iter() + .map(|v| { + ( + [traversed_path.as_slice(), &[self.0.to_string()]].concat(), + v, + ) + }) + .collect() } } diff --git a/serde_json_path_core/src/spec/selector/slice.rs b/serde_json_path_core/src/spec/selector/slice.rs index ffe1140..4548b2f 100644 --- a/serde_json_path_core/src/spec/selector/slice.rs +++ b/serde_json_path_core/src/spec/selector/slice.rs @@ -1,7 +1,7 @@ //! Slice selectors for selecting array slices in JSONPath use serde_json::Value; -use crate::spec::query::Queryable; +use crate::spec::query::{QueryResult, Queryable, TraversedPath}; /// A slice selector #[derive(Debug, PartialEq, Eq, Default, Clone, Copy)] @@ -63,7 +63,12 @@ impl Slice { impl Queryable for Slice { #[cfg_attr(feature = "trace", tracing::instrument(name = "Query Slice", level = "trace", parent = None, ret))] - fn query<'b>(&self, current: &'b Value, _root: &'b Value) -> Vec<&'b Value> { + fn query<'b>( + &self, + current: &'b Value, + _root: &'b Value, + traversed_path: TraversedPath, + ) -> QueryResult<'b> { if let Some(list) = current.as_array() { let mut query = Vec::new(); let step = self.step.unwrap_or(1); @@ -85,7 +90,7 @@ impl Queryable for Slice { let mut i = lower; while i < upper { if let Some(v) = usize::try_from(i).ok().and_then(|i| list.get(i)) { - query.push(v); + query.push(([traversed_path.as_slice(), &[i.to_string()]].concat(), v)); } i += step; } @@ -108,7 +113,7 @@ impl Queryable for Slice { let mut i = upper; while lower < i { if let Some(v) = usize::try_from(i).ok().and_then(|i| list.get(i)) { - query.push(v); + query.push(([traversed_path.as_slice(), &[i.to_string()]].concat(), v)); } i += step; }