SPARQL Protocol and RDF Query Language/Requêtes de lecture

Début de la boite de navigation du chapitre

L'ambition de SPARQL est d’offrir une interopérabilité, non seulement au niveau des services, comme avec les services Web, mais aussi au niveau des données, structurées ou non, qui sont disponible au travers de l'Internet. Toutes ces données disponibles en ligne via SPARQL sont ce que l’on nomme le Web des données. Ce chapitre va nous permettre d'apprendre à interroger ce Web des données.

Requêtes de lecture
Icône de la faculté
Chapitre no 5
Leçon : SPARQL Protocol and RDF Query Language
Chap. préc. :Le protocole SPARQL
Chap. suiv. :Requêtes d'écriture
fin de la boite de navigation du chapitre
En raison de limitations techniques, la typographie souhaitable du titre, « SPARQL : Requêtes de lecture
SPARQL Protocol and RDF Query Language/Requêtes de lecture
 », n'a pu être restituée correctement ci-dessus.

Introduction

modifier

Commentaires

modifier

On peut écrire des commentaires dans les requêtes SPARQL. Les commentaires commencent par un '#' en début de ligne.


Préfixes

modifier

Dans le chapitre Modèle de données RDF, nous avons vu comment écrire des IRIs relatifs à l'aide de préfixes.

En SPARQL également, une requête peut contenir des IRIs relatifs. Les préfixes doivent être déclarés avant d'écrire une requête.

Exemple : déclaration de préfixes

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ...

Si vous ne connaissez pas un préfixe dans un exemple, vous pouvez utiliser le site http://prefix.cc pour retrouver la définition d'un préfixe.

Abréviations

modifier

rdf:type

modifier

Vous pouvez rencontrer dans les requêtes SPARQL le mot "a", qui est l’abréviation du prédicat <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> (ou rdf:type)

De plus, le sujet des triplets est souvent répété dans une requête pour écrire plusieurs conditions sur ce même sujet. Ainsi, le caractère ";" évite de retaper le sujet des triplets suivants.

Exemple de triplets sans ";"

:book1  dc:title  "SPARQL Tutorial" .
:book1  ns:price  42 .
:book1  foaf:homepage   <http://example.org/book/sparql> .

Les mêmes triplets avec l’utilisation de ";"

:book1  dc:title  "SPARQL Tutorial" ;
        ns:price  42 ;
        foaf:homepage   <http://example.org/book/sparql> .

Prédicat

modifier

Pour les mêmes raisons mais pour éviter d'écrire le même prédicat, vous pouvez utiliser le caractère ",". Par exemple :

:book1  dc:author  "Michel" .
:book1  dc:author  "Paul" .

Est équivalent à

:book1  dc:author  "Michel" , "Paul" .

Les littéraux

modifier

Dans le chapitre Modèle de données RDF, nous avons vu comment écrire un littéral dans le format RDF/Turtle. Dans une requête en SPARQL qui utilise également des triplets, comme dans le format RDF/Turtle, les objets peuvent également être des littéraux, en ayant la même syntaxe que Turtle.

Un littéral dans SPARQL s'écrit comme dans le format Turtle

...
SELECT  ...
WHERE   { 
    ... "2002-10-10T12:00:00Z"^^xsd:date .
        }

Variables

modifier

Une variable dans une requête commence par le caractère "?" et ce caractère ne fait pas partie du nom de la variable. Le nom d'une variable :

  • ne doit pas commencer par un chiffre
  • est sensible à la casse
  • ne doit pas contenir d’espace
  • doit être signifiant, car il sert de nom de colonne dans le résultat SPARQL 1.0


Par exemple, si une variable correspond à l'IRI de l’objet adresse, on peut utiliser la variable "address" qui sera utilisée dans la requête sous la forme "?address".

Requêtes SELECT

modifier

Structure d'une requête

modifier

Une requête SELECT se divise en 5 parties :

  1. Définition des préfixes si vous utilisez des IRIs relatifs
  2. Définition des résultats que vous désirez obtenir en sortie
  3. Définition des jeux de données sur lesquelles porte la requête. Attention : seulement disponible dans SPARQL 1.1
  4. Définition des conditions
  5. Modificateurs de résultats (facultatif)

Structure d'une requête SELECT

# Déclaration des préfixes s’il y a utilisation d'IRI relatifs
PREFIX foo: <...> #Pas d’espace entre Le nom du préfixe et Les deux points
PREFIX ...
...

SELECT ... #Clause SELECT : définition des résultats

