Thank you so much for the insight and suggestions!
I think you’re right that in order to accurately describe how a Collection has been filtered, you might need to expose the SQL/SPARQL statement and/or additional processing that’s been done after querying. However, at that point, it would require that the client is able to process such instructions, so it becomes less of a client “hint” and probably too demanding.
Here I think integrating the SHACL W3C standard could provide a solution.
First, a simple example. Below is a “fep:CollectionViewPage” with some basic filtering and sorting applied via “fep:filter” and “fep:sort”. This is a View of Alice’s Outbox. In this case, Alice’s full Outbox contains types of Activities other than “as:Create”, such as “as:Accept” and “as:Delete”.
Filtering is indicated by a SHACL “sh:NodeShape” with a constraint on a top-level property, where the “@type” is “as:Create”.
For sorting, SHACL’s “sh:path” indicates that the property being sorted is “as:published” and “fep:order” indicates the sort direction.
{
"name": "Recent Outbox Posts by Alice",
"type": "CollectionView",
"filter": {
"type": "NodeShape",
"property": [{
"type": "PropertyShape",
"path": "type",
"hasValue": "Create"
}]
},
"sort": {
"type": "SortRule",
"rule": {
"type": "PropertyShape",
"path": "published"
},
"order": "Ascending"
}
}
Below is a more complex example.
This is a CollectionView of Alice’s Inbox which contains only Blog Posts by her Co-workers. (Alice has a custom stream of mutual friends who she has labeled as Co-workers.)
The first filter is the same.
SHACL’s “sh:path” can be an array, so the second filter is on a nested property, in this case “object.type” matching on “as:Article”.
The third filter is a custom SHACL Shape “fep:isInCollection” which gets provided the URL for the Co-workers Collection as an argument. The underlying SHACL mechanics are basically that this is referencing a JavaScript function that is supposed to perform the check, but this does necessarily need to be implemented. For “client hint” purposes, this is just a way to provide arguments associated with predetermined domain-specific terms that are not easily represented using the rest of the SHACL vocabulary.
{
"name": "Blog Posts by Alice's Co-workers",
"type": "CollectionView",
"filter": {
"type": "NodeShape",
"property": [{
"type": "PropertyShape",
"path": "type",
"hasValue": "Create"
}, {
"type": "PropertyShape",
"path": ["object", "type"],
"hasValue": "Article"
}, {
"type": "PropertyShape",
"path": "actor",
"isInCollection": {
"collectionUrl": "https://example.social/@alice/following/coworkers"
}
}]
},
"sort": [{
"type": "SortRule",
"rule": {
"type": "PropertyShape",
"path": "published"
},
"order": "Ascending"
}]
}
Here’s what the “@context” definition might look like for FEP with these additions:
{
"@context": {
"@version": 1.1,
"@vocab": ":_",
"fep": "https://w3id.org/fep#",
"rdf": "http://www.w3.org/1999/02/22-rdf-syntax-ns#",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"sh": "http://www.w3.org/ns/shacl#",
"as": "https://www.w3.org/ns/activitystreams#"
},
"@graph": [
{
"@id": "fep:CollectionView",
"@type": "rdfs:Class",
"rdfs:subClassOf": "as:OrderedCollection"
},
{
"@id": "fep:CollectionViewPage",
"@type": "rdfs:Class",
"rdfs:subClassOf": "as:OrderedCollectionPage"
},
{
"@id": "fep:filter",
"@type": "rdf:Property",
"rdfs:domain": "fep:CollectionViewPage",
"rdfs:range": "sh:NodeShape"
},
{
"@id": "fep:sort",
"@type": "rdf:Property",
"rdfs:domain": "fep:CollectionViewPage",
"rdfs:range": "fep:SortRule"
},
{
"@id": "fep:SortRule",
"@type": "rdfs:Class"
},
{
"@id": "fep:rule",
"@type": "rdf:Property",
"rdfs:domain": "fep:SortRule",
"rdfs:range": "sh:PropertyShape"
},
{
"@id": "fep:order",
"@type": "rdf:Property",
"rdfs:domain": "fep:SortRule",
"rdfs:range": "fep:SortOrder"
},
{
"@id": "fep:SortOrder",
"@type": "rdfs:Class"
},
{
"@id": "fep:Ascending",
"@type": "fep:SortOrder"
},
{
"@id": "fep:Descending",
"@type": "fep:SortOrder"
}
]
}
Please let me know what you think of this idea.