Enhanced filtering abilities in the API
Introduce new operators and let user combine filters.
This commit is contained in:
parent
3f86fc21af
commit
0f03700f71
@ -8,3 +8,70 @@ API token (see the doc about deployment for more infos).
|
|||||||
A helper script is available under `scripts/api_doc.py` to export a
|
A helper script is available under `scripts/api_doc.py` to export a
|
||||||
documentation of the available API endpoints and usage in the current version
|
documentation of the available API endpoints and usage in the current version
|
||||||
of the code.
|
of the code.
|
||||||
|
|
||||||
|
|
||||||
|
### Pagination
|
||||||
|
|
||||||
|
All the API endpoints support pagination. By default, no pagination is done.
|
||||||
|
You can force pagination specifying a `page[size]` GET parameter to specify a
|
||||||
|
number of items per page and a `page[number]` GET parameter to specify the
|
||||||
|
page to return. Pages are numbered starting from zero.
|
||||||
|
|
||||||
|
For instance,
|
||||||
|
|
||||||
|
```
|
||||||
|
> GET /api/v1/reports?page[size]=10&page[number]=0
|
||||||
|
```
|
||||||
|
|
||||||
|
will return the ten first reports of the first page, which are the ten first
|
||||||
|
reports from the database.
|
||||||
|
|
||||||
|
|
||||||
|
### Filtering
|
||||||
|
|
||||||
|
#### Basic filtering
|
||||||
|
|
||||||
|
Filtering is possible in the API through the use of a `filter` GET parameter.
|
||||||
|
|
||||||
|
You can filter on a given field value using the parameter value
|
||||||
|
`filter[FIELD]=VALUE`. For instance,
|
||||||
|
|
||||||
|
```
|
||||||
|
> GET /api/v1/reports?filter[id]=1
|
||||||
|
```
|
||||||
|
|
||||||
|
will return the reports with `id` 1.
|
||||||
|
|
||||||
|
#### Combining filters
|
||||||
|
|
||||||
|
All provided filters must be filled for an item to be returned. That is, if
|
||||||
|
you want to return all the reports of type `interrupt` with no `upvotes`, you
|
||||||
|
can use
|
||||||
|
|
||||||
|
```
|
||||||
|
> GET /api/v1/reports?filter[type]=interrupt&filter[upvotes]=0
|
||||||
|
```
|
||||||
|
|
||||||
|
If you want to make an `OR` condition, satisfying either one of the filters,
|
||||||
|
you should make two different API calls.
|
||||||
|
|
||||||
|
|
||||||
|
#### Commplex filtering
|
||||||
|
|
||||||
|
You can also use more complex elaborations through the syntax
|
||||||
|
`filter[FIELD][OPERATION]=VALUE`. The available operations are `eq` (equal,
|
||||||
|
default operation), `ne` (not equal), `gt` (greater than), `ge` (greater or
|
||||||
|
equal), `lt` (lower than) and `le` (lower or equal). For instance,
|
||||||
|
|
||||||
|
```
|
||||||
|
> GET /api/v1/reports?filter[id][ne]=1
|
||||||
|
```
|
||||||
|
|
||||||
|
will return all the reports except the one with `id` 1.
|
||||||
|
|
||||||
|
With the filters combination capability, you can use this to get all the
|
||||||
|
reports in a geographical bounding box:
|
||||||
|
|
||||||
|
```
|
||||||
|
> GET /api/v1/reports?filter[lat][gt]=LAT_MIN&filter[lat][lt]=LAT_MAX&filter[lng][gt]=LNG_MIN&filter[lng][lt]=LNG_MAX
|
||||||
|
```
|
||||||
|
@ -10,7 +10,7 @@ import re
|
|||||||
|
|
||||||
import bottle
|
import bottle
|
||||||
|
|
||||||
FILTER_RE = re.compile(r"filter\[([A-z0-9_]+)\]")
|
FILTER_RE = re.compile(r"filter\[([A-z0-9_]+?)\](\[([A-z0-9_]+)\])?")
|
||||||
|
|
||||||
|
|
||||||
class DateAwareJSONEncoder(json.JSONEncoder):
|
class DateAwareJSONEncoder(json.JSONEncoder):
|
||||||
@ -73,8 +73,23 @@ def JsonApiParseQuery(query, model, default_sorting=None):
|
|||||||
if not filter_match:
|
if not filter_match:
|
||||||
continue
|
continue
|
||||||
field = getattr(model, filter_match.group(1))
|
field = getattr(model, filter_match.group(1))
|
||||||
value = query[filter_match.group(0)]
|
for value in query.getall(param):
|
||||||
|
# Handle operation
|
||||||
|
operation = filter_match.group(3)
|
||||||
|
if operation == 'eq' or operation is None:
|
||||||
filters.append(field == value)
|
filters.append(field == value)
|
||||||
|
elif operation == 'ne':
|
||||||
|
filters.append(field != value)
|
||||||
|
elif operation == 'gt':
|
||||||
|
filters.append(field > value)
|
||||||
|
elif operation == 'ge':
|
||||||
|
filters.append(field >= value)
|
||||||
|
elif operation == 'lt':
|
||||||
|
filters.append(field < value)
|
||||||
|
elif operation == 'le':
|
||||||
|
filters.append(field <= value)
|
||||||
|
else:
|
||||||
|
raise ValueError("Invalid filtering operator provided.")
|
||||||
|
|
||||||
# Handle pagination according to JSON API spec
|
# Handle pagination according to JSON API spec
|
||||||
page_number, page_size = 0, None
|
page_number, page_size = 0, None
|
||||||
|
Loading…
Reference in New Issue
Block a user