1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225
|
# Query Iterators
**_New in version 1.1.0_**
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.
[`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.
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.
```python
from jsonpath import query
# data = ...
values = (
query("$.some[?@.thing]", data)
.skip(5)
.limit(10)
.values()
)
for value in values:
# ...
```
`Query` objects are iterable and can only be iterated once. Pass the query to `list()` (or other sequence) to get a list of results that can be iterated multiple times or otherwise manipulated.
```python
from jsonpath import query
# data = ...
values = list(
query("$.some[?@.thing]", data)
.skip(5)
.limit(10)
.values()
)
print(values[1])
```
## Chainable methods
The following `Query` methods all return `self` (the same `Query` instance), so method calls can be chained to further manipulate the underlying iterator.
| Method | Aliases | Description |
| --------------- | --------------- | -------------------------------------------------- |
| `skip(n: int)` | `drop` | Drop up to _n_ matches from the iterator. |
| `limit(n: int)` | `head`, `first` | Yield at most _n_ matches from the iterator. |
| `tail(n: int)` | `last` | Drop matches from the iterator up to the last _n_. |
## Terminal methods
These are terminal methods of the `Query` class. They can not be chained.
| Method | Aliases | Description |
| ------------- | ------- | ------------------------------------------------------------------------------------------- |
| `values()` | | Return an iterable of objects, one for each match in the iterable. |
| `locations()` | | Return an iterable of normalized paths, one for each match in the iterable. |
| `items()` | | Return an iterable of (object, normalized path) tuples, one for each match in the iterable. |
| `pointers()` | | Return an iterable of `JSONPointer` instances, one for each match in the iterable. |
| `first_one()` | `one` | Return the first `JSONPathMatch`, or `None` if there were no matches. |
| `last_one()` | | Return the last `JSONPathMatch`, or `None` if there were no matches. |
## Take
[`Query.take(self, n: int)`](api.md#jsonpath.Query.take) returns a new `Query` instance, iterating over the next _n_ matches. It leaves the existing query in a safe state, ready to resume iteration of remaining matches.
```python
from jsonpath import query
it = query("$.some.*", {"some": [0, 1, 2, 3]})
for match in it.take(2):
print(match.value) # 0, 1
for value in it.values():
print(value) # 2, 3
```
## Tee
[`tee()`](api.md#jsonpath.Query.tee) creates multiple independent queries from one query iterator. It is not safe to use the initial `Query` instance after calling `tee()`.
```python
from jsonpath import query
it1, it2 = query("$.some[?@.thing]", data).tee()
head = it1.head(10) # first 10 matches
tail = it2.tail(10) # last 10 matches
```
## Select
[`select(*expressions, projection=Projection.RELATIVE)`](api.md/#jsonpath.Query.select) performs JSONPath match projection, selecting a subset of values according to one or more JSONPath query expressions relative to the match location. For example:
```python
from jsonpath import query
data = {
"categories": [
{
"name": "footwear",
"products": [
{
"title": "Trainers",
"description": "Fashionable trainers.",
"price": 89.99,
},
{
"title": "Barefoot Trainers",
"description": "Running trainers.",
"price": 130.00,
"social": {"likes": 12, "shares": 7},
},
],
},
{
"name": "headwear",
"products": [
{
"title": "Cap",
"description": "Baseball cap",
"price": 15.00,
},
{
"title": "Beanie",
"description": "Winter running hat.",
"price": 9.00,
},
],
},
],
"price_cap": 10,
}
for product in query("$..products.*", data).select("title", "price"):
print(product)
```
Which selects just the `title` and `price` fields for each product.
```text
{'title': 'Trainers', 'price': 89.99}
{'title': 'Barefoot Trainers', 'price': 130.0}
{'title': 'Cap', 'price': 15.0}
{'title': 'Beanie', 'price': 9.0}
```
Without the call to `select()`, we'd get all fields in each product object.
```python
# ...
for product in query("$..products.*", data).values():
print(product)
```
```text
{'title': 'Trainers', 'description': 'Fashionable trainers.', 'price': 89.99}
{'title': 'Barefoot Trainers', 'description': 'Running trainers.', 'price': 130.0, 'social': {'likes': 12, 'shares': 7}}
{'title': 'Cap', 'description': 'Baseball cap', 'price': 15.0}
{'title': 'Beanie', 'description': 'Winter running hat.', 'price': 9.0}
```
We can select nested values too, and arguments to `select()` can be pre-compiled paths.
```python
import jsonpath
# ...
projection = (jsonpath.compile("title"), jsonpath.compile("social.shares"))
for product in jsonpath.query("$..products.*", data).select(*projection):
print(product)
```
```text
{'title': 'Trainers'}
{'title': 'Barefoot Trainers', 'social': {'shares': 7}}
{'title': 'Cap'}
{'title': 'Beanie'}
```
And flatten the selection into a sequence of values.
```python
from jsonpath import Projection
# ...
for product in query("$..products.*", data).select(
"title", "social.shares", projection=Projection.FLAT
):
print(product)
```
```text
['Trainers']
['Barefoot Trainers', 7]
['Cap']
['Beanie']
```
Or project the selection from the JSON value root.
```python
# ..
for product in query("$..products[?@.social]", data).select(
"title",
"social.shares",
projection=Projection.ROOT,
):
print(product)
```
```text
{'categories': [{'products': [{'title': 'Barefoot Trainers', 'social': {'shares': 7}}]}]}
```
|