:tocdepth: 3 ======== 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 ] .. nextslide:: .. code-block:: sparql PREFIX foaf: 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. .. code-block:: sparql PREFIX foaf: PREFIX : Termes ++++++ IRI en extension (relatif ou absolu) :: <../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) : .. code-block:: sparql ?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 ``"."``\ : .. code-block:: sparql ?p1 foaf:name "Pierre-Antoine Champin" . * cas particulier : le mot clé ``"a"`` en position de prédicat est un raccourci pour ````\ : .. code-block:: sparql ?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 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 , . * 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 : .. code-block:: sparql <#pa> foaf:know _:quelqun . _:quelqun a foaf:Person ; foaf:name ?n . on peut utiliser la notation ``[]``\ : .. code-block:: sparql <#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`` : .. code-block:: sparql <#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 : .. code-block:: sparql ?p1 a foaf:Person ; foaf:name ?n . OPTIONAL { ?p1 foaf:img ?img } OPTIONAL { ?p1 foaf:phone ?tel } ou .. code-block:: sparql ?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``. .. code-block:: sparql ?p foaf:age ?a . FILTER (?a >= 18) On peut combiner des conditions avec les opérateurs logiques « et » (``&&``), « ou » (``||``) et « non » (``!``). .. code-block:: sparql 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(, )`` Pour plus d'information, consultez la `documentation de SPARQL`__. __ https://www.w3.org/TR/sparql11-query/#expressions 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. .. code-block:: sparql ?p a foaf:Person ; owl:sameAs ?db . FILTER (regex(str(?db), "dbpedia.org")) SERVICE { ?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 WHERE { }`` Expression ++++++++++ Les résultats du SELECT peuvent être des expressions complexes, calculées à partir des variables de la clause WHERE. .. code-block:: sparql SELECT ?p (concat(?gn, " ", ?fn) as ?name) WHERE { ?p foaf:givenName ?gn ; foaf:familyName ?fn . } DISTINCT ++++++++ .. code-block:: sparql 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 : .. code-block:: sparql SELECT ?p WHERE { <#pa> foaf:knows ?p. } LIMIT 10 Pour obtenir les 5 résultats suivants : .. code-block:: sparql SELECT ?p WHERE { <#pa> foaf:knows ?p. } LIMIT 5 OFFSET 10 ORDER BY ++++++++ .. code-block:: sparql SELECT ?p ?n WHERE { <#pa> foaf:knows [ foaf:givenName ?p ; foaf:familyName ?n ] } ORDER BY ?n ?p On peut aussi trier par ordre descendant : .. code-block:: sparql 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``. .. code-block:: sparql 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) : .. code-block:: sparql 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. .. code-block:: sparql 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 ----------------------------------- .. code-block:: sparql 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 ------------------------------------------ .. code-block:: sparql SELECT DISTINCT ?prop WHERE { ?o a ; ?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 { }`` CONSTRUCT +++++++++ * Sert à construire un graphe à partir des résultat d'un autre * Résultat : un graphe RDF * Structure : ``CONSTRUCT { } WHERE { }`` * Peut jouer un rôle similaire à XSL-T pour RDF Exemple ------- .. code-block:: sparql 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/ .. ifslides:: .. slide:: Chapitre suivant `vocab`:doc: