I wanted to share/propose the filtering strategy I designed when it came time for adding filtering of resource collections. After much debate, the filtering strategy I decided upon was to adopt a “subset” or “best of” from the OData 4.0 query standard for the following reasons:
- Well designed and maintained query options standard, why reinvent the wheel.
- Same reason why I adopted the json-api standard, standards are good!
- Already an adopted industry standard and well documented.
- After much review I found it expressive, URL friendly, and would meet almost 100% of my needs for filtering.
Here is the OData 4.0 documentation for it’s URL conventions: http://docs.oasis-open.org/odata/odata/v4.0/odata-v4.0-part2-url-conventions.html
Again I am not proposing the entire OData query syntax, only the best parts with minor tweaking if necessary. With that said here is what I have currently adopted and have working for filtering of our json:api based resource collections:
neoperator (Not Equals)
gtoperator (Greater Than)
geoperator (Greater Than or Equal)
ltoperator (Less Than)
leoperator (Less Than or Equal)
Grouping with the open
( and close
) parenthesis operators.
containsfunction (filters if a string contains another substring)
startswithfunction (filters if string startswith a substring)
endswithfunction (filters if string endswith a substring)
geo.distancefunction (filters on distance between 2 geographical points (latitude/longitude))
The OData standard does not currently support the
in operator but has an open requirement to add it. It was so fundamental that I added the
in operator based on the open pull request that will be adding it to the OData standard.
The following are examples of filtering based on this OData “subset” and “best-of” that I have currently implemented and have working:
Assume we have a
Movie resource collection at:
/movies?filter=movie-name eq 'Braveheart' /movies?filter=movie-rating eq 'G' or movie-rating eq 'PG-13' /movies?filter=contains(movie-name, 'Star Wars') and price lt 10 /movies?filter=runtime-in-minutes ge 90 and runtime-in-minutes le 120 and rating in ('R', 'PG-13') /movies?filter=schedule-date eq '12/25/2015' and geo.distance(theater-location, POINT(-80.2008 26.3681)) le 10000 /movies?filter=not(startswith(movie-name, 'The') and movie-rating in ('G', 'PG-13')) or schedule-date eq '1/1/2016'
As you can see with just the “subset” and “best-of” from the OData query standard we have a powerful filter framework to filter our resource collections currently. It was an interesting engineering challenge to create the lexical scanner, Pratt parser, and create the EntityFramework WHERE clause from the parsed syntax tree but now that it is working, filtering is easy and actually fun…
Maybe the json:api team should consider something like this as another possible “recommendation” or “proposal” for filtering, or not…