# (SPARQL 1.1) Définir le jeu de données (facultatif)
FROM <...> 
FROM NAMED <...> 

#Clause WHERE : conditions qui devront être respectées
WHERE {
  ...
}

#Modificateurs de résultats (facultatif)
GROUP BY ... #SPARQL 1.1
HAVING ... #SPARQL 1.1
ORDER BY ...
LIMIT ...
OFFSET ...
BINDINGS ... #SPARQL 1.1

Définir le tableau de résultats

modifier

Nous avons vu dans le protocole SPARQL que les résultats prennent la forme d'un tableau. C'est dans la clause SELECT que les colonnes sont définies en alignant les variables qui sont présentes dans la clause WHERE. Par exemple "SELECT ?name ?adress" affichera les lignes avec 2 champs : le nom et l'adresse.

Vous pouvez aussi écrire :

  • " SELECT * " : permet d'afficher dans les résultats toutes les variables présentes dans la clause WHERE.
  • "SELECT DISTINCT ?country" : permet de ne pas afficher les lignes qui ont un résultat identique.
  • dans SPARQL 1.1, "SELECT ?country (100 * ?rate AS ?percent)" permettra de faire des calculs et renommer les champs, etc. (comme on peut le faire dans SQL)

Modifier les résultats

modifier

Pour le moment, on expliquera uniquement l’utilisation des mots-clés ORDER BY, LIMIT et OFFSET.

ORDER BY

modifier

ORDER BY permet d'ordonner le résultat de la requête. Il suffit de lui donner en argument la colonne selon laquelle il doit ordonner les résultats.

Début de l'exemple
Fin de l'exemple


Vous constatez qu'en effet, c’est ordonné selon l’ordre des noms, mais vous pouvez également spécifier plusieurs colonnes pour l'ordonnancement du résultat. Par exemple par nom puis par prénom :

Début de l'exemple
Fin de l'exemple


Si vous voulez trier une colonne par ordre inverse, il vous suffit d'ajouter le mot-clef DESC devant le nom de la colonne concernée entre parenthèses. Par exemple la même requête qu'au dessus avec les noms triés par ordre inverse :

Début de l'exemple
Fin de l'exemple


  Non ORDER BY permet de trier selon les colonnes récupérées uniquement. Ainsi, la requête suivante n’est pas conforme, étant donné que la colonne "givenName" ne fait pas partie des colonnes rapatriées (dans la clause SELECT).

Début de l'exemple
Fin de l'exemple


LIMIT permet de limiter le nombre de lignes retournées.

Début de l'exemple
Fin de l'exemple


OFFSET permet de n'afficher le résultat qu’à partir de la ligne indiquée.

Début de l'exemple
Fin de l'exemple


On peut indiquer en même temps une LIMIT et un OFFSET.

Début de l'exemple
Fin de l'exemple

On a maintenant tout ce qu’il nous faut pour naviguer de page en page dans les résultats d'une requête.

Définir les conditions

modifier

La plupart des requêtes SPARQL contiennent un ensemble de triplets appelé masque de graphe.

Ce masque de graphe est composé d'un ensemble de triplets RDF, à ceci près que chaque sujet, prédicat et objet peut être une variable. Chaque triplet devient alors une condition pour notre requête.

Ce masque de graphe est comparé à des données RDF. Il y a alors deux réponses possibles :

  • Ce masque de graphe ne peut recouvrir aucun sous-graphe des données RDF. Il n'y a alors aucun résultat.
  • Ce masque de graphe peut recouvrir un ou plusieurs sous-graphe des données RDF. Le résultat est alors équivalent à tous les sous-graphes ainsi découvert à l'aide de ce masque.

Écrire une requête simple

modifier

L'exemple ci-dessous montre une requête SPARQL pour trouver le titre d'un livre à partir du graphe des données RDF suivant, décrit au format Turtle. La requête se compose de deux parties :

  1. la clause SELECT identifie les variables à faire apparaître dans la réponse de cette requête,
  2. et la clause WHERE fournit le masque du graphe à appliquer sur le graphe des données RDF pour chercher cette réponse.

Le masque du graphe, dans cet exemple, est constitué d'un triplet avec une seule variable "?Titre" à l'endroit du triplet où l’objet est attendu.

Dans le graphe RDF/Turtle :

<http://example.org/book/book1> <http://purl.org/dc/elements/1.1/title> "Tutoriel SPARQL".

On applique la requête SPARQL :

#Ma première requête
SELECT ?titre
WHERE
{
   <http://example.org/book/book1> <http://purl.org/dc/elements/1.1/title> ?titre.
}

