- From: Gregg Kellogg <gregg@greggkellogg.net>
- Date: Thu, 24 Apr 2014 10:57:35 -0700
- To: Thomas Hoppe <thomas.hoppe@n-fuse.de>
- Cc: public-hydra@w3.org
Hi Thomas, On Apr 24, 2014, at 1:14 AM, Thomas Hoppe <thomas.hoppe@n-fuse.de> wrote: > 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. As I mentioned elsewhere, depending on the type of IRI you want to mint, joining a template to the resource base can be a problem. For example, if I have a resource I've retrieved from <http://example/giants> and I want to construct a template "interests{?likeKind}", if I do basic URI joining, I would get <http://example/interests?like=fan> , where I probably want it to be relative to the giants. The problem is easier if all resources are returned with a trailing "/", such as <http://example/giants/>, but this may be too big of an assumption. > - 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). I was using the SPARQL queries to illustrate the kinds of searches we might want to perform, not to bind them that tightly. I suspect this will be out-of-scope at this point, but you could imagine a search with an IRI template such as "interests?group-by=likeKind&count" that might return multiple results with the count of each kind of UserLike. Gregg > 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 17:58:05 UTC