Skip to content

Commit 86ae59d

Browse files
committed
docs: describe the API and add function descriptions in the code
1 parent dab0084 commit 86ae59d

File tree

7 files changed

+257
-10
lines changed

7 files changed

+257
-10
lines changed

README.md

Lines changed: 145 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
![JSON Query Logo](https://jsonquerylang.org/frog-756900-100.png)
44

5-
This is the Python implementation of **JSON Query**, a small, flexible, and expandable JSON query language.
5+
This is a Python implementation of **JSON Query**, a small, flexible, and expandable JSON query language.
66

77
Try it out on the online playground: <https://jsonquerylang.org>
88

@@ -35,6 +35,150 @@ pprint(execute([32, 19, 23])) # [19, 23, 32]
3535
pprint(execute([5, 2, 7, 4])) # [2, 4, 5, 7]
3636
```
3737

38+
## API
39+
40+
### jsonquery
41+
42+
Compile and evaluate a JSON query.
43+
44+
Syntax:
45+
46+
```
47+
jsonquery(data, query [, options])
48+
```
49+
50+
Where:
51+
52+
- `data` is a JSON object or array
53+
- `query` is a JSON query or string containing a text query
54+
- `options` is an optional object which can have the following options:
55+
- `functions` an object with custom functions
56+
57+
Example:
58+
59+
```python
60+
from pprint import pprint
61+
from "jsonquery" import jsonquery
62+
63+
input = [
64+
{"name": "Chris", "age": 23, "scores": [7.2, 5, 8.0]},
65+
{"name": "Joe", "age": 32, "scores": [6.1, 8.1]},
66+
{"name": "Emily", "age": 19},
67+
]
68+
query = ["sort", ["get", "age"], "desc"]
69+
output = jsonquery(query)
70+
pprint(output)
71+
# [{'age': 32, 'name': 'Joe', 'scores': [6.1, 8.1]},
72+
# {'age': 23, 'name': 'Chris', 'scores': [7.2, 5, 8.0]},
73+
# {'age': 19, 'name': 'Emily'}]
74+
```
75+
76+
### compile
77+
78+
Compile a JSON Query. Returns a function which can execute the query.
79+
80+
Syntax:
81+
82+
```
83+
compile(query [, options])
84+
```
85+
86+
Where:
87+
88+
- `query` is a JSON query or string containing a text query
89+
- `options` is an optional object which can have the following options:
90+
- `functions` an object with custom functions
91+
92+
The function returns a lambda function which can be executed by passing JSON data as first argument.
93+
94+
Example:
95+
96+
```python
97+
from pprint import pprint
98+
from "jsonquery" import compile
99+
100+
input = [
101+
{"name": "Chris", "age": 23, "scores": [7.2, 5, 8.0]},
102+
{"name": "Joe", "age": 32, "scores": [6.1, 8.1]},
103+
{"name": "Emily", "age": 19},
104+
]
105+
query = ["sort", ["get", "age"], "desc"]
106+
queryMe = compile(query)
107+
output = queryMe(input)
108+
pprint(output)
109+
# [{'age': 32, 'name': 'Joe', 'scores': [6.1, 8.1]},
110+
# {'age': 23, 'name': 'Chris', 'scores': [7.2, 5, 8.0]},
111+
# {'age': 19, 'name': 'Emily'}]
112+
```
113+
114+
### parse
115+
116+
Parse a string containing a JSON Query into JSON.
117+
118+
Syntax:
119+
120+
```
121+
parse(textQuery, [, options])
122+
```
123+
124+
Where:
125+
126+
- `textQuery`: A query in text format
127+
- `options`: An optional object which can have the following properties:
128+
- `functions` an object with custom functions
129+
- `operators` an object with the names of custom operators both as key and value
130+
131+
Example:
132+
133+
```python
134+
from pprint import pprint
135+
from "jsonquery" import parse
136+
137+
text_query = '.friends | filter(.city == "new York") | sort(.age) | pick(.name, .age)'
138+
json_query = parse(text_query)
139+
pprint(json_query)
140+
# ['pipe',
141+
# ['get', 'friends'],
142+
# ['filter', ['eq', ['get', 'city'], 'New York']],
143+
# ['sort', ['get', 'age']],
144+
# ['pick', ['get', 'name'], ['get', 'age']]]
145+
```
146+
147+
### stringify
148+
149+
Stringify a JSON Query into a readable, human friendly text format.
150+
151+
Syntax:
152+
153+
```
154+
stringify(query [, options])
155+
```
156+
157+
Where:
158+
159+
- `query` is a JSON Query
160+
- `options` is an optional object that can have the following properties:
161+
- `operators` an object with the names of custom operators both as key and value
162+
- `indentation` a string containing the desired indentation, defaults to two spaces: `" "`
163+
- `max_line_length` a number with the maximum line length, used for wrapping contents. Default value: `40`.
164+
165+
Example:
166+
167+
```python
168+
from "jsonquery" import stringify
169+
170+
jsonQuery = [
171+
"pipe",
172+
["get", "friends"],
173+
["filter", ["eq", ["get", "city"], "New York"]],
174+
["sort", ["get", "age"]],
175+
["pick", ["get", "name"], ["get", "age"]],
176+
]
177+
textQuery = stringify(jsonQuery)
178+
print(textQuery)
179+
# '.friends | filter(.city == "new York") | sort(.age) | pick(.name, .age)'
180+
```
181+
38182
## License
39183

40184
Released under the [ISC license](LICENSE.md).

jsonquery/compile.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,25 @@ def compile(
88
query: JsonQueryType, options: Optional[JsonQueryOptions] = None
99
) -> Callable[[JsonType], JsonType]:
1010
"""
11-
Compile a JSON Query
11+
Compile a JSON Query.
12+
13+
Example:
14+
15+
from pprint import pprint
16+
from "jsonquery" import compile
17+
18+
input = [
19+
{"name": "Chris", "age": 23, "scores": [7.2, 5, 8.0]},
20+
{"name": "Joe", "age": 32, "scores": [6.1, 8.1]},
21+
{"name": "Emily", "age": 19},
22+
]
23+
query = ["sort", ["get", "age"], "desc"]
24+
queryMe = compile(query)
25+
output = queryMe(input)
26+
pprint(output)
27+
# [{'age': 32, 'name': 'Joe', 'scores': [6.1, 8.1]},
28+
# {'age': 23, 'name': 'Chris', 'scores': [7.2, 5, 8.0]},
29+
# {'age': 19, 'name': 'Emily'}]
1230
1331
:param query: A JSON Query
1432
:param options: Can an object with custom functions

jsonquery/jsonquery.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,39 @@
1-
from jsonquery.types import JsonType, JsonQueryType, JsonQueryOptions
21
from jsonquery.compile import compile
2+
from jsonquery.parse import parse
3+
from jsonquery.types import JsonType, JsonQueryType, JsonQueryOptions
34

45

56
def jsonquery(
6-
data: JsonType, query: JsonQueryType, options: JsonQueryOptions | None = None
7+
data: JsonType, query: JsonQueryType | str, options: JsonQueryOptions | None = None
78
) -> JsonType:
89
"""
9-
Compile and evaluate a query
10+
Compile and evaluate a JSON query.
11+
12+
Example:
13+
14+
from pprint import pprint
15+
from "jsonquery" import jsonquery
16+
17+
input = [
18+
{"name": "Chris", "age": 23, "scores": [7.2, 5, 8.0]},
19+
{"name": "Joe", "age": 32, "scores": [6.1, 8.1]},
20+
{"name": "Emily", "age": 19},
21+
]
22+
query = ["sort", ["get", "age"], "desc"]
23+
output = jsonquery(query)
24+
pprint(output)
25+
# [{'age': 32, 'name': 'Joe', 'scores': [6.1, 8.1]},
26+
# {'age': 23, 'name': 'Chris', 'scores': [7.2, 5, 8.0]},
27+
# {'age': 19, 'name': 'Emily'}]
1028
1129
:param data: The JSON document to be queried
1230
:param query: A JSON Query
1331
:param options: Can an object with custom functions
1432
:return: Returns the result of the query applied to the data
1533
"""
1634

17-
evaluate = compile(query, options)
35+
_query = parse(query, options) if isinstance(query, str) else query
36+
37+
evaluate = compile(_query, options)
1838

1939
return evaluate(data)

jsonquery/parse.py

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,34 @@
1515

1616

1717
def parse(query: str, options: Optional[JsonQueryParseOptions] = None) -> JsonQueryType:
18-
# FIXME: document the function
19-
18+
"""
19+
Parse a string containing a JSON Query into JSON.
20+
21+
Example:
22+
23+
from pprint import pprint
24+
from "jsonquery" import parse
25+
26+
text_query = '.friends | filter(.city == "new York") | sort(.age) | pick(.name, .age)'
27+
json_query = parse(text_query)
28+
pprint(json_query)
29+
# ['pipe',
30+
# ['get', 'friends'],
31+
# ['filter', ['eq', ['get', 'city'], 'New York']],
32+
# ['sort', ['get', 'age']],
33+
# ['pick', ['get', 'name'], ['get', 'age']]]
34+
35+
:param query: A query in text format
36+
:param options: Can an object with custom operators and functions
37+
:return: Returns the query in JSON format
38+
"""
39+
jsonQuery = [
40+
"pipe",
41+
["get", "friends"],
42+
["filter", ["eq", ["get", "city"], "New York"]],
43+
["sort", ["get", "age"]],
44+
["pick", ["get", "name"], ["get", "age"]],
45+
]
2046
custom_operators: Final = (options.get("operators") if options else None) or {}
2147
custom_functions: Final = (options.get("functions") if options else None) or {}
2248
all_functions: Final = {**functions, **custom_functions}

jsonquery/stringify.py

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,28 @@
1717
def stringify(
1818
query: JsonQueryType, options: Optional[JsonQueryStringifyOptions] = None
1919
) -> str:
20-
# FIXME: document the function
20+
"""
21+
Stringify a JSON Query into a readable, human friendly text syntax.
22+
23+
Example:
24+
25+
from "jsonquery" import stringify
26+
27+
jsonQuery = [
28+
"pipe",
29+
["get", "friends"],
30+
["filter", ["eq", ["get", "city"], "New York"]],
31+
["sort", ["get", "age"]],
32+
["pick", ["get", "name"], ["get", "age"]],
33+
]
34+
textQuery = stringify(jsonQuery)
35+
print(textQuery)
36+
# '.friends | filter(.city == "new York") | sort(.age) | pick(.name, .age)'
37+
38+
:param query: A JSON Query
39+
:param options: A dict with custom operators, max_line_length, and indentation
40+
:return: Returns a human friendly string representation of the query
41+
"""
2142

2243
space: Final = (
2344
options.get("indentation") if options else None

jsonquery/types.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
from typing import TypeAlias, List, Mapping, TypedDict, Callable, NotRequired
22

3-
JsonType: TypeAlias = List["JsonValueType"] | Mapping[str, "JsonValueType"]
3+
JsonType: TypeAlias = (
4+
List["JsonValueType"] | Mapping[str, "JsonValueType"] | "JsonValueType"
5+
)
46
JsonValueType: TypeAlias = str | int | float | None | JsonType
57

68
JsonPath = List[str | int]

tests/test_jsonquery.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,22 @@ def test_jsonquery(self):
2020
],
2121
)
2222

23+
def test_jsonquery_str1(self):
24+
"""Test jsonquery parsing a query text"""
25+
self.assertEqual(
26+
jsonquery(data, "sort(.name)"),
27+
[
28+
{"name": "Chris", "age": 23, "scores": [7.2, 5, 8.0]},
29+
{"name": "Emily", "age": 19},
30+
{"name": "Joe", "age": 32, "scores": [6.1, 8.1]},
31+
],
32+
)
33+
34+
def test_jsonquery_str2(self):
35+
"""Test jsonquery parsing a query text with options"""
36+
options = {"functions": {"times": lambda factor: lambda x: x * factor}}
37+
self.assertEqual(jsonquery(4, "times(3)", options), 12)
38+
2339
def test_options1(self):
2440
"""Test defining a custom function"""
2541

0 commit comments

Comments
 (0)