Ce TME aborde le traitement de requêtes Sparql dans la plateforme Spark.
Récupérer les jeux de données de Freebase et Yago
mkdir -p /tmp/BDLE/dataset cd /tmp/BDLE/dataset tar zxvf /Infos/bd/spark/dataset/freebase/freebase_snippet_surls.tgz tar zxvf /Infos/bd/spark/dataset/yago/yagoFacts5M.tgz cp /Infos/bd/spark/dataset/yago/yagoMiniSample.txt /tmp/BDLE/dataset
TODO (oct 2017): page MPI pour sparql endpoint sur Yago
Aller dans votre répertoire de travail et lancer le spark-shell avec l'option –driver-memory 10G
cd <votre répertoire> spark-shell --driver-memory 10G sc.setLogLevel("ERROR")
Une requête Sparql sélectionne des nœuds dans un graphe RDF qui vérifient certaines conditions. La syntaxe d’une telle requête comporte une clause select qui permet de spécifier les nœuds à retourner et une clause where qui exprime la condition devant être vérifiée. Un motif élémentaire Sparql prend l’une des formes suivantes :
Ici ?x et ?y sont deux variables devant être substituées par des valeurs de sujet et d’objet pour que le motif soit satisfait. L’évaluation d’un motif de graphe consiste donc à trouver liaison entre des sujets ou des objets et des variables. Exemple, soient les trois triplets suivants :
(james joue guitare) (marie joue piano) (james ami_de marie) (marie ami_de james)
le motif ( ?x joue piano) retourne la liaison ?x→marie le motif ( ?x joue ?y) retourne les liaisons ?x→james, ?y→ guitare d’une part et ?x→maire, ?y→piano d’autre part. Il est également possible d’exprimer des motifs complexes tels que ( james ami_de ?x) ( ?x joue ?y) qui permet de trouver l’ami de james qui joue un instrument. Vous remarquerez que pour évaluer ce motif, une jointure est nécessaire pour mettre en correspondance l’objet du triplet (james ami_de marie) avec le sujet du triplet (marie joue piano). Ce type de motif est appelé objet-sujet. Les autres formes possibles sont : sujet-sujet et sujet-objet. Pour formuler une requête Sparql, il suffit de spécifier dans sa clause select les variables à retourner (projection). Par exemple, select ?x where {?x joue ?y } retourne les liaisons ?x→james et ?x→marie. Ici, on ne retourne pas la variable ?y.
Lire un exemple de données
more /Infos/bd/spark/dataset/yago/yagoMiniSample.txt
Lire la liste des propriétés
more /Infos/bd/spark/dataset/yago/yagoFacts5M_properties.txt
case class Triple(sujet: String, prop: String, objet: String) val yagoFile = "/tmp/BDLE/dataset/yagoFacts5M.txt" val yago = sc.textFile(yagoFile). map(ligne => ligne.split("\\t")).coalesce(8). map(tab => Triple(tab(0), tab(1), tab(2))).toDS() yago.persist yago.count
?x <hasCapital> <Nantes>
La requête s'écrit ainsi avec une opération de sélection where
, un renommage withColumnRenamed
et une projection select
:
val r1 = yago.where("prop = '<hasCapital>' and objet = '<Nantes>'"). withColumnRenamed("sujet","x"). select("x") r1.show(5)
Le résultat de la requête est:
| x | +-------------------+ |<Duchy_of_Brittany>| | <Pays_de_la_Loire>| +-------------------+
Afficher toutes les paires (propriété,objet) au sujet de <Barack_Obama>
REPONSE
val t1 = yago.where("sujet = '<Barack_Obama>'"). withColumnRenamed("prop","p"). withColumnRenamed("objet","o"). select("p", "o") t1.show(20)
Le résultat doit être :
| p | o | +----------------+--------------------+ | <graduatedFrom>|<Columbia_University| | <graduatedFrom>|<Harvard_Law_School>| | <hasGender>| <male>| | <hasWonPrize>| <Grammy_Award>| | <hasWonPrize>| <Nobel_Peace_Prize>| |<isAffiliatedTo>|<Democratic_Party> | | <isCitizenOf>| <Germany>| | <isCitizenOf>| <Wales>| | <isCitizenOf>| <United_States>| | <isCitizenOf>| <Scotland>| | <isCitizenOf>| <Kenya>| | <isCitizenOf>| <Israel>| |<isPoliticianOf>| <United_States>| | <livesIn>| <Illinois>| | <livesIn>| <Chicago>| | <wasBornIn>| <Honolulu>| +----------------+--------------------+
La requête est une étoile formée de deux triplets
?x <livesIn> <Paris> . ?x <isLeaderOf> ?z
REPONSE
val t1 = yago.where("prop = '<livesIn>' and objet = '<Paris>'"). withColumnRenamed("sujet","x"). select("x") val t2 = yago.where("prop = '<isLeaderOf>' "). withColumnRenamed("sujet","x"). withColumnRenamed("objet","z"). select("x","z") val t1t2 = t1.join(t2, "x") t1t2.show(5)
Le résultat doit être:
| x | z | +--------------------+--------------------+ | <Louis_Blanc>|<Republican_Union...| |<Stefano_Zacchiroli>| <Software_Heritage>| +--------------------+--------------------+
La requête est un chemin formé de deux triplets
?x <playsFor> ?y . ?y <isLocatedIn> <United_Kingdom>
REPONSE
val t1 = yago.where("prop = '<playsFor>'"). withColumnRenamed("sujet","x"). withColumnRenamed("objet","y"). select("x","y") val t2 = yago.where("prop = '<isLocatedIn>' and objet = '<United_Kingdom>'"). withColumnRenamed("sujet","y"). select("y") val t1t2 = t1.join(t2, "y") t1t2.show(5)
Les 5 premiers éléments du résultat sont :
| y | x | +--------------------+--------------------+ | <Royal_Air_Force>| <George_Ayres>| | <Royal_Air_Force>|<Jack_Jones_(foot...| | <Royal_Air_Force>|<John_Hinton_(foo...| |<University_of_Ed...|<James_Craigen_(f...| |<University_of_Ed...| <Ronald_Brebner>| +--------------------+--------------------+
La requête est un flocon (ou snowflake) formé de 5 triplets :
?x <isCitizenOf> ?y . ?x <actedIn> ?z . ?x <influences> ?t . ?t <livesIn> ?u . ?t <created> ?v
REPONSE
val t1 = yago.where("prop = '<isCitizenOf>'"). withColumnRenamed("sujet","x"). withColumnRenamed("objet","y"). select("x","y") val t2 = yago.where("prop = '<actedIn>'"). withColumnRenamed("sujet","x"). withColumnRenamed("objet","z"). select("x","z") val t3 = yago.where("prop = '<influences>'"). withColumnRenamed("sujet","x"). withColumnRenamed("objet","t"). select("x", "t") val t4 = yago.where("prop = '<livesIn>'"). withColumnRenamed("sujet","t"). withColumnRenamed("objet","u"). select("t","u") val t5 = yago.where("prop = '<created>'"). withColumnRenamed("sujet","t"). withColumnRenamed("objet","v"). select("t", "v") val r = t1.join(t2, "x").join(t3,"x").join(t4,"t").join(t5,"t") r.show(10)
Le résultat, limité à 10 lignes, doit être:
+--------------+----------+---------+-----------------+--------------------+----------------+ | t| x| y| z| u| v| +--------------+----------+---------+-----------------+--------------------+----------------+ |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Cry-Baby>| <Dublin>|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Cry-Baby>|<Republic_of_Irel...|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Suck_(film)>| <Dublin>|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Suck_(film)>|<Republic_of_Irel...|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>|<Hardware_(film)>| <Dublin>|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>|<Hardware_(film)>|<Republic_of_Irel...|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Dead_Man>| <Dublin>|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Dead_Man>|<Republic_of_Irel...|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Gimme_Danger>| <Dublin>|<Dockers_(film)>| |<Irvine_Welsh>|<Iggy_Pop>|<Germany>| <Gimme_Danger>|<Republic_of_Irel...|<Dockers_(film)>| +--------------+----------+---------+-----------------+--------------------+----------------+
L’objectif de cet exercice est d’exprimer en Scala les différents types de motifs complexes.
Q1 : requête avec une seule variable de jointure
SELECT ?b WHERE {?b <book.book.characters> ?c . ?b <book.book.genre> ?x . ?b <book.book.editions> ?y. }
Q2: requête avec un filtre
SELECT ?c WHERE { ?c <people.person.education> ?e . ?c <people.person.date_of_birth> ?x . ?c <people.person.nationality> ?y . FILTER( ?x CONTAINS '1990') }
Q3 : requête avec 2 variables de jointure
SELECT ?ar WHERE { ?ar <music.artist.album> ?al . ?ar <music.artist.genre> ?g . ?ar <music.artist.home_page> ?h. ?al <music.album.release_date> ?d . ?al <music.album.contributor> ?c. ?al <music.album.featured_artists> ?f }
Q4: requête formant un chemin
SELECT * WHERE { ?m <music.recording.artist> ?ar . ?ar <music.artist.supporting_artists> ?sar . ?sar <music.artist.album> ?al }
Q5
SELECT ?ar ?al ?t WHERE { ?ar <music.artist.genre> ?g . ?ar <music.artist.origin> ?o . ?ar <music.artist.album> ?al . ?ar <music.artist.label> ?l . ?ar <music.artist.track> ?t }
a) A partir de Q4 proposer une requête formant un chemin de longueur 4 puis 5 (avec des propriétés différentes de celles de Q4).
b) A partir de Q3, proposer une requête dont une partie du motif est optionnelle.
c) Proposer une requête qui est l'union de deux motifs
d) Proposer une requête avec 3 motifs de triplets et 3 variables de jointures (i.e., motif formant un triangle).
e) Lister les 10 propriétés les plus fréquentes. Lister les 10 sujets les plus fréquents.
f) Etant donné un sujet S0, lister toutes les propriétés pour lesquelles il existe un triplet ayant le sujet S0.