Skip to content

Commit f766b50

Browse files
committed
Add JSONPath.query() for getting a query iterator from a compiled path
1 parent 8cada95 commit f766b50

File tree

5 files changed

+53
-3
lines changed

5 files changed

+53
-3
lines changed

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@
99
**Features**
1010

1111
- Allow JSONPath filter expression membership operators (`contains` and `in`) to operate on object/mapping data as well as arrays/sequences. See [#55](https://github.com/jg-rp/python-jsonpath/issues/55).
12-
- Added a `select` method to the JSONPath [query iterator interface](https://jg-rp.github.io/python-jsonpath/query/), generating a projection of each JSONPath match by selecting a subset of its values.
12+
- Added a `select()` method to the JSONPath [query iterator interface](https://jg-rp.github.io/python-jsonpath/query/), generating a projection of each JSONPath match by selecting a subset of its values.
13+
- Added the `query()` method to the `JSONPath` class. Get a query iterator from an already compiled path.
1314
- Added the `addne` and `addap` operations to [JSONPatch](https://jg-rp.github.io/python-jsonpath/api/#jsonpath.JSONPatch). `addne` (add if not exists) is like the standard `add` operation, but only adds object keys/values if the key does not exist. `addap` (add or append) is like the standard `add` operation, but assumes an index of `-` if the target index can not be resolved.
1415

1516
## Version 1.1.1

docs/query.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
In addition to [`findall()`](api.md#jsonpath.JSONPathEnvironment.findall) and [`finditer()`](api.md#jsonpath.JSONPathEnvironment.finditer), covered in the [quick start guide](./quickstart.md), Python JSONPath offers a fluent _query iterator_ interface.
66

7-
[`Query`](api.md#jsonpath.Query) objects provide chainable methods for manipulating a [`JSONPathMatch`](api.md#jsonpath.JSONPathMatch) iterator, like you'd get from `finditer()`. Obtain a `Query` object using the package-level `query()` function or [`JSONPathEnvironment.query()`](api.md#jsonpath.JSONPathEnvironment.query).
7+
[`Query`](api.md#jsonpath.Query) objects provide chainable methods for manipulating a [`JSONPathMatch`](api.md#jsonpath.JSONPathMatch) iterator, like you'd get from `finditer()`. Obtain a `Query` object using the package-level `query()` function, [`JSONPathEnvironment.query()`](api.md#jsonpath.JSONPathEnvironment.query) or using the [`query()`](api.md#jsonpath.JSONPath.query) method of a compiled JSONPath.
88

99
This example uses the query API to skip the first five matches, limit the total number of matches to ten, then get the value associated with each match.
1010

jsonpath/env.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -318,7 +318,7 @@ def query(
318318
data: Union[str, IOBase, Sequence[Any], Mapping[str, Any]],
319319
filter_context: Optional[FilterContextVars] = None,
320320
) -> Query:
321-
"""Return a `Query` object over matches found by applying _path_ to _data_.
321+
"""Return a `Query` iterator over matches found by applying _path_ to _data_.
322322
323323
`Query` objects are iterable.
324324
@@ -353,6 +353,21 @@ def query(
353353
for obj in jsonpath.query("$.foo..bar", data).limit(5).values():
354354
...
355355
```
356+
357+
Arguments:
358+
path: The JSONPath as a string.
359+
data: A JSON document or Python object implementing the `Sequence`
360+
or `Mapping` interfaces.
361+
filter_context: Arbitrary data made available to filters using
362+
the _filter context_ selector.
363+
364+
Returns:
365+
A query iterator.
366+
367+
Raises:
368+
JSONPathSyntaxError: If the path is invalid.
369+
JSONPathTypeError: If a filter expression attempts to use types in
370+
an incompatible way.
356371
"""
357372
return Query(self.finditer(path, data, filter_context=filter_context), self)
358373

jsonpath/path.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
from typing import Union
1616

1717
from jsonpath._data import load_data
18+
from jsonpath.fluent_api import Query
1819
from jsonpath.match import FilterContextVars
1920
from jsonpath.match import JSONPathMatch
2021
from jsonpath.selectors import IndexSelector
@@ -210,6 +211,30 @@ def match(
210211
except StopIteration:
211212
return None
212213

214+
def query(
215+
self,
216+
data: Union[str, IOBase, Sequence[Any], Mapping[str, Any]],
217+
*,
218+
filter_context: Optional[FilterContextVars] = None,
219+
) -> Query:
220+
"""Return a `Query` iterator over matches found by applying this path to _data_.
221+
222+
Arguments:
223+
data: A JSON document or Python object implementing the `Sequence`
224+
or `Mapping` interfaces.
225+
filter_context: Arbitrary data made available to filters using
226+
the _filter context_ selector.
227+
228+
Returns:
229+
A query iterator.
230+
231+
Raises:
232+
JSONPathSyntaxError: If the path is invalid.
233+
JSONPathTypeError: If a filter expression attempts to use types in
234+
an incompatible way.
235+
"""
236+
return Query(self.finditer(data, filter_context=filter_context), self.env)
237+
213238
def empty(self) -> bool:
214239
"""Return `True` if this path has no selectors."""
215240
return not bool(self.selectors)

tests/test_fluent_api.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
"""Test cases for the fluent API."""
2+
23
import pytest
34

45
from jsonpath import JSONPathMatch
56
from jsonpath import JSONPointer
7+
from jsonpath import compile
68
from jsonpath import query
79

810

@@ -267,3 +269,10 @@ def test_query_take_more() -> None:
267269
assert len(head) == 4 # noqa: PLR2004
268270
assert head == [0, 1, 2, 3]
269271
assert list(it.values()) == []
272+
273+
274+
def test_query_from_compiled_path() -> None:
275+
"""Test that we can get a query iterator from a compiled path."""
276+
path = compile("$.some.*")
277+
it = path.query({"some": [0, 1, 2, 3]}).values()
278+
assert list(it) == [0, 1, 2, 3]

0 commit comments

Comments
 (0)