SPARQL

Introduction

Objectifs

  • Vous donner des bases pour écrire des requêtes SPARQL.

  • Bonus: lire/écrire du Turtle (très proche de SPARQL).

  • Ce n’est qu’une introduction ; pour en savoir plus :

    http://www.w3.org/TR/sparql11-overview/

Requête simple

Considérons les données suivantes:

digraph simpleDataset {
graph [ rankdir="LR" margin=0]

Person [ label="foaf:Person" ]
p1 [ label="a" ]
p2 [ label="b" ]
p3 [ label="c" ]
p4 [ label="d" ]
p5 [ label="e" ]

n1 [ label="Alice" shape=box ]
n2 [ label="Bob" shape=box ]
n3 [ label="Carol" shape=box ]
n4 [ label="Dan" shape=box ]
n5 [ label="Dr Jekyll" shape=box ]
n5b [ label="Mr Hyde" shape=box ]

Person -> p1 [ label="rdf:type" dir=back ]
Person -> p2 [ label="rdf:type" dir=back ]
Person -> p3 [ label="rdf:type" dir=back ]
Person -> p4 [ label="rdf:type" dir=back ]
Person -> p5 [ label="rdf:type" dir=back ]
p1 -> n1 [ label="foaf:name" ]
p2 -> n2 [ label="foaf:name" ]
p3 -> n3 [ label="foaf:name" ]
p4 -> n4 [ label="foaf:name" ]
p5 -> n5 [ label="foaf:name" ]
p5 -> n5b [ label="foaf:name" ]

# there seem to be a bug with contraint=false,
# it reverses the direction of the arc in some cases,
# hence the "dir=back" below
p1 -> p2 [ label="foaf:knows" constraint=false ]
p3 -> p4 [ label="foaf:knows" constraint=false ]
p5 -> p4 [ label="foaf:knows" constraint=false ]
}
PREFIX foaf: <http://xmlns.com/foaf/0.1/>

SELECT ?n1 ?n2
WHERE {
    ?p1 a foaf:Person ;
        foaf:name ?n1 ;
        foaf:knows ?p2 .
    ?p2 a foaf:Person ;
        foaf:name ?n2 .
}
digraph simpleSelectQuery {
graph [ rankdir="LR" margin=0 ]

Person [ label="foaf:Person" ]
p1 [ label="?p1" ]
n1 [ label="?n1" style=filled, fillcolor=gray ]
p2 [ label="?p2" ]
n2 [ label="?n2" style=filled, fillcolor=gray ]

Person -> p1 [ label="rdf:type" dir=back]
p1 -> n1 [ label="foaf:name" ]
p1 -> p2 [ label="foaf:knows" constraint=false ]
Person -> p2 [ label="rdf:type" dir=back]
p2 -> n2 [ label="foaf:name" ]
}

Résultats

n1

n2

Alice

Bob

Carol

Dan

Dr Jekyll

Dan

Mr Hide

Dan

Description du graphe requête

Préfixes

Rappel : les préfixes servent à abréger les IRIs.

PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX : <http://example.com/>

Termes

IRI en extension (relatif ou absolu) :

<http://xmlns.org/foaf/0.1/Person>
<../other-file.rdf>
<#something>
<>

IRI abrégé :

foaf:Person
:something

Termes (suite)

Litéral :

"Hello"@en          # avec tag de langue
"123"^^xsd:integer  # typé

"Bonjour"           # equiv. "Bonjour"^^xsd:string
42                  # equiv. "42"^^xsd:integer
1.5                 # equiv. "1.5"^^xsd:decimal
314e-2              # equiv. "314e-2"^^xsd:double
true                # equiv. "true"^^xsd:boolean

Nœud muet :

_:toto
[]       # voir ci-après

Termes (suite)

Variable (SPARQL seulement) :

?x
$y

NB: pas de distinction entre ? et $, donc ?x et $x identifient la même variable.

Triplets

  • 3 termes (sujet, prédicat, objet) séparés par des espaces et suivis d’un point "." :

?p1 foaf:name "Pierre-Antoine Champin" .
  • cas particulier : le mot clé "a" en position de prédicat est un raccourci pour <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> :

?p1 a foaf:Person .
  • le retour à la ligne vaut pour une espace ; la structure est donnée par la ponctuation uniquement.

Factorisation

  • On peut « factoriser » plusieurs triplets ayant le même sujet en séparant les couples <prédicat, objet> par un point-virgule ";" :

    ?p a               foaf:Person ;
       foaf:givenName  "Pierre-Anntoine" ;
       foaf:familyName "Champin" .
    
  • On peut « factoriser » plusieurs triplets ayant le même sujet et le même prédicat en séparant les objets par une virgule "," :

    ?p foaf:phone <tel:+33-472-44-82-40>, <tel:+33-472-69-21-73>.
    
  • On peut bien sûr combiner les deux types de factorisation.

  • On n’est jamais obligé de factoriser, on peut aussi répéter les termes.

