We have four official languages in Switzerland so for many data we have the same label in up to four languages available. On other data there is either no language tag assigned or it is @en.

I would like to filter to the preferred language of the user and if we don't find that, either fall back to English or one with no language assigned. So far I came up with this:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

CONSTRUCT 
{
  ?s rdf:type rdfs:Class ;
  skos:prefLabel ?label .
}
WHERE 
{
  ?s rdf:type rdfs:Class ;
  skos:prefLabel ?label .
  FILTER( lang(?label) = "de" || lang(?label) = "en")
}

This works but on some labels I now get two values back which means I have to include logic in the UI to select the best match for the user. Is there a way to do the SPARQL query in a way that it stops after the first match and returns only this one?

We had an interesting discussion at #swig with several ideas but it looks pretty complex so far.

asked 29 Jan '13, 05:57

ktk's gravatar image

ktk
255128
accept rate: 0%

edited 29 Jan '13, 06:32

Rob%20Vesse's gravatar image

Rob Vesse ♦
13.9k1715


Likely the best thing for you to do is to include a number of optionals and then use the COALESCE function in a BIND statement to pick the first value found e.g.

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

CONSTRUCT 
{
  ?s rdf:type rdfs:Class ;
  skos:prefLabel ?label .
}
WHERE 
{
  ?s rdf:type rdfs:Class .
  OPTIONAL 
  { 
    ?s skos:prefLabel ?userLangLabel .
    FILTER(LANGMATCHES(LANG(?userLangLabel), "de"))
  }
  OPTIONAL
  {
    ?s skos:prefLabel ?defaultLangLabel .
    FILTER(LANGMATCHES(LANG(?defaultLangLabel), "en"))
  }
  BIND(COALESCE(?userLangLabel, ?defaultLangLabel) AS ?label)
}
permanent link

answered 29 Jan '13, 06:36

Rob%20Vesse's gravatar image

Rob Vesse ♦
13.9k1715
accept rate: 29%

Works fine, thanks. It's a bit overhead though, would be a nice extension to SPARQL to make this easier syntax wise.

(29 Jan '13, 08:25) ktk ktk's gravatar image

@ktk What would an extension look like syntactically, if you have an idea for what that would look like I would love to see it.

As the alternative solution I just posted shows there are multiple ways to do this and all are somewhat ugly. Some thinks like this are just better expressed in your code rather than in SPARQL which is primarily a declarative graph pattern matching language.

(29 Jan '13, 09:16) Rob Vesse ♦ Rob%20Vesse's gravatar image

@rob-vesse ah you are right I was on the wrong track, I expected it to stop in the OR when the first one matches but I didn't think that the filter has to be done for each triple. So for the english one there is no german tag and thus it does not stop evaluating. I don't have any idea for syntactic sugar right now.

(29 Jan '13, 11:06) ktk ktk's gravatar image

@ktk You may want to look at @AndyS's answer at http://answers.semanticweb.com/questions/20682/preference-patterns-for-sparql-11 for another alternative approach that is somewhat nicer than this

(31 Jan '13, 08:01) Rob Vesse ♦ Rob%20Vesse's gravatar image

An alternative query which would also answer your question is as follows:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX skos: <http://www.w3.org/2004/02/skos/core#>

CONSTRUCT 
{
  ?s rdf:type rdfs:Class ;
  skos:prefLabel ?label .
}
WHERE 
{
  {
    SELECT ?s (COALESCE(SAMPLE(?userLangLabel), SAMPLE(?defaultLangLabel)) AS ?label)
    WHERE {
      ?s rdf:type rdfs:Class ;
         skos:prefLabel ?userLangLabel ,
                        ?defaultLangLabel .
      FILTER(LANGMATCHES(LANG(?userLangLabel), "de") 
             || LANGMATCHES(LANG(?defaultLangLabel), "en"))
    } GROUP BY ?s
  }
}

This one again uses COALESCE but uses a sub-query in conjunction with GROUP BY to avoid the need for OPTIONAL.
However it is still rather ugly since you need to use SAMPLE as the label variables are not group keys.

permanent link

answered 29 Jan '13, 09:13

Rob%20Vesse's gravatar image

Rob Vesse ♦
13.9k1715
accept rate: 29%

Your answer
toggle preview

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Markdown Basics

  • *italic* or _italic_
  • **bold** or __bold__
  • link:[text](http://url.com/ "title")
  • image?![alt text](/path/img.jpg "title")
  • numbered list: 1. Foo 2. Bar
  • to add a line break simply add two spaces to where you would like the new line to be.
  • basic HTML tags are also supported

Question tags:

×1,299
×23
×9

question asked: 29 Jan '13, 05:57

question was seen: 1,343 times

last updated: 31 Jan '13, 08:01