Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow Query.select() to accept precompiled paths #64

Merged
merged 1 commit into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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}]
Loading