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
|
||||
documentation of the available API endpoints and usage in the current version
|
||||
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
|
||||
|
||||
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):
|
||||
@ -73,8 +73,23 @@ def JsonApiParseQuery(query, model, default_sorting=None):
|
||||
if not filter_match:
|
||||
continue
|
||||
field = getattr(model, filter_match.group(1))
|
||||
value = query[filter_match.group(0)]
|
||||
filters.append(field == value)
|
||||
for value in query.getall(param):
|
||||
# Handle operation
|
||||
operation = filter_match.group(3)
|
||||
if operation == 'eq' or operation is None:
|
||||
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
|
||||
page_number, page_size = 0, None
|
||||
|
Loading…
Reference in New Issue
Block a user