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

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.

## Introduction
### Commentaires

On peut écrire des commentaires dans les requêtes SPARQL. Les commentaires commencent par un '#' en début de ligne.
Tous les logiciels ne gèrent pas les commentaires. Il faut vérifier que cela n'engendre pas d'erreurs avec la base de données que vous utilisez.

### Préfixes

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
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
#### rdf:type

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)
Le prédicat rdf:type sert à sélectionner l’objet en fonction de son type. C'est donc un prédicat qui est souvent utilisé au sein des requêtes, d'où cette abréviation.
Attention, c’est "a" minuscule. Écrire A peut entraîner une erreur.

#### Sujet

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 ";"
Les mêmes triplets avec l’utilisation de ";"

#### Prédicat

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

### Les littéraux

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

### Variables

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
SPARQL 1.1 introduit le mot clé AS pour changer le nom des colonnes comme on peut le faire en SQL.
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
### Structure d'une requête

Une requête SELECT se divise en 5 parties :
* Définition des préfixes si vous utilisez des IRIs relatifs
* Définition des résultats que vous désirez obtenir en sortie
* Définition des jeux de données sur lesquelles porte la requête. Attention : seulement disponible dans SPARQL 1.1
* Définition des conditions
* Modificateurs de résultats (facultatif)
Structure d'une requête SELECT

### Définir le tableau de résultats

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

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

#### ORDER BY

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.
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 :
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 :
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).

#### LIMIT

LIMIT permet de limiter le nombre de lignes retournées.
N'hésitez pas à l’utiliser, car dans beaucoup de cas cela vous permettra d'obtenir des temps de réponse plus faibles du serveur interrogé.

#### OFFSET

OFFSET permet de n'afficher le résultat qu’à partir de la ligne indiquée.
On peut indiquer en même temps une LIMIT et un OFFSET.
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

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

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 :
* la clause SELECT identifie les variables à faire apparaître dans la réponse de cette requête,
* 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 :
On applique la requête SPARQL :
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

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 :
On applique la requête SPARQL :
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

Utilisons le graphe RDF/Turtle suivant :
* "cat"@en est un littéral avec la forme lexicale "cat" et une balise de langue en (pour langue anglaise)
* "42"^^xsd:integer est un littéral typé avec le type de données http://www.w3.org/2001/XMLSchema#integer
* , et "abc"^^dt:specialDatatype est un littéral typé avec le type de données http://example.org/datatype#specialDatatype.

#### Littéral avec une balise de langue

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 :
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 :
donne la solution :
* v
* <http://example.org/ns#x>

#### Littéral avec un type numérique

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 :
donne la solution :
* v
* <http://example.org/ns#y>

#### Littéral avec un type arbitraire

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 :
donne la solution :
* v
* <http://example.org/ns#z>

### Contraintes

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 :

#### Littéral texte

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 :
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 :
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 :
Et voici résultat de la requête :
* title
* "The Semantic Web"@en

#### Littéral numérique

La clause FILTER peut vérifier des conditions numériques.
Par exemple la requête SPARQL :
donnera le résultat:
| title | price |
| "The Semantic Web"@en | 23 |

#### Les autres littéraux

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.

### Jointures

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

#### Jointure basique

Considérer A et B comme des graphes. "A.B" représente une jointure, rassemblant les résultats de A et B en fonction des variables en commun.
Les points qui séparent les triplets dans la clause WHERE représentent des jointures basiques.

#### Jointure à gauche

Considérer A et B comme des graphes. "A OPTIONAL { B }" représente une jointure à gauche. Il rassemble les résultats de A et B en fonction des variables en commun, si possible. Il garde toutes les solutions de A s’il y a ou non des résultats dans B.
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 :
Ainsi la requête :
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 :
Ainsi, la requête :
donne la solution :
| title | price | hpage |
| "SPARQL Tutorial" | 42 | <http://example.org/book/sparql> |
| "The Semantic Web" | | |
| "MediaWiki" | 28 | |

#### Addition

Considérer A et B comme des graphes. "{ A } UNION { B } " représente une addition. Il rassemble les résultats de A et B sans condition.
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 :
Ainsi la requête :
donne la solution :
* title
* "SPARQL Protocol Tutorial"
* "SPARQL"
* "SPARQL (updated)"
* "SPARQL Query Language Tutorial"

#### Soustraction

Seulement disponible pour SPARQL 1.1.
Considérer A et B comme des graphes. "A MINUS { B } " représente une soustraction. Il conserve uniquement les résultats de A qui ne sont pas dans les résultats de B.

### Exercices
#### Interroger un point d'accès SPARQL

* Aller sur le site de DBpedia et trouver l’IRI au sein de DBpedia qui correspond le mieux à la page w:Doctor Who
* 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.
* Écrire une requête pour afficher les sujets et prédicats de cet IRI.
1. En lisant la documentation de DBpedia, vous pouvez comprendre que les IRI des pages Wikipédia sont les objets du triplet suivant :
<http://dbpedia.org/resource/Doctor_Who> foaf:page <http://en.wikipedia.org/wiki/Doctor_Who>
Vous pouvez remarquer que l'IRI du sujet du triplet ne fait pas référence à une langue. Pourtant, les prédicats et les objets de ce sujet ne font pas référence directement à toutes les pages « Doctor Who » dans tous les wikis.
Seule une petite partie de Wikipédia est utilisée par DBpedia et les pages des langues non anglaises sont très peu exploitées par DBpedia.
La page w:Doctor Who ne figure pas directement dans DBpedia. La référence (IRI) la plus proche sera donc <http://dbpedia.org/resource/Doctor_Who>
2. Requête pour afficher les prédicats et objets du sujet : <http://dbpedia.org/resource/Doctor_Who>
3. Requête pour afficher les sujets et prédicats qui font référence à : <http://dbpedia.org/resource/Doctor_Who>

#### Faire une requête complexe

* Avec DBpedia, faire une requête pour trouver les dix premiers films français.
* Vous afficherez en résultat pour chaque film : le lien vers la page Wikipédia du film, la date, le nom et la description.
Vous pouvez voir le résultat de cette requête sur la page : http://en.sparql.pro/wiki/First_French_films
Vous pouvez trouver d'autres exemples sur le site avec DBpedia sur le site : « http://en.sparql.pro/wiki/DBpedia » . Consulté le 2014-11-15

## Requêtes ASK
### Résultat

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

Elle se divise en 3 parties :
* Définition des préfixes si vous utilisez des IRIs relatifs
* Définition des jeux de données sur lesquels porte la requête. Attention : seulement disponible dans SPARQL 1.1.
* Définition des conditions.
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 :
On applique la requête SPARQL :
Résultat de la requête: TRUE
En revanche, avec la requête SPARQL :
Le résultat sera : FALSE

## Requêtes DESCRIBE

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.
Donnera un résultat qui contiendra les triplets dont la racine sera la référence à décrire :
On peut également décrire plusieurs références sélectionnées en même temps et qui respectent des conditions.
On reviendra sur la requête DESCRIBE dans le chapitre sur les requêtes de découverte.
| Le protocole SPARQL | Requêtes d'écriture |