- From: Thomas Hoppe <thomas.hoppe@n-fuse.de>
- Date: Thu, 24 Apr 2014 10:14:50 +0200
- To: public-hydra@w3.org
Hello Gregg, just my 50 Cent on this: - I don't understand why the {collection} is part of the templated link, I would expect that a client knows this from its current IRI. - Apart from that I would model the likeKind filter as you did. - The SPARQL queries are just the implementation to perform the actual query behind the API (if you have a SPARQL query at hand) but I don't see how they are related to the API. Of course you could introduce an operation "SPARQLQuery" that exepects a somehow JSON-LD serialized sparql query (what I quite like but as we know is not realistic). Greets, Thomas On 04/12/2014 11:49 PM, Gregg Kellogg wrote: > I've been discussing some issues with Markus off-list related to API issues we're working through in defining the YourSports API using Hydra. One of them deals with how to query a collection. > > For an example, I may have a schema:SportsTeam with a collection expressing interests on the team (such as fan, like, dislike, neutral). Some data may look like the following (sorry for the long setup): > > </giants> a :SportsTeam; > :name "SF Giants"; > :interest [ > a :UserLikes; > :linkKind "fan"; > :performer </gregg> > ], [ > a :UserLikes; > :likKind "foe"; > :performer </markus> > ] . > > As expressions of interest on a particular team may be quite large, I want to separate these into a PagedCollection. As an interim solution to avoid breaking a specific "interest" property on SportsTeam, I'm using a separate collection property, and referencing it collection property from the SupportedProperty on interest. A part of the API description looks like the following: > > ysapi: a hydra:ApiDocumentation; > hydra:supportedClass :SportsTeam . > > :SportsTeam a hydra:Class; > hydra:supportedProperty [ > a hydra:SupportedProperty; > hydra:description """ > The interest property does not appear directly in an entity description; interests are asserted in a Collection identified as the value of the interestCollection property. > > Entries are managed through the associated Collection. > """@en; > hydra:property :interest; > hydra:collectionProperty :interestCollection; > hydra:writeOnly true; > hydra:required false > ], [ > a hydra:SupportedProperty; > hydra:description """ > Identifies a Collection containing member references to all UserLikes along with the interest relationships from the Thing to each member of that Collection. > """@en; > hydra:property :interestCollection; > hydra:writeOnly true; > hydra:required false > ] . > > :interestCollection a hydra:Link . > > :InterestCollection a hydra:Class; > hydra:supportedOperation [ > a hydra:CreateResourceOperation; > hydra:description "Indicate if you are a fan, like, neutral, dislike or a foe of something."@en; > hydra:expects :UserLikes; > hydra:method "POST"; > hydra:returns :UserLikes; > hydra:title "Express interest in something"@en > ]; > hydra:supportedProperty [ > a hydra:SupportedProperty; > hydra:multiple false; > hydra:property hydra:member; > hydra:required false > ] . > > As :interest is not defined as a hydra:Link, but :interestCollection is, an API client will know to retrieve the collection value to get an InterestCollection (defined using rangeIncludes). The two resources that might enable this are the following: > > # Giants resource > </giants> a :SportsTeam; > :name "SF Giants"; > :interestCollection </giants/interests> . > > # InterestCollection resource > </giants/interests> a :InterestCollection, hydra:Collection; > hydra:member </giants/interests/gregg>, </giants/interests/markus> . > </giants> :interest </giants/interests/gregg>, </giants/interests/markus> . > > # Interest resources > </giants/interests/gregg> a :UserLikes; :likeKind "fan"; :performer </gregg> . > > </giants/interests/markus> a :UserLikes :likeKind "foe"; :performer </markus> . > > Now, for the question about how to query such a collection. > > So, how might I go about finding just the fans of the Giants? Certainly, one way would be to create a different collection property for each variation of the :likeKind property value, and implement server-side logic to perform an appropriate query. However, given the combinatorics of this, it probably makes sense to defer this logic to the API definition. As I understand it, I could create a SupportedProperty in :InterestCollection which is associated with some property with a a domain of :InterestCollection which would allow me to specify such a query. For example: > > :InterestCollection a hydra:Class; > hydra:supportedProperty [ > a hydra:SupportedProperty; > hydra:multiple false; > hydra:property hydra:member; > hydra:required false > ], [ > a hydra:SupportedProperty; > hydra:property :searchInterestCollection > ], . > > :searchInterestCollection a hydra:TemplatedLink; > hydra:search [ > a hydra:IriTemplate; > hydra:template "{?collection}?likeKind={?kind}"; > hydra:mapping [ > a hydra:IriTemplateMapping; > hydra:variable "collection"; > hydra:property :interestCollection; > hydra:required true > ], [ > a hydra:IriTemplateMapping; > hydra:variable "kind"; > hydra:property :likeKind > hydra:required true > ] . > > The documentation's a bit scant on TemplatedLinks, but I think this says that :searchInterestCollection is a property of an :InterestCollection. To search the collection, you construct an IRI using the template "{?collection}?likeKind={?kind}". The "collection" variable is bound to the property :interestCollection, although the value of that would be in SportsTeam, not the InterestCollection itself. Perhaps there's a standin for the IRI of the subject. The "kind" variable is bound to the property :likeKind, although that is a property of a :UserLikes (in YS, anyway). Anyway, that property is defined with a range restriction being one of "fan", "foe", etc., so an API client might have some way of knowing what kind of value to place here. > > Another way to look at search is that a Collection represents a Graph composed of the collection resource, and the referenced member resources. Performing a search, could be thought of as defining a SPARQL query against this graph, which returns a CONSTRUCT based on the members of that graph. So, the searchInterestCollection could be thought of as being like the following query: > > CONSTRUCT ?member > FROM </giants/interests> > WHERE { > </giants/interests> hydra:member ?member . > ?member :likeKind ?kind > FILTER ?kind = "fan" > } > > If we think of it this way, then it might be nice to consider doing aggregations as well, for example: > > SELECT ?kind, COUNT(?member) AS ?count > FROM </giants/interests> > WHERE { > </giants/interests> hydra:member ?member . > ?member :likeKind ?kind > } > GROUP BY ?kind > > Gregg Kellogg > gregg@greggkellogg.net > >
Received on Thursday, 24 April 2014 08:15:21 UTC