|
1 |
| -# Filterparams # |
| 1 | +# Python Filterparams # |
| 2 | + |
| 3 | +Filterparams is a library for parsing URL paramters for filter |
| 4 | +purposes in a backend. It provides a syntax to map SQL-like |
| 5 | +queries on top of the query parameters and parses it into a |
| 6 | +python object. |
| 7 | + |
| 8 | +This is a helper library for providing filter collection APIs. |
| 9 | +The primary use case for developing the library is to |
| 10 | +use it with a REST-API which uses the [JSONAPI](http://jsonapi.org/) |
| 11 | +standard. Because of this the syntax is completely compatible with |
| 12 | +the standard and encapsulates everything in the `filter` query |
| 13 | +parameter. |
| 14 | + |
| 15 | +## Example ## |
| 16 | + |
| 17 | +Given the URL (non URL escaped for better readability): |
| 18 | +``` |
| 19 | +/users?filter[param][name][like][no_default_name]=doe&filter[param][first_name]=doe%&filter[binding]=(!no_brand_name&first_name)&filter[order]=name&filter[order]=desc(first_name) |
| 20 | +``` |
| 21 | + |
| 22 | +It can be parsed by the given function: |
| 23 | + |
| 24 | +```python |
| 25 | +from filterparams import build_parser |
| 26 | +valid_filters = ['eq', 'like'] |
| 27 | +default_filter = 'eq' |
| 28 | + |
| 29 | +parser = build_parser( |
| 30 | + valid_filters=valid_filters, |
| 31 | + default_filter=default_filter, |
| 32 | +) |
| 33 | + |
| 34 | +query = parser( |
| 35 | + |
| 36 | +) |
| 37 | +``` |
| 38 | + |
| 39 | +Would parse the data. You can access the parsed filters through |
| 40 | +`.param_order` and the orders through `.orders`. The param order |
| 41 | +in this specific case would be resolved to: |
| 42 | + |
| 43 | +```python |
| 44 | +And( |
| 45 | + left=Parameter( |
| 46 | + name='name', |
| 47 | + alias='no_default_name', |
| 48 | + filter='like', |
| 49 | + value='doe%', |
| 50 | + ), |
| 51 | + right=Parameter( |
| 52 | + name='first_name', |
| 53 | + alias='first_name', |
| 54 | + filter='eq', |
| 55 | + value='doe', |
| 56 | + ) |
| 57 | +) |
| 58 | +``` |
| 59 | + |
| 60 | +The orders would be: |
| 61 | + |
| 62 | +```python |
| 63 | +[Order(name='name', direction='asc'), |
| 64 | + Order(name='first_name', direction='desc')] |
| 65 | +``` |
| 66 | + |
| 67 | +## Syntax ## |
| 68 | + |
| 69 | +All arguments must be prefixed by "filter". It is possible to |
| 70 | +query for specific data with filters, apply orders to the result |
| 71 | +and to combine filters through AND, NOT and OR bindings. |
| 72 | + |
| 73 | +The syntax builds under the filter parameter a virtual object. |
| 74 | +The keys of the object are simulated through specifying `[{key}]` |
| 75 | +in the passed query parameter. Thus `filter[param]` would point |
| 76 | +to the param key in the filter object. |
| 77 | + |
| 78 | +### Filter specification ### |
| 79 | + |
| 80 | +The solution supports to query data through the `param` subkey. |
| 81 | + |
| 82 | +``` |
| 83 | +filter[param][{parameter_name}][{operation}][{alias}] = {to_query_value} |
| 84 | +``` |
| 85 | + |
| 86 | +The `operation` and `alias` parameters may be omitted. If no |
| 87 | +`alias` is provided the given parameter name is used for it. |
| 88 | +If no `operation` is given, the default one is used (in the |
| 89 | +example this would be equal). |
| 90 | + |
| 91 | +Example: |
| 92 | +``` |
| 93 | +filter[param][phone_number][like]=001% |
| 94 | +``` |
| 95 | + |
| 96 | +This would add a filter to all phone numbers which start with "001". |
| 97 | + |
| 98 | +### Filter binding ### |
| 99 | + |
| 100 | +Per default all filters are combined through AND clauses. |
| 101 | +You can change that by specifying the `filter[binding]` argument. |
| 102 | + |
| 103 | +This is where the aliases which you can define come into place. |
| 104 | +The binding provides means to combine filters with AND and OR. |
| 105 | +Also you are able to negate filters here. |
| 106 | + |
| 107 | +The filters are addressed by their alias or name, if no alias is |
| 108 | +provided. |
| 109 | + |
| 110 | +If you have a filter `search_for_name`, `search_for_phone_number` |
| 111 | +and `search_for_account_number` defined you can say |
| 112 | +`search_for_name OR NOT search_for_number AND search_for_account_number` |
| 113 | +by specifying the following filter: |
| 114 | + |
| 115 | +``` |
| 116 | +filter[binding]=search_for_name|(!search_for_phone_number&search_for_account_number) |
| 117 | +``` |
| 118 | + |
| 119 | +Even though the brackets are useless here, you can use them in |
| 120 | +more complex filters. |
| 121 | + |
| 122 | +The following table summarizes the possible configuration options: |
| 123 | +<table> |
| 124 | + <thead> |
| 125 | + <tr> |
| 126 | + <th>Type</th> |
| 127 | + <th>Symbol</th> |
| 128 | + <th>Example</th> |
| 129 | + </tr> |
| 130 | + </thead> |
| 131 | + <tbody> |
| 132 | + <tr> |
| 133 | + <td>AND</td> |
| 134 | + <td>&</td> |
| 135 | + <td>a&b</td> |
| 136 | + </tr> |
| 137 | + <tr> |
| 138 | + <td>OR</td> |
| 139 | + <td>|</td> |
| 140 | + <td>a|b</td> |
| 141 | + </tr> |
| 142 | + <tr> |
| 143 | + <td>NOT</td> |
| 144 | + <td>!</td> |
| 145 | + <td>!a</td> |
| 146 | + </tr> |
| 147 | + <tr> |
| 148 | + <td>Bracket</td> |
| 149 | + <td>()</td> |
| 150 | + <td>(a|b)&c</td> |
| 151 | + </tr> |
| 152 | + </tbody> |
| 153 | +</table> |
| 154 | + |
| 155 | +### Ordering ### |
| 156 | + |
| 157 | +To specify a sort order of the results the `filter[order]` parameter |
| 158 | +may be used. The value can be specified multiple times. To add |
| 159 | +ordering you have to provide the name of the parameter which should |
| 160 | +be ordered, not its alias! |
| 161 | + |
| 162 | +If you want to order by `name`, `first_name` and in reverse order |
| 163 | +`balance` you can do so by specifying the following query url |
| 164 | +parameters: |
| 165 | + |
| 166 | +``` |
| 167 | +filter[order]=name&filter[order]=first_name&filter[order]=desc(balance) |
| 168 | +``` |
| 169 | + |
| 170 | +As you can see the `desc()` definition can be used to indicate |
| 171 | +reverse ordering. |
| 172 | + |
| 173 | +### Filter definition ### |
| 174 | + |
| 175 | +Not every backend does or should support all possible filter |
| 176 | +mechanisms. This is why the filters which should be accepted |
| 177 | +by the backend have to be added before processing the query |
| 178 | +parameters. |
| 179 | + |
| 180 | +You can limit the allowed filters by building a parse through the |
| 181 | +`filterparams.build_parser` function. You can configure the allowed |
| 182 | +filters through the `valid_filters` definition. Additionally you |
| 183 | +have to add the default filter by using the second `default_filter` |
| 184 | +parameter. |
| 185 | + |
| 186 | +```python |
| 187 | +from filterparams import build_parser |
| 188 | + |
| 189 | +valid_filters = ['eq', 'like'] |
| 190 | +default_filter = 'eq' |
| 191 | + |
| 192 | +parser = build_parser( |
| 193 | + valid_filters=valid_filters, |
| 194 | + default_filter=default_filter, |
| 195 | +) |
| 196 | + |
| 197 | +query = parser({}) |
| 198 | +``` |
| 199 | + |
| 200 | +If you don't want any validation you can use the `parse` function. |
| 201 | + |
| 202 | +```python |
| 203 | +from filterparams import parse |
| 204 | + |
| 205 | +query = parse({}) |
| 206 | +``` |
| 207 | + |
| 208 | +## Notes ## |
| 209 | + |
| 210 | +- There do no yet exist any public projects which use this library to provide transparent mapping to an underlying |
| 211 | +backend. I plan long-term to add another library which does use this package and provide a way to map it on sqlalchemy models. |
| 212 | +If you are planning to do this or use it for other data mapping please contact me and I'll add a reference to it in |
| 213 | +the README. |
| 214 | +- The same as mentioned above is valid for client libraries, which generate the filter query structure in any language. |
| 215 | +Again, as soon as the API is stable I'll probably add a JavaScript library. |
| 216 | +- Depending on your backend it might not make sense to support all features (ordering, parameter binding) of the |
| 217 | +language. You might still want to use it to parse your basic parameters though and ignore the rest. |
| 218 | + |
| 219 | +## Used Libraries ## |
| 220 | + |
| 221 | +For evaluating the filter params ordering the [funcparserlib](https://github.com/vlasovskikh/funcparserlib) ([MIT license](https://github.com/vlasovskikh/funcparserlib/blob/master/LICENSE)) |
| 222 | +module is used. Additionally the [Werkzeug](https://github.com/mitsuhiko/werkzeug/blob/master/LICENSE) package is used for supporting dicts with multiple values in the same key. |
2 | 223 |
|
3 | 224 | ## Other Languages ##
|
4 | 225 |
|
|
0 commit comments