Nœud muet

Lorsqu’un nœud muet n’a qu’un seul arc entrant, au lieu de lui inventer un identifiant local :

<#pa> foaf:know _:quelqun .
_:quelqun a foaf:Person ; foaf:name ?n .

on peut utiliser la notation [] :

<#pa> foaf:knows [
    a foaf:Person ;
    foaf:name ?n
] .
digraph bracketBlankNode {
graph [ rankdir=LR ]

pa [ label="<#pa>" ]
bn [ label="" ]
Person [ label="foaf:Person" ]
n [ label="?n" ]
pa -> bn [ label="foaf:knows" ]
bn -> Person [ label="rdf:type" ]
bn -> n [ label="foaf:name"]
}

Union

Pour exprimer un « ou » logique entre plusieurs contraintes, on place chaque alternative entre accolades, séparées par le mot-clé UNION :

<#pa> foaf:knows ?p1 .
{ ?p1 a foaf:Person ; foaf:name ?n }
UNION
{ ?p1 a schema:Person; schema:name ?n }

Sous-graphe optionel

On peut accepter qu’une partie du graphe ne soit pas satisfaite :

?p1 a foaf:Person ; foaf:name ?n .
OPTIONAL { ?p1 foaf:img ?img }
OPTIONAL { ?p1 foaf:phone ?tel }

ou

?p1 a foaf:Person ; foaf:name ?n1 .
OPTIONAL { ?p1 foaf:knows ?p2. ?p2 foaf:name ?n2 }

Dans le résultat, les variables des clauses optionnelles peuvent donc ne recevoir aucune valeur (null).

Note

Dans le deuxième exemple (et contrairement au premier), les deux triplets optionnels sont « solidaires », si l’un d’eux est manquant, c’est toute la clause optionnelle qui est considérée comme non satisfaite.

Ainsi, dans le graphe suivant :

digraph optionalExample {
graph [ rankdir="LR" margin=0]

Person [ label="foaf:Person" ]
Person -> a [ label="rdf:type", dir=back ]
a -> "\"Alice\"" [ label="foaf:name" ]
a -> b [ label="foaf:knows" ]
}

le résultat sera : p1=a, n1=“Alice », p2=null, n2=null .

Filtres

On peut ajouter des contraintes sur les valeurs des résultats, avec la clause FILTER.

?p foaf:age ?a .
FILTER (?a >= 18)

On peut combiner des conditions avec les opérateurs logiques « et » (&&), « ou » (||) et « non » (!).

FILTER ( 20 <= ?a  &&  ?a < 30 )

Opérateurs et fonctions pour les filtres

  • comparaisons : =, !=, <, >, <=, >=

  • opérateurs arithmétiques : +, -, *, /

  • nature d’un nœud : isIRI, isBLANK, isLITERAL, isNUMERIC

  • vérifier qu’une variable (utilisée avec OPTIONAL) a bien une valeur : Bound

  • recherche de texte : REGEX(<variable>, <texte>)

Pour plus d’information, consultez la documentation de SPARQL.

Requête fédérée

Avec la clause SERVICE, il est possible de transmettre une partie de la requête à un autre point d’accès SPARQL.

?p a foaf:Person ; owl:sameAs ?db .
FILTER (regex(str(?db), "dbpedia.org"))

SERVICE <http://dbpedia.org/sparql> {
    ?db dbo:birthDate ?birth
}

Note

Cette fonctionnalité est souvent désactivé dans les points d’accès SPARQL public, pour des raisons de sécurité.

Requête SELECT

Présentation

  • Similaire au SELECT de SQL :

    projection sur un sous-ensemble des variables du graphe

  • Résultat : tableau

    • une colonne par variable sélectionnée

    • une ligne par résultat

  • Structure :

    SELECT <variables/expression> WHERE { <graphe> }

Expression

Les résultats du SELECT peuvent être des expressions complexes, calculées à partir des variables de la clause WHERE.

SELECT ?p (concat(?gn, " ", ?fn) as ?name)
WHERE { ?p foaf:givenName ?gn ; foaf:familyName ?fn . }

DISTINCT

