stopopol
stopopol

Reputation: 506

SPARQL - recursive query to get a term and all of its multi-level children

I wrote a query for this thesaurus: http://vocabs.ceh.ac.uk/evn/tbl/envthes.evn#http%3A%2F%2Fvocabs.lter-europe.net%2FEnvThes%2F10000

The Sparql endpoint is here: http://vocabs.ceh.ac.uk/evn/tbl/swp?_viewClass=endpoint:HomePage Just select "urn:x-evn-pub:envthes" as the default graph.

It returns every term that is sorted under the term "measure" (http://vocabs.lter-europe.net/EnvThes/10004). It works as desired, but the problem is that it neither elegant nor easy to write like that.

Therefore I am looking for a better way to write the following query:

select distinct ?concept (str(?prefLab) as ?label) (str(?altlab) as ?code)
(str(?p) as ?parent) (str(?pl) as ?parlab) ("EnvThes" as ?source)

WHERE {
 ?concept <http://www.w3.org/2004/02/skos/core#broader> ?level2.
 ?concept <http://www.w3.org/2004/02/skos/core#prefLabel> ?prefLab.
 OPTIONAL {?concept <http://www.w3.org/2004/02/skos/core#altLabel> ?altlab.
  FILTER (lang(?altlab)='en').
 }.

 OPTIONAL {?concept <http://www.w3.org/2004/02/skos/core#broader> ?p.
 ?p <http://www.w3.org/2004/02/skos/core#prefLabel> ?pl}
 OPTIONAL
 {?level2 <http://www.w3.org/2004/02/skos/core#broader> ?level3.
 OPTIONAL
 {?level3 <http://www.w3.org/2004/02/skos/core#broader> ?level4.
 OPTIONAL
 {?level4 <http://www.w3.org/2004/02/skos/core#broader> ?level5.
 OPTIONAL
 {?level5 <http://www.w3.org/2004/02/skos/core#broader> ?level6.
 OPTIONAL
 {?level6 <http://www.w3.org/2004/02/skos/core#broader> ?level7.
 OPTIONAL
 {?level7 <http://www.w3.org/2004/02/skos/core#broader> ?level8.
 OPTIONAL
 {?level8 <http://www.w3.org/2004/02/skos/core#broader> ?level9.
 OPTIONAL
 {?level9 <http://www.w3.org/2004/02/skos/core#broader> ?level10.
 }.}.}.}.}.}.}.}.

 FILTER(
  ?level10 = <http://vocabs.lter-europe.net/EnvThes/10004> || 
  ?level9 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level8 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level7 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level6 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level5 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level4 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level3 = <http://vocabs.lter-europe.net/EnvThes/10004> ||
  ?level2 = <http://vocabs.lter-europe.net/EnvThes/10004>).
 FILTER(lang(?prefLab) = 'en'). 
}

Is there any way to make this recursive? I'm still very new to Sparql and having a hard time writing queries that actually work in the first place.

Thanks.

EDIT: Wow. Thanks for the super helpful answer and comment. I was already able to rewrite the query. For the sake of completeness I'll put it here, but the credit belongs to Joshua Taylor.

Shortened query:

prefix skos: <http://www.w3.org/2004/02/skos/core#>
prefix envthes: <http://vocabs.lter-europe.net/EnvThes/>
select * {
  values ?category { envthes:10004 }
  ?concept skos:broader* ?category .
  ?concept skos:prefLabel ?prefLab .

  filter langMatches(lang(?prefLab), 'en')
  optional {
    ?concept skos:altLabel ?altlab
    filter langMatches(lang(?altLabel), 'en')
  }.
  OPTIONAL {?concept skos:altLabel ?altlab.
  FILTER (lang(?altlab)='en').}.
  OPTIONAL {?concept skos:broader ?parent.
    ?parent skos:prefLabel ?parLab .
  }.
}

Upvotes: 0

Views: 1078

Answers (1)

Joshua Taylor
Joshua Taylor

Reputation: 85863

This isn't exactly the same as your query, and I may have reversed the direction of the link between the concept and the category (I never remember the exact semantics of skos:broader, and which way it's supposed to go). The main changes here, though, are to use a prefix to make the query more readable, and to use a property path (skos:broader*) to link ?category and ?concept. I also used a values block to bind ?concept to the particular fixed value that you mentioned.

prefix skos: <http://www.w3.org/2004/02/skos/core#>

select * {
  #-- specify the value for ?category (you can just
  #-- use this inline, too, but defining it with
  #-- values makes it easier to add others later, and
  #-- can make the query easier to read)
  values ?category { <http://vocabs.lter-europe.net/EnvThes/10004> }

  #-- require that ?concept is related to ?category
  #-- by a chain skos:broader properties of length
  #-- zero or more.  (Zero means that ?concept can be
  #-- ?category.  Use skos:broader+ to require a path
  #-- of length one or more.)
  ?concept skos:broader* ?category .

  #-- get an preferred label in English (required)
  ?concept skos:prefLabel ?prefLab .
  filter langMatches(lang(?prefLab), 'en')

  #-- get an alternative label in English (optional)
  optional {
    ?concept <http://www.w3.org/2004/02/skos/core#altLabel> ?altlab
    filter langMatches(lang(?altLabel), 'en')
  }
}

Upvotes: 3

Related Questions