Cette requête sur les données ci-dessus a une seule solution.

Résultat de la requête :

titre
"Tutoriel SPARQL"

Obtenir plusieurs réponses

modifier

Le résultat d'une requête est un tableau où chaque ligne représente une solution différente, qui correspond à un sous-graphe trouvé à l'aide du masque de la requête. Il peut y avoir zéro, une ou plusieurs solutions à une requête.

Chaque ligne contient la liste des variables indiquées dans la clause SELECT de la requête. Pour chaque solution, il existe une ligne dans la solution et chaque ligne affichera la valeur qu'a dû prendre chaque variable de la clause SELECT pour trouver cette solution.

Dans le graphe RDF/Turtle :

@prefix foaf:  <http://xmlns.com/foaf/0.1/> .

<http://jlow.me>  foaf:name   "Johnny Lee Outlaw" .
<http://jlow.me>  foaf:mbox   <mailto:jlow@jlow.me> .
<http://peter.me>  foaf:name   "Peter Goodguy" .
<http://peter.me>  foaf:mbox   <mailto:peter@peter.me> .
<http://carol.me>  foaf:mbox   <mailto:carol@carol.me> .

On applique la requête SPARQL :

PREFIX foaf:   <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE
  { ?x foaf:name ?name .
    ?x foaf:mbox ?mbox }

Résultat de la requête:

name mbox
"Johnny Lee Outlaw" <mailto:jlow@jlow.me>
"Peter Goodguy" <mailto:peter@peter.me>

Conditions par la comparaison

modifier

Utilisons le graphe RDF/Turtle suivant :

@prefix dt:   <http://example.org/datatype#> .
@prefix ns:   <http://example.org/ns#> .
@prefix :     <http://example.org/ns#> .
@prefix xsd:  <http://www.w3.org/2001/XMLSchema#> .

:x   ns:p     "cat"@en .
:y   ns:p     "42"^^xsd:integer .
:z   ns:p     "abc"^^dt:specialDatatype .


Littéral avec une balise de langue

modifier

Précédée du symbole @, la balise de langue qui suit un littéral permet de faire cohabiter dans une même base de données plusieurs langues et permet d'obtenir toutes les langues disponibles à travers une simple requête.

Cependant, quand on compare un littéral, il est alors indispensable de ne pas oublier la balise de langue sous peine de n'obtenir aucune solution.

Ainsi, la requête :

SELECT ?v WHERE { ?v ?p "cat" }

ne donne aucune réponse, car elle ne précise pas la balise de la langue contenue dans les données interrogées.

v

mais la requête :

SELECT ?v WHERE { ?v ?p "cat"@en }

donne la solution :

v
<http://example.org/ns#x>

Littéral avec un type numérique

modifier

Les entiers, dans une requête SPARQL, sont implicitement de type xsd:integer.
Exemple: le littéral 42 est équivalent à l'écriture
"42"^^<http://www.w3.org/2001/XMLSchema#integer>.

Ainsi, la requête :

SELECT ?v WHERE { ?v ?p 42 }

donne la solution :

v
<http://example.org/ns#y>

Littéral avec un type arbitraire

modifier

La base de données n'a pas besoin de comprendre ce que représente un type pour l'enregistrer ou pour le retrouver. La base de données va simplement comparer la forme lexicale et la référence IRI du type avec les données qu'elle contient.

Ainsi, la requête :