SELECT DISTINCT ?sn
WHERE { <#pa> foaf:knows ?p. ?p foaf:familyName ?sn. }
digraph exempleDistinct {
graph [ rankdir=LR ]

pa [ label="<#pa>" ]
john [ label="" ]
jane [ label="" ]
doe [ shape=rectangle, label="Doe" ]
john_name [ shape=rectangle, label="John" ]
jane_name [ shape=rectangle, label="Jane" ]

pa -> john [ label="foaf:knows" ]
pa -> jane [ label="foaf:knows" ]
john -> john_name [ label="foaf:givenName" ]
john -> doe [ label="foaf:familyName" ]
jane -> doe [ label="foaf:familyName" ]
jane -> jane_name [ label="foaf:givenName" ]
}

Sans le DISTINCT, la requête renverra deux fois le résultat sn="Doe".

LIMIT et OFFSET

Pour obtenir les 10 premiers résultats :

SELECT ?p
WHERE { <#pa> foaf:knows ?p. }
LIMIT 10

Pour obtenir les 5 résultats suivants :

SELECT ?p
WHERE { <#pa> foaf:knows ?p. }
LIMIT 5 OFFSET 10

ORDER BY

SELECT ?p ?n
WHERE { <#pa> foaf:knows [ foaf:givenName ?p ; foaf:familyName ?n ] }
ORDER BY ?n ?p

On peut aussi trier par ordre descendant :

SELECT ?name ?age
WHERE { <#pa> foaf:knows [ foaf:name ?name ; foaf:age ?age ] }
ORDER BY DESC(?age)
LIMIT 1

Note

l’utilisation du tri et de LIMIT 1 permet ici de n’obtenir que la personne la plus vieille que connaît <#pa>.

GROUP BY

Sert à aggréger certaines valeurs avec l’une des fonctions d’aggrégations : Count, Sum, Avg, Min, Max, GroupConcat et Sample.

SELECT ?p1 (count(?p2) as ?cp2)
WHERE { ?p1 foaf:knows ?p2 }
GROUP BY ?p1

On peut combiner GROUP BY avec ORDER BY et LIMIT (attention à l’ordre) :

SELECT ?p1 (count(?p2) as ?cp2)
WHERE { ?p1 foaf:knows ?p2 }
GROUP BY ?p1
ORDER BY DESC(?cp2)
LIMIT 3

Sous-requêtes

Il est possible d’inclure, dans une clause WHERE, une requête SELECT entre accolades.

Par exemple, la requête suivante donne, pour chaque personne, son écart par rapport à l’age moyen.

SELECT ?p ((?age - ?ageMoyen) as ?ecart)
WHERE {
    ?p a foaf:Person ; foaf:age ?age .
    {
      SELECT (avg(?age2) as ?ageMoyen) {
        ?p2 a foaf:Person ; foaf:age ?age2.
      }
    }
}

Quelques requêtes utiles

Ces requêtes permettent de découvrir le vocabulaire utilisé par une source de données.

Exploration des types de ressources

SELECT ?type
WHERE { ?o a ?type }
GROUP BY ?type
ORDER BY DESC(count(?o))
LIMIT 30

Exploration des propriétés liées à un type

SELECT DISTINCT ?prop
WHERE { ?o a <http://example.org/UnType> ; ?prop ?val . }
LIMIT 30

Autres types de requête

ASK

  • Sert à demander si un graphe existe ou non dans la base.

  • Résultat : vrai ou faux

  • Structure :

    ASK { <graphe> }

CONSTRUCT

  • Sert à construire un graphe à partir des résultat d’un autre

  • Résultat : un graphe RDF

  • Structure :

    CONSTRUCT { <graphe> } WHERE { <graphe> }

  • Peut jouer un rôle similaire à XSL-T pour RDF

Exemple

CONSTRUCT {
  ?p1 ex:hasMutualFriendWith ?p3 ;
      foaf:name ?n1 .
  ?p3 foaf:name ?n3 .
} WHERE {
  ?p1 foaf:knows ?p2 .
  ?p3 foaf:knows ?p2 .
  FILTER(?p1 != ?p3)
  OPTIONAL { ?p1 foaf:name ?n1 }
  OPTIONAL { ?p3 foaf:name ?n3 }
}

construit un graphe contenant des arcs ex:hasMutualFriendWith entre les personnes qui partagent au moins un ami, et copie également les arcs foaf:name concernant ces personnes, le cas échéant.

Résultat de l’exemple

En appliquant l’exemple de CONSTRUCT ci-dessus au graphe donné en exemple en début de chapitre, on obtient :

digraph simpleDataset {
graph [ rankdir="LR" margin=0]

p3 [ label="c" ]
p5 [ label="e" ]

n3 [ label="Carol" shape=box ]
n5 [ label="Dr Jekyll" shape=box ]
n5b [ label="Mr Hyde" shape=box ]

p3 -> n3 [ label="foaf:name" ]
p5 -> n5 [ label="foaf:name" ]
p5 -> n5b [ label="foaf:name" ]

p3 -> p5 [ label="ex:hasMutualFriendWith" constraint=False ]
p5 -> p3 [ label=" " constraint=False ]

# the invisible node and arc below
# are here to ensure that the label ex:hasMutualFriendWith is visible
Person [ label="" style=invis ]
Person -> p3 [ style=invis ]
Person -> p5 [ style=invis ]
}

SPARQL Update

Depuis la version 1.1, possibilité de modifier les données.

https://www.w3.org/TR/sparql11-update/