INaLCO - M2 Plurital
Introduction à XML

Jean-François Perrot

Introduction à SPARQL

Mise à jour du 6/11/2013

  1. Introduction
    1. Langage de requêtes
    2. Références
    3. Mise en œuvre

  2. Requêtes de type SELECT
    1. Exemple simple
    2. Un exemple un peu plus élaboré, emprunté à Wikipédia
    3. Une DTD pour la représentation des résultats en XML

  3. Requêtes de type CONSTRUCT
    1. Exemple très simple
    2. Exemple presqu'aussi simple
    3. Exemple un peu moins simple

  4. Contraintes par FILTER
    1. Comparaison : exiger une différence
    2. Conversion des URI et comparaison de chaînes


  1. Introduction

    1. Langage de requêtes

      SPARQL (acronyme obscur dérivé de SQL) est un langage de requêtes destiné à interroger les bases de données (fichiers) RDF,
      standardisé par le W3C et implémenté en divers langages, notamment en Java avec le framework Jena
      (il ne semble pas que ce nom ait quoi que ce soit à voir avec la ville de Thuringe connue en France comme Iéna).

      Nous nous bornerons ici à illustrer deux types de requêtes (SELECT et CONSTRUCT), ainsi que le mécanisme de contraintes FILTER.
    2. Références


    3. Mise en œuvre

      Avec le script Jena précomposé /$JENAROOT/bin/sparql et ses options, en donnant

      • la requête seule, dont le champ FROM contient le chemin (ou l'URL) du fichier de données
        $JENAROOT/bin/sparql --query le_chemin_de_la_requête

      • le fichier de données et la requête
        $JENAROOT/bin/sparql --data chemin_données --query chemin_requête

      • pour avoir une sortie en XML, ajouter l'option "--results XML"
       
      Essayez !
      (en requête seule, et comme le programme travaille sur le serveur,
      il faut que le champ "FROM" de votre requête contienne une URL accessible depuis le réseau.)

      De même qu'on écrit le plus souvent les requêtes SQL au moyen d'une interface aimable,
      on trouve des logiciels permettant de composer des requêtes SPARQL avec un peu de confort.
      Par exemple Twinkle.
  2. Requêtes de type SELECT

    Comme son nom l'indique, le langage de requêtes SPARQL suit son devancier SQL.

    1. Exemple simple

      fichier vcard1.rq

      PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
      SELECT ?prenom
      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/Exemples/vcardCat.rdf>
      WHERE {
          ?x vcard:N ?c .
          ?c vcard:Given ?prenom .
      }



      ce qui se lit :
      chercher dans le fichier "vcardCat.rdf" (ci-dessus) toutes les chaînes (ici désignées par "?prenom")
      telles qu'il existe dans le graphe correspondant deux triplets de la forme  "?x vcard:N ?c" et "?c vcard:Given ?prenom",
      avec la même valeur pour "?c"
      .

      Exécution :

      %/$JENAROOT/bin/sparql --query vcard1.rq
      -------------
      | prenom    |
      =============
      | "Rebecca" |
      | "Matthew" |
      | "Sarah"   |
      | "John"    |
      -------------



      Les valeurs recherchables ne se limitent pas aux chaînes terminales, mais on obtiendra toutjours des chaînes représentatives :

      Exemple : fichier vcard3.rq

      PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
      SELECT ?prenom ?x ?c
      FROM <vcardCat.rdf>
      WHERE {
          ?x vcard:N ?c .
          ?c vcard:Given ?prenom .
      }


      %/$JENAROOT/bin/sparql --query vcard3.rq
      -------------------------------------------------------
      | prenom    | x                                | c    |
      =======================================================
      | "Rebecca" | <http://somewhere/RebeccaSmith/> | _:b0 |
      | "Matthew" | <http://somewhere/MattJones/>    | _:b1 |
      | "Sarah"   | <http://somewhere/SarahJones/>   | _:b2 |
      | "John"    | <http://somewhere/JohnSmith/>    | _:b3 |
      -------------------------------------------------------



      Et le résultat obtenu peut aussi avoir une forme propre à une exploitation ultérieure, par exemple en XML :


      %/$JENAROOT/bin/sparql --query vcard1.rq --results XML
      <?xml version="1.0"?>
      <sparql xmlns="http://www.w3.org/2005/sparql-results#">
        <head>
          <variable name="prenom"/>
        </head>
        <results>
          <result>
            <binding name="prenom">
              <literal>Rebecca</literal>
            </binding>
          </result>
          <result>
            <binding name="prenom">
              <literal>Matthew</literal>
            </binding>
          </result>
          <result>
            <binding name="prenom">
              <literal>Sarah</literal>
            </binding>
          </result>
          <result>
            <binding name="prenom">
              <literal>John</literal>
            </binding>
          </result>
        </results>
      </sparql>



    2. Un exemple un peu plus élaboré, emprunté à Wikipédia

      [Exemple de requête SPARQL retournant une liste de photos avec le nom de la personne qu'elles représentent et une description, à partir d'une base de données de type RDF utilisant l'ontologie (vocabulaire) FOAF (une des plus connues et utilisée pour décrire les personnes et les liens entre elles).]

      fichier ExWiki.rdf (base-source)

      <?xml version="1.0" ?>
      <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
        xmlns:foaf="http://xmlns.com/foaf/0.1/"
        xmlns:dc="http://purl.org/dc/elements/1.1/">
          <foaf:Person rdf:about="http://example.net/Paul_Dupont">
              <foaf:name>Paul Dupont</foaf:name>
              <foaf:img rdf:resource="http://example.net/Paul_Dupont.jpg"/>
              <foaf:knows rdf:resource="http://example.net/Pierre_Dumoulin"/>
          </foaf:Person>
          <foaf:Person rdf:about="http://example.net/Pierre_Dumoulin">
              <foaf:name>Pierre Dumoulin</foaf:name>
              <foaf:img rdf:resource="http://example.net/Pierre_Dumoulin.jpg"/>
          </foaf:Person>
          <foaf:Image rdf:about="http://example.net/Paul_Dupont.jpg">
              <dc:description>Photo d'identité de Paul Dupont</dc:description>
          </foaf:Image>
          <foaf:Image rdf:about="http://example.net/Pierre_Dumoulin.jpg">
              <dc:description>Photo d'identité de Pierre Dumoulin</dc:description>
          </foaf:Image>
      </rdf:RDF>



      ou si vous préférez le lire en format N3 :

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

      <http://example.net/Pierre_Dumoulin>
            a       foaf:Person ;
            foaf:img <http://example.net/Pierre_Dumoulin.jpg> ;
            foaf:name "Pierre Dumoulin" .

      <http://example.net/Paul_Dupont>
            a       foaf:Person ;
            foaf:img <http://example.net/Paul_Dupont.jpg> ;
            foaf:knows <http://example.net/Pierre_Dumoulin> ;
            foaf:name "Paul Dupont" .

      <http://example.net/Paul_Dupont.jpg>
            a       foaf:Image ;
            dc:description "Photo d'identité de Paul Dupont" .

      <http://example.net/Pierre_Dumoulin.jpg>
            a       foaf:Image ;
            dc:description "Photo d'identité de Pierre Dumoulin" .




      fichier ExWiki.rq (requête SPARQL)

      PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
      PREFIX foaf: <http://xmlns.com/foaf/0.1/>
      PREFIX dc: <http://purl.org/dc/elements/1.1/>
      SELECT DISTINCT ?nom ?image ?description
      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/ExWiki.rdf>
      WHERE {
          ?personne rdf:type foaf:Person .
          ?personne foaf:name ?nom .
          ?image rdf:type foaf:Image .
          ?personne foaf:img ?image .
          ?image dc:description ?description
      }


       
      le résultat en mode texte :

      --------------------------------------------------------------------------------------------------------
      | nom               | image                                    | description                           |
      ========================================================================================================
      | "Pierre Dumoulin" | <http://example.net/Pierre_Dumoulin.jpg> | "Photo d'identité de Pierre Dumoulin" |
      | "Paul Dupont"     | <http://example.net/Paul_Dupont.jpg>     | "Photo d'identité de Paul Dupont"     |
      --------------------------------------------------------------------------------------------------------



      et en syntaxe XML (fichier ExWiki.sparql)

      <?xml version="1.0" ?>
      <sparql xmlns="http://www.w3.org/2005/sparql-results#">
          <head>
              <variable name="nom"/>
              <variable name="image"/>
              <variable name="description"/>
          </head>
          <results ordered="false" distinct="true">
              <result>
                  <binding name="nom">
                      <literal>Pierre Dumoulin</literal>
                  </binding>
                  <binding name="image">
                      <uri>http://example.net/Pierre_Dumoulin.jpg</uri>
                  </binding>
                  <binding name="description">
                      <literal>Photo d'identité de Pierre Dumoulin</literal>
                  </binding>
              </result>
              <result>
                  <binding name="nom">
                      <literal>Paul Dupont</literal>
                  </binding>
                  <binding name="image">
                      <uri>http://example.net/Paul_Dupont.jpg</uri>
                  </binding>
                  <binding name="description">
                      <literal>Photo d'identité de Paul Dupont</literal>
                  </binding>
              </result>
          </results>
      </sparql>




    3. Une DTD pour la représentation des résultats en XML

      Le seul point à éclaircir est la signification du nom de balise binding :
      • le verbe to bind signifie lier, son participe présent substantivé binding désigne une liaison, au sens de l'état de deux choses liées ensemble - et dans notre contexte ?
      • dans une requête SPARQL figurent quelques variables (dont les noms sont préfixés par '?'),
        et le résultat de la requête est composé des valeurs que prennent ces variables ;
      • dans le discours technique des logiciens, on préfère parler des valeurs auquelles ces variables sont liées,
        et des différentes liaisons (bindings) dans lesquelles les variables peuvent entrer ;
        l'avantage est que la liaison désigne le couple variable - valeur, non la valeur en elle-même ;
      • un résultat prend donc la forme d'un système de liaisons, avec une liaison par variable...


      <!ELEMENT sparql (head, results)>
      <!ATTLIST sparql xmlns CDATA #REQUIRED>

      <!ELEMENT head (variable*)>
      <!ELEMENT variable EMPTY>
      <!ATTLIST variable name CDATA #REQUIRED>

      <!ELEMENT results (result*)>
      <!ATTLIST results ordered (true|false) #IMPLIED>
      <!ATTLIST results distinct (true|false) #IMPLIED>

      <!ELEMENT result (binding*)>
      <!ELEMENT binding (literal|uri)>
      <!ATTLIST binding name CDATA #REQUIRED>

      <!ELEMENT literal (#PCDATA)>
      <!ELEMENT uri (#PCDATA)>



      Cette DTD est très approximative. Pour tout savoir sur la question, voyez ici !

  3. Requêtes de type CONSTRUCT

    1. Exemple très simple

      Le graphe-gabarit se réduit à un triplet dont seule la relation est spécifiée (vcard:FN),
      le sujet et l'objet étant des variables.

      PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
      PREFIX vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#>

      CONSTRUCT { ?x  vcard:FN ?y }
      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/ExWiki.rdf>
      WHERE  { ?x foaf:name ?y }


      résultat en N3

      @prefix vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#> .
      @prefix foaf:    <http://xmlns.com/foaf/0.1/> .

      <http://example.net/Pierre_Dumoulin>
            vcard:FN      "Pierre Dumoulin" .

      <http://example.net/Paul_Dupont>
            vcard:FN      "Paul Dupont" .



      qui se traduit en XML comme

      <?xml version="1.0" ?>
      <rdf:RDF
          xmlns:foaf="http://xmlns.com/foaf/0.1/"
          xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
          xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#" >
        <rdf:Description rdf:about="http://example.net/Pierre_Dumoulin">
          <vcard:FN>Pierre Dumoulin</vcard:FN>
        </rdf:Description>
        <rdf:Description rdf:about="http://example.net/Paul_Dupont">
          <vcard:FN>Paul Dupont</vcard:FN>
        </rdf:Description>
      </rdf:RDF>



    2. Exemple presqu'aussi simple

      avec un graphe-gabarit à un seul sujet, deux triplets.

      PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
      PREFIX vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#>

      CONSTRUCT { ?x foaf:firstName ?y .
                  ?x foaf:lastName ?z .
                }

      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/Exemples/vcardCat.rdf>
      WHERE
       {        ?x  vcard:N ?u .
                ?u vcard:Given ?y .
                ?u vcard:Family ?z.
       
       }



      résultat en N3

      @prefix vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#> .
      @prefix foaf:    <http://xmlns.com/foaf/0.1/> .

      <http://somewhere/RebeccaSmith/>
            foaf:firstName  "Rebecca" ;
            foaf:lastName  "Smith" .

      <http://somewhere/JohnSmith/>
            foaf:firstName  "John" ;
            foaf:lastName  "Smith" .

      <http://somewhere/SarahJones/>
            foaf:firstName  "Sarah" ;
            foaf:lastName  "Jones" .

      <http://somewhere/MattJones/>
            foaf:firstName  "Matthew" ;
            foaf:lastName  "Jones" .



    3. Exemple un peu moins simple

      avec une ressource anonyme dans le graphe-gabarit

      PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
      PREFIX dc:      <http://purl.org/dc/elements/1.1/>
      PREFIX vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#>

      CONSTRUCT { ?auteur foaf:pastProject _:oeuvre .
                  _:oeuvre dc:title ?nom .
                  _:oeuvre dc:publisher ?editeur .
                  ?auteur foaf:firstName ?p .
                  ?auteur foaf:lastName ?n .
                }

      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/Frantext/RRb.rdf>
      WHERE
       {         ?oe  dc:creator ?auteur .
                 ?oe  dc:title ?nom .
                 ?oe  dc:publisher ?editeur .
                 ?auteur  vcard:N ?vc .
                 ?vc vcard:Family ?n .
                 ?vb vcard:Given ?p .
       }



      résultat en N3

      @prefix vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#> .
      @prefix dc:      <http://purl.org/dc/elements/1.1/> .
      @prefix foaf:    <http://xmlns.com/foaf/0.1/> .

      <http://inalco/M2-Trad/poetes#Théodore_de_BANVILLE>
            foaf:firstName  "Théodore" ;
            foaf:lastName  "de BANVILLE" ;
            foaf:pastProject
                          [ dc:publisher  "Paris : M. Levy, 1859." ;
                            dc:title      "Odes funambulesques"
                          ] .



  4. Contraintes par FILTER

    La clause FILTER introduit une expression booléenne que les solutions à la requête doivent satisfaire.
    Le langage dans lequel s'écrit cette expression est très riche. Il contient des opérateurs de comparaison et des fonctions d'accès.
    Il est décrit dans la section 11 de la spécification.
    En voici deux exemples.
    1. Comparaison : exiger une différence

      Nous faisons l'hypothèse que deux personnes qui ont le même nom de famille se connaissent (foaf:knows).
      Mais nous voulons éviter les résultats banals "X foaf:knows X".
      Pour cela, nous pouvons demander que les deux ressources anonymes désignant les "vcard:N" soient différentes.

      PREFIX foaf:    <http://xmlns.com/foaf/0.1/>
      PREFIX vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#>

      CONSTRUCT { ?x foaf:knows ?y }

      FROM <http://pagesperso-systeme.lip6.fr/Jean-Francois.Perrot/inalco/XML/RDF/Exemples/vcardCat.rdf>
      WHERE
       {         ?x  vcard:N ?va .
                 ?va vcard:Family ?f .
                 ?y  vcard:N ?vb .
                 ?vb vcard:Family ?f .
            FILTER (?va != ?vb )
       }



      résultat en N3

      @prefix vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#> .
      @prefix foaf:    <http://xmlns.com/foaf/0.1/> .

      <http://somewhere/RebeccaSmith/>
            foaf:knows    <http://somewhere/JohnSmith/> .

      <http://somewhere/JohnSmith/>
            foaf:knows    <http://somewhere/RebeccaSmith/> .

      <http://somewhere/SarahJones/>
            foaf:knows    <http://somewhere/MattJones/> .

      <http://somewhere/MattJones/>
            foaf:knows    <http://somewhere/SarahJones/> .



      Bon, mais cette réponse est encore trop verbeuse, car nous savons bien que la relation est symétrique !
      Si on nous dit "X foaf:knows Y", inutile d'ajouter "Y foaf:knows X" !

    2. Conversion des URI et comparaison de chaînes

      La fonction STR va convertir une URI en chaîne, à laquelle on va pouvoir appliquer la comparaison selon l'ordre lexicographique.

      même requête que la précédente, avec
            FILTER (?va != ?vb && STR(?x) < STR(?y) )


      résultat en N3

      @prefix vcard:   <http://www.w3.org/2001/vcard-rdf/3.0#> .
      @prefix foaf:    <http://xmlns.com/foaf/0.1/> .

      <http://somewhere/JohnSmith/>
            foaf:knows    <http://somewhere/RebeccaSmith/> .

      <http://somewhere/MattJones/>
            foaf:knows    <http://somewhere/SarahJones/> .