SELECT ?v WHERE { ?v ?p "abc"^^<http://example.org/datatype#specialDatatype> }

donne la solution :

v
<http://example.org/ns#z>

Contraintes

modifier

Un masque de graphe produit une liste de solutions. On peut, à l'aide du mot clé FILTER, ajouter une contrainte à ce masque. La clause FILTER ajoute une condition qui doit se vérifier pour valider une solution.

Nous allons décrire les principales utilisations de la clause FILTER.

Nous allons utiliser le jeu de données RDF/Turtle suivant :

@prefix dc:   <http://purl.org/dc/elements/1.1/> .
@prefix :     <http://example.org/book/> .
@prefix ns:   <http://example.org/ns#> .


:example  dc:title  "SPARQL Title without tag lang" .
:book1  dc:title  "SPARQL Tutorial"@en .
:book1  ns:price  42 .
:book2  dc:title  "The Semantic Web"@en .
:book2  ns:price  23 .

Littéral texte

modifier

regex() est une fonction qui permet d’utiliser une expression régulière pour vérifier une chaîne de caractères sans balise de langue.

Exemple de requête SPARQL :

PREFIX  dc:  <http://purl.org/dc/elements/1.1/>
SELECT  ?title
WHERE   { ?x dc:title ?title
          FILTER regex(?title, "^SPARQL") 
        }

Résultat de la requête:

title
"SPARQL Title without tag lang"

Pour appliquer cette contrainte à tout littéral sans se soucier de la balise de langue, on utilise la fonction str().

Ainsi, la requête SPARQL devient :

PREFIX  dc:  <http://purl.org/dc/elements/1.1/>
SELECT  ?title
WHERE   { ?x dc:title ?title
          FILTER regex(str(?title), "^SPARQL") 
        }

Et le résultat de la requête fait apparaitre deux résultats:

title
"SPARQL Title without tag lang"
"SPARQL Tutorial"@en

On peut également appliquer l'option "insensible à la casse" à notre expression régulière, avec le paramètre "i".

Par exemple, la requête SPARQL devient :

PREFIX  dc:  <http://purl.org/dc/elements/1.1/>
SELECT  ?title
WHERE   { ?x dc:title ?title
          FILTER regex(str(?title), "web", "i" ) 
        }

Et voici résultat de la requête :

title
"The Semantic Web"@en

Littéral numérique

modifier

La clause FILTER peut vérifier des conditions numériques.

Par exemple la requête SPARQL :

PREFIX  dc:  <http://purl.org/dc/elements/1.1/>
PREFIX  ns:  <http://example.org/ns#>
SELECT  ?title ?price
WHERE   { ?x ns:price ?price .
          FILTER (?price < 30.5)
          ?x dc:title ?title . }

donnera le résultat:

title price
"The Semantic Web"@en 23

Les autres littéraux

modifier

La clause FILTER peut utiliser de nombreuses fonctions et des opérateurs booléens que nous étudierons dans une future leçon. Si vous cherchez une fonction particulière, vous pourrez la trouver, en attendant, dans la documentation de SPARQL ([1]).

Jointures

modifier

Les jointures s'expriment dans une requête SPARQL au sein de la clause WHERE.

Jointure basique

modifier

Les points qui séparent les triplets dans la clause WHERE représentent des jointures basiques.

Jointure à gauche

modifier

Un masque peut contenir des clauses optionnelles qui permettent d'ajouter des solutions, sans exclure les solutions qui existent sans nécessairement respecter ces clauses.

Utilisons le graphe RDF/Turtle suivant :

@prefix foaf:       <http://xmlns.com/foaf/0.1/> .
@prefix rdf:        <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

<http://alice.me>   rdf:type        foaf:Person .
<http://alice.me>   foaf:name       "Alice" .
<http://alice.me>   foaf:mbox       <mailto:alice@example.com> .
<http://alice.me>   foaf:mbox       <mailto:alice@work.example> .

<http://bob.me>   rdf:type        foaf:Person .
<http://bob.me>  foaf:name       "Bob" .

Ainsi la requête :

PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?name ?mbox
WHERE  { ?x foaf:name  ?name .
         OPTIONAL { ?x  foaf:mbox  ?mbox }
       }

donne la solution :

name mbox
"Alice" <mailto:alice@example.com>
"Alice" <mailto:alice@work.example>
"Bob"

Pour "Bob", il n'y a pas de valeur pour la variable mbox, car la clause optionnelle n’est pas vérifiée pour "Bob" .

On peut mettre plusieurs clauses OPTIONAL dans une requête.

Par exemple, avec le graphe RDF/Turtle suivant :

@prefix foaf:   <http://xmlns.com/foaf/0.1/> .
@prefix dc:   <http://purl.org/dc/elements/1.1/> .
@prefix :     <http://example.org/book/> .
@prefix ns:   <http://example.org/ns#> .

:book1  dc:title  "SPARQL Tutorial" .
:book1  ns:price  42 .
:book1  foaf:homepage   <http://example.org/book/sparql> .

:book2  dc:title  "The Semantic Web" .
:book2  ns:price  23 .

:book3  dc:title  "MediaWiki" .
:book3  ns:price  28 .

Ainsi, la requête :

PREFIX  foaf: <http://xmlns.com/foaf/0.1/> 
PREFIX  dc:  <http://purl.org/dc/elements/1.1/>
PREFIX  ns:  <http://example.org/ns#>
SELECT  ?title ?price ?hpage
WHERE   { ?x dc:title ?title .
          OPTIONAL { ?x ns:price ?price . FILTER (?price > 25) }.
          OPTIONAL { ?x foaf:homepage ?hpage }
        }

donne la solution :

title price hpage
"SPARQL Tutorial" 42 <http://example.org/book/sparql>
"The Semantic Web"
"MediaWiki" 28

Addition

modifier

Il arrive que les données que l’on désire atteindre soient enregistrées à des moments différents et donc probablement avec des ontologies différentes, c'est-à-dire dans des graphes de structures différentes.


Le mot clé UNION permet d’utiliser la même variable dans des masques différents sans exclusion mutuelle.

Par exemple, avec le graphe RDF/Turtle suivant :

@prefix :     <http://example.org/book/> .
@prefix dc10:  <http://purl.org/dc/elements/1.0/> .
@prefix dc11:  <http://purl.org/dc/elements/1.1/> .

:a  dc10:title     "SPARQL Query Language Tutorial" .
:a  dc10:creator   "Alice" .

:b  dc11:title     "SPARQL Protocol Tutorial" .
:b  dc11:creator   "Bob" .

:c  dc10:title     "SPARQL" .
:c  dc11:title     "SPARQL (updated)" .

Ainsi la requête :

PREFIX dc10:  <http://purl.org/dc/elements/1.0/>
PREFIX dc11:  <http://purl.org/dc/elements/1.1/>

SELECT ?title
WHERE  { { ?book dc10:title  ?title } UNION { ?book dc11:title  ?title } }

donne la solution :

title
"SPARQL Protocol Tutorial"
"SPARQL"
"SPARQL (updated)"
"SPARQL Query Language Tutorial"

Soustraction

modifier

Seulement disponible pour SPARQL 1.1.


Exercices

modifier

Interroger un point d'accès SPARQL

modifier
  1. Aller sur le site de DBpedia et trouver l’IRI au sein de DBpedia qui correspond le mieux à la page w:Doctor_Who
  2. Dans le formulaire du point d’accès http://dbpedia.org/sparql, écrire une requête pour afficher les prédicats et objets de cet IRI.
  3. Écrire une requête pour afficher les sujets et prédicats de cet IRI.

Faire une requête complexe

modifier
  1. Avec DBpedia, faire une requête pour trouver les dix premiers films français.
  2. Vous afficherez en résultat pour chaque film : le lien vers la page Wikipédia du film, la date, le nom et la description.

Requêtes ASK

modifier

Résultat

modifier

Une requête ASK est très proche d'une requête SELECT sauf qu’il n'y a que deux résultats possibles : true (vrai) ou false (faux).

Structure d'une requête

modifier

Elle se divise en 3 parties :

  1. Définition des préfixes si vous utilisez des IRIs relatifs
  2. Définition des jeux de données sur lesquels porte la requête. Attention : seulement disponible dans SPARQL 1.1.
  3. Définition des conditions.
# Déclaration des préfixes s’il y a utilisation d'IRI relatifs
PREFIX foo: <...> #Pas d’espace entre Le nom du préfixe et Les deux points
PREFIX ...
...

ASK

# (SPARQL 1.1) Définir le jeu de données (facultatif)
FROM <...> 
FROM NAMED <...> 

#Clause WHERE : conditions qui devront être respectées
WHERE {
  ...
}

Une requête renvoie vrai si les conditions de la requête trouvent au moins une réponse dans les données. S'il n'y a aucune réponse possible, la requête renvoie faux.

Prenons les données Dans le graphe RDF/Turtle :

@prefix foaf:       <http://xmlns.com/foaf/0.1/> .

<http://Alice.me>  foaf:name       "Alice" .
<http://Alice.me>  foaf:homepage   <http://work.example.org/alice/> .

<http://Bob.me>  foaf:name       "Bob" .
<http://Bob.me>  foaf:mbox       <mailto:bob@work.example> .

On applique la requête SPARQL :

PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
ASK  WHERE { ?x foaf:name  "Alice" }

Résultat de la requête: TRUE

En revanche, avec la requête SPARQL :

PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
ASK WHERE { ?x foaf:name  "Alice" ;
          foaf:mbox  <mailto:alice@work.example> }

Le résultat sera : FALSE

Requêtes DESCRIBE

modifier

La requête DESCRIBE a pour fonction de décrire une référence. Bien souvent, c’est le premier niveau des données du graphe en partant de la référence qui est utilisée pour donner un résultat.

Début de l'exemple
Fin de l'exemple


On peut également décrire plusieurs références sélectionnées en même temps et qui respectent des conditions.

Début de l'exemple
Fin de l'exemple

On reviendra sur la requête DESCRIBE dans le chapitre sur les requêtes de découverte.