Skip to content

Commit

Permalink
Merge pull request #64 from jg-rp/compiled-select
Browse files Browse the repository at this point in the history
Allow `Query.select()` to accept precompiled paths
  • Loading branch information
jg-rp authored Jul 11, 2024
2 parents c1710ae + 586fbc2 commit b5da1d4
Show file tree
Hide file tree
Showing 3 changed files with 22 additions and 7 deletions.
8 changes: 6 additions & 2 deletions docs/query.md
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,16 @@ for product in query("$..products.*", data).values():
{'title': 'Beanie', 'description': 'Winter running hat.', 'price': 9.0}
```

We can select nested values too.
We can select nested values too, and arguments to `select()` can be pre-compiled paths.

```python
import jsonpath

# ...

for product in query("$..products.*", data).select("title", "social.shares"):
projection = (jsonpath.compile("title"), jsonpath.compile("social.shares"))

for product in jsonpath.query("$..products.*", data).select(*projection):
print(product)
```

Expand Down
13 changes: 8 additions & 5 deletions jsonpath/fluent_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from .patch import JSONPatch

if TYPE_CHECKING:
from jsonpath import CompoundJSONPath
from jsonpath import JSONPath
from jsonpath import JSONPathEnvironment
from jsonpath import JSONPathMatch
from jsonpath import JSONPointer
Expand Down Expand Up @@ -191,7 +193,7 @@ def take(self, n: int) -> Query:

def select(
self,
*expressions: str,
*expressions: Union[str, JSONPath, CompoundJSONPath],
projection: Projection = Projection.RELATIVE,
) -> Iterable[object]:
"""Query projection using relative JSONPaths.
Expand All @@ -217,7 +219,7 @@ def select(
def _select(
self,
match: JSONPathMatch,
expressions: Tuple[str, ...],
expressions: Tuple[Union[str, JSONPath, CompoundJSONPath], ...],
projection: Projection,
) -> object:
if isinstance(match.obj, str):
Expand All @@ -232,20 +234,21 @@ def _select(
patch = JSONPatch()

for expr in expressions:
self._patch(match, expr, patch, projection)
path = self._env.compile(expr) if isinstance(expr, str) else expr
self._patch(match, path, patch, projection)

return _fix_sparse_arrays(patch.apply(obj))

def _patch(
self,
match: JSONPathMatch,
expr: str,
path: Union[JSONPath, CompoundJSONPath],
patch: JSONPatch,
projection: Projection,
) -> None:
root_pointer = match.pointer()

for rel_match in self._env.finditer(expr, match.obj): # type: ignore
for rel_match in path.finditer(match.obj): # type: ignore
if projection == Projection.FLAT:
patch.addap("/-", rel_match.obj)
elif projection == Projection.ROOT:
Expand Down
8 changes: 8 additions & 0 deletions tests/test_query_projection.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,3 +159,11 @@ def test_sparse_array_selection() -> None:
assert list(it) == [
{"categories": [{"products": [{"title": "Beanie", "social": {"shares": 7}}]}]}
]


def test_pre_compiled_select_many() -> None:
expr = "$.*"
data = [{"a": 1, "b": 1, "c": 1}, {"a": 2, "b": 2, "c": 2}, {"b": 3, "a": 3}]
projection = (jsonpath.compile("a"), "c")
it = jsonpath.query(expr, data).select(*projection)
assert list(it) == [{"a": 1, "c": 1}, {"a": 2, "c": 2}, {"a": 3}]

0 comments on commit b5da1d4

Please sign in to comment.