1use crate::path::parser::{CompareOp, JsonPath, JsonPathPredicate, PredicateValue};
7use crate::value::JsonbValue;
8use alloc::vec::Vec;
9
10impl JsonbValue {
11 pub fn query<'a>(&'a self, path: &JsonPath) -> Vec<&'a JsonbValue> {
13 let mut results = Vec::new();
14 eval_path(self, path, &mut results);
15 results
16 }
17
18 pub fn query_first<'a>(&'a self, path: &JsonPath) -> Option<&'a JsonbValue> {
20 self.query(path).into_iter().next()
21 }
22}
23
24fn eval_path<'a>(value: &'a JsonbValue, path: &JsonPath, results: &mut Vec<&'a JsonbValue>) {
25 match path {
26 JsonPath::Root => {
27 results.push(value);
28 }
29 JsonPath::Field(parent, field) => {
30 let parent_results = eval_path_collect(value, parent);
31 for v in parent_results {
32 if let Some(obj) = v.as_object() {
33 if let Some(field_value) = obj.get(field) {
34 results.push(field_value);
35 }
36 }
37 }
38 }
39 JsonPath::Index(parent, index) => {
40 let parent_results = eval_path_collect(value, parent);
41 for v in parent_results {
42 if let Some(arr) = v.as_array() {
43 if let Some(item) = arr.get(*index) {
44 results.push(item);
45 }
46 }
47 }
48 }
49 JsonPath::Slice(parent, start, end) => {
50 let parent_results = eval_path_collect(value, parent);
51 for v in parent_results {
52 if let Some(arr) = v.as_array() {
53 let start_idx = start.unwrap_or(0);
54 let end_idx = end.unwrap_or(arr.len());
55 for item in arr.iter().skip(start_idx).take(end_idx - start_idx) {
56 results.push(item);
57 }
58 }
59 }
60 }
61 JsonPath::Wildcard(parent) => {
62 let parent_results = eval_path_collect(value, parent);
63 for v in parent_results {
64 match v {
65 JsonbValue::Array(arr) => {
66 for item in arr {
67 results.push(item);
68 }
69 }
70 JsonbValue::Object(obj) => {
71 for (_, val) in obj.iter() {
72 results.push(val);
73 }
74 }
75 _ => {}
76 }
77 }
78 }
79 JsonPath::RecursiveField(parent, field) => {
80 let parent_results = eval_path_collect(value, parent);
81 for v in parent_results {
82 recursive_field_search(v, field, results);
83 }
84 }
85 JsonPath::Filter(parent, predicate) => {
86 let parent_results = eval_path_collect(value, parent);
87 for v in parent_results {
88 if let Some(arr) = v.as_array() {
89 for item in arr {
90 if eval_predicate(item, predicate) {
91 results.push(item);
92 }
93 }
94 }
95 }
96 }
97 }
98}
99
100fn eval_path_collect<'a>(value: &'a JsonbValue, path: &JsonPath) -> Vec<&'a JsonbValue> {
101 let mut results = Vec::new();
102 eval_path(value, path, &mut results);
103 results
104}
105
106fn recursive_field_search<'a>(
107 value: &'a JsonbValue,
108 field: &str,
109 results: &mut Vec<&'a JsonbValue>,
110) {
111 match value {
112 JsonbValue::Object(obj) => {
113 if let Some(v) = obj.get(field) {
114 results.push(v);
115 }
116 for (_, v) in obj.iter() {
117 recursive_field_search(v, field, results);
118 }
119 }
120 JsonbValue::Array(arr) => {
121 for item in arr {
122 recursive_field_search(item, field, results);
123 }
124 }
125 _ => {}
126 }
127}
128
129fn eval_predicate(value: &JsonbValue, predicate: &JsonPathPredicate) -> bool {
130 match predicate {
131 JsonPathPredicate::Exists(field) => {
132 if let Some(obj) = value.as_object() {
133 obj.contains_key(field)
134 } else {
135 false
136 }
137 }
138 JsonPathPredicate::Compare(field, op, expected) => {
139 if let Some(obj) = value.as_object() {
140 if let Some(actual) = obj.get(field) {
141 compare_values(actual, op, expected)
142 } else {
143 false
144 }
145 } else {
146 false
147 }
148 }
149 JsonPathPredicate::And(left, right) => {
150 eval_predicate(value, left) && eval_predicate(value, right)
151 }
152 JsonPathPredicate::Or(left, right) => {
153 eval_predicate(value, left) || eval_predicate(value, right)
154 }
155 JsonPathPredicate::Not(inner) => !eval_predicate(value, inner),
156 }
157}
158
159fn compare_values(actual: &JsonbValue, op: &CompareOp, expected: &PredicateValue) -> bool {
160 match (actual, expected) {
161 (JsonbValue::Null, PredicateValue::Null) => matches!(op, CompareOp::Eq),
162 (JsonbValue::Bool(a), PredicateValue::Bool(b)) => match op {
163 CompareOp::Eq => a == b,
164 CompareOp::Ne => a != b,
165 _ => false,
166 },
167 (JsonbValue::Number(a), PredicateValue::Number(b)) => match op {
168 CompareOp::Eq => (a - b).abs() < f64::EPSILON,
169 CompareOp::Ne => (a - b).abs() >= f64::EPSILON,
170 CompareOp::Lt => a < b,
171 CompareOp::Le => a <= b,
172 CompareOp::Gt => a > b,
173 CompareOp::Ge => a >= b,
174 },
175 (JsonbValue::String(a), PredicateValue::String(b)) => match op {
176 CompareOp::Eq => a == b,
177 CompareOp::Ne => a != b,
178 CompareOp::Lt => a < b,
179 CompareOp::Le => a <= b,
180 CompareOp::Gt => a > b,
181 CompareOp::Ge => a >= b,
182 },
183 _ => false,
184 }
185}
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190 use crate::value::JsonbObject;
191 use alloc::vec;
192
193 fn make_test_json() -> JsonbValue {
194 let mut user = JsonbObject::new();
195 user.insert("name".into(), JsonbValue::String("Alice".into()));
196 user.insert(
197 "tags".into(),
198 JsonbValue::Array(vec![
199 JsonbValue::String("admin".into()),
200 JsonbValue::String("developer".into()),
201 ]),
202 );
203
204 let mut root = JsonbObject::new();
205 root.insert("user".into(), JsonbValue::Object(user));
206 JsonbValue::Object(root)
207 }
208
209 #[test]
210 fn test_query_root() {
211 let json = make_test_json();
212 let path = JsonPath::parse("$").unwrap();
213 let results = json.query(&path);
214 assert_eq!(results.len(), 1);
215 }
216
217 #[test]
218 fn test_query_field() {
219 let json = make_test_json();
220 let path = JsonPath::parse("$.user.name").unwrap();
221 let results = json.query(&path);
222 assert_eq!(results, vec![&JsonbValue::String("Alice".into())]);
223 }
224
225 #[test]
226 fn test_query_array_index() {
227 let json = make_test_json();
228 let path = JsonPath::parse("$.user.tags[0]").unwrap();
229 let results = json.query(&path);
230 assert_eq!(results, vec![&JsonbValue::String("admin".into())]);
231 }
232
233 #[test]
234 fn test_query_array_slice() {
235 let json = make_test_json();
236 let path = JsonPath::parse("$.user.tags[0:2]").unwrap();
237 let results = json.query(&path);
238 assert_eq!(results.len(), 2);
239 assert_eq!(results[0], &JsonbValue::String("admin".into()));
240 assert_eq!(results[1], &JsonbValue::String("developer".into()));
241 }
242
243 #[test]
244 fn test_query_wildcard() {
245 let json = make_test_json();
246 let path = JsonPath::parse("$.user.tags[*]").unwrap();
247 let results = json.query(&path);
248 assert_eq!(results.len(), 2);
249 }
250
251 #[test]
252 fn test_query_recursive() {
253 let mut inner = JsonbObject::new();
254 inner.insert("name".into(), JsonbValue::String("inner".into()));
255
256 let mut outer = JsonbObject::new();
257 outer.insert("name".into(), JsonbValue::String("outer".into()));
258 outer.insert("child".into(), JsonbValue::Object(inner));
259
260 let json = JsonbValue::Object(outer);
261 let path = JsonPath::parse("$..name").unwrap();
262 let results = json.query(&path);
263 assert_eq!(results.len(), 2);
264 }
265
266 #[test]
267 fn test_query_filter() {
268 let mut item1 = JsonbObject::new();
269 item1.insert("price".into(), JsonbValue::Number(5.0));
270
271 let mut item2 = JsonbObject::new();
272 item2.insert("price".into(), JsonbValue::Number(15.0));
273
274 let mut item3 = JsonbObject::new();
275 item3.insert("price".into(), JsonbValue::Number(8.0));
276
277 let mut root = JsonbObject::new();
278 root.insert(
279 "items".into(),
280 JsonbValue::Array(vec![
281 JsonbValue::Object(item1),
282 JsonbValue::Object(item2),
283 JsonbValue::Object(item3),
284 ]),
285 );
286
287 let json = JsonbValue::Object(root);
288 let path = JsonPath::parse("$.items[?(@.price < 10)]").unwrap();
289 let results = json.query(&path);
290 assert_eq!(results.len(), 2);
291 }
292
293 #[test]
294 fn test_query_first() {
295 let json = make_test_json();
296 let path = JsonPath::parse("$.user.name").unwrap();
297 let result = json.query_first(&path);
298 assert_eq!(result, Some(&JsonbValue::String("Alice".into())));
299 }
300
301 #[test]
302 fn test_query_missing_field() {
303 let json = make_test_json();
304 let path = JsonPath::parse("$.user.email").unwrap();
305 let results = json.query(&path);
306 assert!(results.is_empty());
307 }
308}