Filtering
This page collects design proposals for ISSUE-45. Proposals for the collection design in general can be found on the Collection Design page.
Open Questions
To summarize, the things that still need to be discussed/decided are:
- do we need to make the filter function explicit or can we safely default to AND
- should we focus on describing Filters or FilteredViews (or similar)
- if it's the former, can a IriTemplate be a Filter itself at the same time
- if it's the latter, how do we connect the filter, the FilteredView and the IriTemplate
- assuming that the same property needs to be used multiple times in a IriTemplate, how we model that
- assuming we need "transformations" or also just simply the equivalent of the SELECT part of a SQL/SPARQL query how do we possibly model that
- how can we enable filteing by subject (@id)?
Proposed Designs
Implictly imply AND filter
{ "@id": "/collection", "filter": { "@type": "IriTemplate", "template": "/collection{?first,last}", "mapping": [ { "variable": "first", "property": "schema:givenName" }, { "variable": "last", "property": "schema:familyName" } ] } }
Pros:
- Simple and straightforward
Cons:
- Not very powerful and difficult to add more sophisticated filters later on as it would likely break clients
Explicitly describe the filter
{ "@id": "/collection", "filter": { "@type": "IriTemplate", "filterSpecification": { "@type": "AndFilter", "input": [ { "variable": "first" }, { "variable": "last" } ] }, "template": "/collection{?first,last}", "mapping": { "variable": "first", "property": "schema:givenName" }, "mapping": { "variable": "last", "property": "schema:familyName" } } }
This would allow to nest filters to describe more complicated expressions (the filter would be name OR (first AND last)):
{ "@id": "/collection", "filter": { "@type": "IriTemplate", "filterSpecification": { "@type": "OrFilter", "input": [ { "variable": "name" }, { "@type": "AndFilter", "input": [ { "variable": "first" }, { "variable": "last" } ] } ] }, "template": "/collection{?name,first,last}", "mapping": [ { "variable": "name", "property": "schema:name" }, { "variable": "first", "property": "schema:givenName" }, { "variable": "last", "property": "schema:familyName" } ] } }
Pros:
- Rather simple to understand
- Very powerful
Cons:
- Puts a big burden on the client. If a client wants to filter a collection in a specific way, it would need to be able to check whether his query can be accepted directly by the server or whether it needs to rewrite the query into simpler subqueries or generalize the query and complement it with some additional client side querying.
Describe supported logical operators per parameter
A simpler alternative could look somewhat like this but is not as expressive as it doesn't allow grouping (the typical order of precedence is NOT, AND, OR) and is less intuitive:
{ "@id": "/collection", "filter": { "@type": [ "IriTemplate", "Filter" ], "variableRepresentation": "FilterRepresentation", "template": "/collection{?name,first,last}", "mapping": [ { "variable": "name", "property": "schema:name", "allowedOperators": [ "NOT", "OR" ] }, { "variable": "first", "property": "schema:givenName", "allowedOperators": [ "NOT" ] }, { "variable": "last", "property": "schema:familyName" } ] } }
If no operator is given, it would default to AND. This would allow to the following expressions (I hope I didn't forget any):
name AND first AND last NOT name AND first AND last NOT name OR first AND last name OR first AND last
name AND NOT first AND last NOT name AND NOT first AND last NOT name OR NOT first AND last name OR NOT first AND last
Pros:
- Easier for a client to check if a query is supported by the server or not
Cons:
- Difficult to understand
- Some not so obvious limitations in the query expressivity
Using of hydra:view instead of hydra:filter
Instead of using hydra:filter to point to the IRI template, the more general hydra:view could be reused. This is orthogonal to the description of the filter itself.
{ "@id": /collection", "@type": "Collection", "view": { "@type": "ViewTemplate", "template": "/collection{?n}", "mapping": { "variable": "n" "property": "schema:name" "filter": true } } }
The result of a GET /collection?n=Markus would then look as follows:
{ "@id": /collection", "@type": "Collection", "totalItems": 2, "member": [ { "@id": "/markus", "name": "Markus" } ], "view": { "@id": /collection?n=Markus", "@type": "PartialCollectionView" } }
Pros:
- More generalized and better composable (whether that's true, is being debated on the mailing list)
- Follows the pagination design
Cons:
- There are no derived collections that are directly addressable (that could be probably be changed easily though)