Wikidata:Tutoriel SPARQL

This page is a translated version of the page Wikidata:SPARQL tutorial and the translation is 99% complete.
Outdated translations are marked like this.
Other languages:
Bahasa Indonesia • ‎Deutsch • ‎English • ‎Türkçe • ‎català • ‎dansk • ‎eesti • ‎español • ‎français • ‎italiano • ‎polski • ‎português do Brasil • ‎svenska • ‎русский • ‎українська • ‎հայերեն • ‎العربية • ‎日本語

WDQS, le Wikidata Query Service, est un outil épatant pour répondre à de nombreuses questions que vous pourriez vous poser, et ce guide vous permettra de comprendre comment l'utiliser.

Avant de commencer

Ce guide a l'air très long, et peut sembler intimidant. S'il vous plaît, ne vous laissez pas effrayer ! SPARQL est compliqué, mais acquérir les bases va déjà vous permettre de faire pas mal de chemin — si vous le désirez, vous pouvez vous arrêter de lire après #Notre première requête, et vous en saurez assez pour écrire de nombreuses questions intéressantes. Après cela, les sections de cette page ajoutent des informations sur d'autres sujets que vous pouvez utiliser pour écrire toute sorte de questions. Chacun des sujets va vous outiller pour écrire encore plus de questions formidables — mais aucun des sujets n'est indispensable — vous pouvez vous arrêter de lire à n'importe quel point et — nous l'espérons — repartir d'ici avec beaucoup de connaissances utiles !

Si vous n'avez jamais entendu parler de Wikidata, SPARQL ou WDQS jusqu'à maintenant, voici une courte explication de ces termes :

Wikidata est une base de connaissances. Cette base contient de nombreuses affirmations, comme "la capitale du Canada est Ottawa" ou "la Joconde est peinte avec de la peinture à l'huile sur du bois de peuplier" ou encore "l'or a une conductivité thermale de 25.418 joule par mole-kelvin". SPARQL est un langage pour formuler des questions (requêtes) dans une base de connaissance. Avec la bonne base, une requête SPARQL peut répondre à des questions comme “quelle est la tonalité la plus populaire en musique?” ou “quel est le personnage qui a été le plus joué par des acteurs ou actrices ?” ou “quelle est la distribution des groupes sanguins?” or “quelles sont les œuvres d'auteur qui entrent dans le domaine public cette année?

WDQS, Wikidata Query Service, service de requête Wikidata, joint les deux précédents: vous entrez une requête SPARQL, et elle s'exécute sur l'ensemble des données de Wikidata et vous montre le résultat.

Les bases de SPARQL

Une requête SPARQL simple se présente ainsi:

SELECT ?a ?b ?c
WHERE
{
  x y ?a.
  m n ?b.
  ?b f ?c.
}

La clause SELECT liste les variables que vous voulez renvoyer (les variables commencent avec un point d'interrogation) et la clause WHERE contient des restrictions sur ces variables, principalement sous la forme de triplets; quand vous exécutez la requête, le service de requête essaye de combiner les variables avec les valeurs courantes de telle manière que les triplets résultant de ce remplissage soient présent dans la base de connaissance, et renvoie un résultat pour chaque combinaison de variable que le service trouve.

Un triplet peut être lu comme une phrase (qui se termine avec un point), avec un sujet, un prédicat et un objet. Les termes anglais sont: subject, predicate, and object :

SELECT ?fruit
WHERE
{
  ?fruit aCouleur jaune.
?fruit goût acide.
}

Les résultats pour cette question peuvent inclure, par exemple, "citron". Dans Wikidata, la plupart des propriétés sont de type "a le/la" (en anglais : “has”-kind properties), ainsi la requête pourrait aussi être lue :

SELECT ?fruit
WHERE
{
  ?fruit couleur jaune.
  ?fruit goût aigre.
}

qui se lit comme “?fruit a la couleur ‘jaune’” (et non pas?fruit est la couleur de ‘jaune’” – gardez cela en tête pour les paires de propriété comme “parent”/“enfant”!).

Cependant, ce n'est pas un bon exemple pour WDQS. Le goût est subjectif, aussi Wikidata n'a pas de propriété pour cela. laissons cela de côté, et intéressons nous aux relations parent/enfant, qui sont généralement non-ambigües.

Notre première requête

Supposons que nous voulons la liste des tous les enfants du compositeur baroque Jean Sébastien Bach (Johann Sebastian Bach). En utilisant les pseudo-éléments comme dans les requêtes ci-dessus, comment écririez-vous la requête ?

Avec un peu de chance, vous obtenez quelque chose comme cela :

SELECT ?enfant
WHERE
{
  #  enfant "a pour parent" Bach
  ?enfant parent Bach.
  # (note : tout ce qui se trouve après « # » est un commentaire et est ignoré par WDQS.)
}

ou ceci,

SELECT ?enfant
WHERE
{
  # enfant "a pour père" Bach 
  ?enfant père Bach. 
}

ou ceci,

SELECT ?enfant
WHERE
{
  #  Bach "a pour enfant" enfant
  Bach enfant ?enfant.
}

Les deux premiers triplets disent que la variable ?enfant doit avoir le 'parent/père' Bach; le troisième triplet dit que Bach doit avoir un enfant avec une variable ?enfant. Allons-y avec le deuxième pour l'instant.

Que reste-t-il à faire pour transformer cela en une requête WDQS correcte ? Dans Wikidata, les éléments et les propriétés ne sont pas identifiés par des noms lisibles par des humains tel que "père" (propriété) ou "Bach" (élément). (Pour de bonnes raisons: "Johann Sebastian Bach" est aussi le nom d'un peintre allemand et "Bach" peut aussi faire référence au nom de famille, à la commune française, au cratère sur Mercure, etc.) Au lieu de cela, éléments et propriétés de Wikidata sont affectés à un identifiant. Pour trouver l'identifiant d'un élément, nous cherchons cet élément et nous copions le Q-nombre qui semble être celui de l'élément que nous cherchons (en nous basant sur la description, par exemple). Pour trouver l'identifiant d'une propriété, nous faisons la même chose mais en cherchant “P:terme cherché” au lieu de “terme cherché”, ce qui limite la recherche aux propriétés. Ceci nous apprend que le fameux compositeur Jean-Sébastien bach est Q1339 et que la propriété pour désigner le père d'un élément est P:P22.

Enfin, nous avons besoin d'inclure les préfixes. Pour des triplets WDQS de base, les éléments doivent être préfixés avec wd: et les propriétés avec wdt:. (Mais ceci ne s'applique qu'aux valeurs - les variables n'ont pas de préfixe !)

En mettant tout cela ensemble, nous arrivons à notre première requête WDQS correcte :

SELECT ?enfant
WHERE
{
# ?enfant père Bach
  ?enfant wdt:P22 wd:Q1339.
}

Try it!

Cliquez sur le lien "Essayez!" puis exécutez la requête sur la page WDQS. Qu'obtenez-vous ?

enfant
wd:Q57225
wd:Q76428

Bon c'est décevant. Vous ne voyez que les identificateurs. Vous pouvez cliquer dessus pour voir leur page Wikidata (incluant un label lisible par les humains), mais n'y-a-t-il pas une meilleure manière de voir les résultats ?

Et bien, comme nous allons le voir, c'est possible ! (N'est-ce pas que c'est génial de se poser des questions rhétoriques ?) Si vous incluez le texte magique

SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }

quelque part à l'intérieur de la clause WHERE, vous obtenez des variables additionnelles : pour chaque variable ?foo, vous avez maintenant une variable ?fooLabel qui contient le libellé de l'élément correspondant à ?foo. Si vous ajoutez ceci à la clause SELECT, vous obtenez l'élément et aussi le libellé

SELECT ?enfant ?enfantLabel
WHERE
{
# ?enfant père Bach
  ?enfant wdt:P22 wd:Q1339.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Essayez d'exécuter la requête — vous devriez voir non seulement les numéros des éléments, mais aussi les noms des différents enfants.

enfant enfantLibellé
wd:Q57225 Johann Christoph Friedrich Bach
wd:Q76428 Carl Philipp Emanuel Bach

Auto-complétion

Le bout de code SERVICE est difficile à retenir, n'est-ce pas ? Et parcourir la fonction de recherche pendant que vous écrivez la requête est aussi fastidieux. Heureusement, WDQS offre une bonne solution à ceci: l'auto-complétion. Dans l'éditeur de requêtes query.data.org, vous pouvez appuyer sur Ctrl+Espace à n'importe quel point de la question et avoir des suggestions de code qui peuvent être appropriées; sélectionnez la bonne suggestion avec les touches flèche haut et flèche bas, et appuyer sur Entrée pour la sélectionner.

Par exemple, au lieu d'écrire SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } à chaque fois, vous pouvez saisir SERV, taper Ctrl+Space, et la première suggestion sera l'incantation complète du label du service, prête à l'emploi! Taper simplement Entrée pour l'accepter. (Le formatage sera un peu différent, mais ça n'a pas d'importance.)

Et l'auto-complétion peut aussi chercher pour vous. Si vous tapez un des préfixes Wikidata, comme wd: ou wdt:, et que vous écrivez ensuite du texte après cela, Ctrl+Space va faire une recherche avec ce texte dans Wikidata et suggérer des résultats. wd: cherche des éléments, wdt: des propriétés. Par exemple, au lieu de chercher les éléments pour Johann Sebastian Bach (Q1339) et father (P22), vous pouvez simplement taper wd:Bach et wdt:per et sélectionner la bonne entrée proposée par l'auto-complétion. (Ceci marche aussi avec des espaces dans le texte, e. g. wd:Johann Sebastian Bach.)

Motifs de triplets avancés

Jusqu'à maintenant nous avons vu tous les enfants de Johann Sebastian Bach - plus exactement: tous les éléments avec le père Johann Sebastian Bach. Mais Bach a eu deux épouses, et ces éléments ont donc deux mères différentes: que faire si nous voulons seulement voir les enfants de Johann Sebastian Bach avec sa première épouse, Maria Barbara Bach (Q57487)? Essayez d'écrire cette requête basée sur celle ci-dessous.

C'est fait? Ok, alors la solution. Le plus simple est d'ajouter un deuxième triplet avec cette restriction:

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339.
  ?enfant wdt:P25 wd:Q57487.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

En langage naturel, cela se lit :

Enfant a pour père Johann Sebastian Bach.

Enfant a pour mère Maria Barbara Bach.

Cela semble un peu difficile, n'est ce pas ? En langage naturel, nous abrégerions en :

Enfant a pour père Johann Sebastian Bach et pour mère Maria Barbara Bach.

En fait, il est possible d'exprimer la même version abrégée en SPARQL : si vous terminez un triplet avec un point-virgule (;) au lieu d'un point, vous pouvez ajouter une autre paire prédicat-objet. Ceci nous permet d'abréger la requête ci-dessus en :

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339;
         wdt:P25 wd:Q57487.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

ce qui donne le même résultat, mais avec moins de répétition dans la requête.

Maintenant supposons que, parmi ces résultats, nous ne soyons intéressés que par les enfants qui sont compositeurs et pianistes. Les propriétés et les éléments correspondants sont occupation (P106), composer (Q36834) et pianist (Q486748). Essayez de mettre à jour la requête ci-dessus pour ajouter ces restrictions !

Voici ma solution :

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339;
         wdt:P25 wd:Q57487;
         wdt:P106 wd:Q36834;
         wdt:P106 wd:Q486748.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Cette solution utilise l'abrégé ; deux fois pour ajouter les deux professions. Mais comme vous pouvez le remarquer, il y a encore des répétitions. C'est comme si nous disions :

Enfant a la profession compositeur et la profession pianiste.

que nous abrégerions généralement en :

Enfant a les professions de compositeur et de pianiste.

Et SPARQL a une syntaxe pour ça aussi: de la même manière que ; vous permet d'ajouter une paire prédicat-objet à un triplet (en réutilisant le sujet), , vous permet d'ajouter un autre objet à un triplet (en réutilisant à la fois le sujet et le prédicat). Avec cela, la requête peut être abrégée en :

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339;
         wdt:P25 wd:Q57487;
         wdt:P106 wd:Q36834,
                  wd:Q486748.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Note : l’indentation et les autres espaces n'ont pas d'importance — ils rendent la lecture plus facile. Vous pouvez aussi l'écrire comme :

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339;
         wdt:P25 wd:Q57487;
         wdt:P106 wd:Q36834, wd:Q486748.
  # les deux occupations sont sur une seule ligne
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

ou, encore moins lisible :

SELECT ?enfant ?enfantLabel
WHERE
{
  ?enfant wdt:P22 wd:Q1339;
  wdt:P25 wd:Q57487;
  wdt:P106 wd:Q36834,
  wd:Q486748.
  # aucune indentation ; rend plus difficile la distinction entre ; et ,
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Heureusement, l'éditeur WDQS indente automatiquement les lignes, donc généralement vous n'avez pas à vous en occuper.

Bien, résumons tout cela ici. Nous avons vu que les requêtes sont structurées comme du texte. Chaque triplet sur un sujet est terminé par un point. Des prédicats multiples sur le même sujet sont séparés par des points-virgule, et de multiples objets pour le même sujet et le même prédicat peuvent être écrit comme une liste séparée par des virgules.

SELECT ?s1 ?s2 ?s3
WHERE
{
  ?s1 p1 o1;
      p2 o2;
      p3 o31, o32, o33.
  ?s2 p4 o41, o42.
  ?s3 p5 o5;
      p6 o6.
}

Maintenant je veux introduire une autre abréviation qu'offre SPARQL. Vous me permettrez un autre scénario hypothétique ...

Supposons que nous ne sommes pas tellement intéressés par les enfants de Bach (qui sait, c'est peut-être votre cas !). Mais nous nous intéressons à ses petits-enfants (de manière hypothétique). Il y a une complication ici: un petit-enfant peut être relié à Bach par son père ou par sa mère. Il y a deux propriétés différentes, ce qui n'est pas pratique. Au lieu de ça, sautons par dessus le problème : Wikidata a une propriété « enfant », P:P40, qui pointe d'un parent à un enfant et indépendante du genre. avec cette information, pouvez-vous écrire une requête qui renvoie les petits-enfants de Bach ?

Voici ma solution :

SELECT ?petitEnfant ?petitEnfantLabel
WHERE
{
  wd:Q1339 wdt:P40 ?enfant.
  ?enfant wdt:P40 ?petitEnfant.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

En langage naturel, cela se lit :

Bach a un enfant ?enfant.

?enfant a un enfant ?petitEnfant.

Encore une fois, je propose d'abréger cette phrase en langage naturel, et ainsi je veux vous montrer comment SPARQL fournit une telle abréviation. Observez comment nous ne nous soucions pas réellement de l'enfant : nous n'utilisons la variable ?enfant que pour atteindre le petit-enfant. Par conséquent, nous pouvons abréger la phrase en :

Bach a un enfant quelconque qui a un enfant ?petitEnfant.

Au lieu de dire de quel enfant de Bach il s'agit, nous disons juste un enfant "quelconque" : nous faisons pas attention à l'enfant. Mais nous pouvons y faire référence car nous avons dit un enfant "quelconque" "qui": le "qui" démarre une clause relative (une proposition subordonnée) qui nous permet de dire des choses au sujet de cet enfant "quelconque" (e.g. que elle ou lui "a un enfant ?petitEnfant”). D’une certaine manière, « quelconque » est une variable, mais une variable un peu spéciale qui n'est valide que dans la clause relative, et à laquelle on ne veut pas se référer explicitement (nous disons « une quelconque personne qui est ceci et fait cela », et non pas « une quelconque personne qui est ceci et une quelconque personne qui fait cela » — ce sont deux « quelconques » qui sont des personnes différentes).

En SPARQL, cela peut être écrit comme suit :

SELECT ?petitEnfant ?petitEnfantLabel
WHERE
{
  wd:Q1339 wdt:P40 [ wdt:P40 ?petitEnfant ].
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Vous pouvez utiliser une paire de crochets ([]) à la place d'une variable, ce qui a l'effet d'une variable anonyme. Dans les crochets, vous pouvez spécifier des paires prédicats-objets, comme après un ; qui suit un triplet normal; le sujet implicite est dans ce cas la variable anonyme que les crochets représentent. (Note: comme après un ;, vous pouvez ajouter plus de paires prédicat-objet avec plus de points-virgule, ou plus d'objets pour le même prédicat avec plus de virgules.)

Et voilà pour les motifs de triplets ! Il y a plus dans SPARQL, mais comme nous allons quitter les parties qui sont fortement analogues avec le langage naturel, je voudrai résumer ces analogies encore une fois :

langage naturel exemple SPARQL exemple
phrase Juliette aime Roméo. point juliette aime roméo.
conjonction (clause) Roméo aime Juliette et tue Roméo. point-virgule roméo aime juliette ; tue roméo.
conjonction (noms) Roméo tue Tybalt et Roméo. virgule roméo tue tybalt, roméo.
clause relative (proposition subordonnée) Juliette aime quelqu'un qui tue Tybalt. crochets juliette aime [ tue tybalt ].


Classes et instances

Plus tôt, j'ai dit que la plupart des propriétés Wikidata sont des relations "a le / a la" : "a l'" enfant, "a le" père, "a la" profession. Mais quelquefois (en réalité, fréquemment) vous avez aussi besoin de parler sur ce que quelque chose "est". En fait, il y a deux sortes de relations ici:

  • "Autant en emporte le vent" "est" un film.
  • Un film "est" une œuvre d'art.

"Autant en emporte le vent" est un film en particulier. Il a son metteur en scène (Victor Fleming), une durée spécifique (238 minutes), une distribution d'acteurs (Clark Gable, Vivien Leigh, …), et ainsi de suite.

"Film" est un concept général. Les films peuvent avoir des metteurs en scène, des durées, des distributions d'acteurs, mais le concept "film" ne fait référence à aucun metteur en scène, aucune durée, aucune distribution d'acteurs en particulier. Et bien qu'un film "soit" une œuvre d'art, et qu'une œuvre d'art ait généralement un créateur, le concept de "film" lui-même n'a pas de créateur - seules des "instances" particulières de ce concept en ont un (créateur).

Cette différence explique pourquoi il y a deux propriétés pour "est" dans Wikidata : instance of (P31) et subclass of (P279). Autant en emporte le vent est une instance particulière de la classe "film"; la classe "film" est une sous-classe (une classe plus spécifique; une spécialisation) de la classe plus générale "œuvre d'art".

Pour vous aider à faire la différence, vous pouvez essayer d'utiliser deux verbes différents : "est" et "est une sorte de". Si le verbe "est une sorte de" fonctionne (e.g. Un film "est une sorte de" oeuvre d'art), ceci indique que vous énoncez un fait sur une sous-classe, une spécialisation d'une classe plus générale et vous devez utiliser subclass of (P279). Si "est une sorte de" ne fonctionne pas (e.g. la phrase "Autant en emporte le vent est une sorte de film" n'a pas de sens), cela indique que vous énoncez un fait sur une instance particulière et vous devez utiliser instance of (P31).

Note pour le français: la traduction française choisie pour "instance of" est "nature de l'élément", ce qui diffère sensiblement des autres traductions qui veulent plus ou moins dire "est un " ou "instance de". La justification donnée dans la page instance of (P31) est la suivante "Cet élément est un exemple spécifique de cette classe qui en précise la nature."

Donc qu'est ce que ça signifie pour nous lorsque nous écrivons des requêtes SPARQL ? Lorsque nous voulons chercher pour "toutes les œuvres d'art", ce n'est pas suffisant de chercher tous les éléments qui sont des instances directes de "œuvre d'art" :

SELECT ?oeuvre ?oeuvreLabel
WHERE
{
  ?oeuvre wdt:P31 wd:Q838948. # instance d'une œuvre d'art
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

AU moment ou j'écris ceci, cette requête retrouve 2615 résultats - évidemment, il y a plus d’œuvres d'art que cela ! Le problème est qu'il manque des éléments comme "Autant en emporte le vent", qui est seulement une instance de "film" et non de "œuvre d'art". "film" est une sous-classe d'"œuvre d'art", mais nous devons dire à SPARQL de prendre cela en compte lors de la recherche.

Une solution possible est la syntaxe [] dont nous avons déjà parlé: "Autant en emporte le vent" est l'instance d'une sous-classe "quelconque" de "oeuvre d"art". (Pour vous exercer, essayez d'écrire cette requête !) Mais cela pose toujours des problèmes :

  1. Nous n'incluons plus maintenant des éléments qui sont des instances directes de "œuvre d'art".
  2. Nous manquons des éléments qui sont des instances de certaines sous-classes de certaines "autres" sous-classes de "œuvre d'art" - par exemple, "Blanche-Neige et les sept nains" est un dessin animé, qui est un film, qui est une œuvre d'art. Dans ce cas, nous avons besoin de deux propriétés "sous-classe de" - mais on pourrait en avoir besoin de trois, quatre, cinq, de n'importe quel nombre en réalité.

La solution: ?element wdt:P31/wdt:P279* ?classe. Cela veut dire qu'il y a un chemin entre l'élément et la classe qui comporte une propriété « nature de l'élément » et n'importe quel nombre de fois la propriété « sous-classe de ».

SELECT ?oeuvre ?oeuvreLabel
WHERE
{
  ?oeuvre wdt:P31/wdt:P279* wd:Q838948. # instance de n'importe quelle sous-classe d'une œuvre d'art
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

(Je ne recommande pas d'exécuter cette requête. WDQS peut la gérer (tout juste), mais il est possible que votre navigateur se plante lors de l'affichage des résultats car ils sont très nombreux.)

Maintenant vous savez chercher parmi toutes les œuvres d'art ou tous les bâtiments ou toutes les colonies humaines: l'incantation magique wdt:P31/wdt:P279* avec la classe appropriée. Ceci utilise certaines caractéristiques de SPARQL que je n'ai pas encore expliqué, mais honnêtement, on a là (presque) la seule utilisation pertinente de ces caractéristiques, ainsi vous n'avez pas "besoin" de comprendre comment ça fonctionne pour utiliser efficacement WDQS . Si vous voulez en savoir plus, je vais expliquer cela un petit peu, mais vous pouvez aussi sauter la prochaine section et mémoriser ou copier-coller wdt:P31/wdt:P279* à partir d'ici quand vous en avez besoin.

Chemins de propriétés

Les chemins de propriétés sont une manière d'écrire sobrement une suite de propriétés entre deux éléments. Le chemin le plus simple est composé d'une seule propriété, ce qui forme un triplet ordinaire :

?item wdt:P31 ?class.

On peut ajouter des maillons de chemins avec un slash droit (/).

?item wdt:P31/wdt:P279/wdt:P279 ?class.

Ce qui est équivalent à l'une ou l'autre des formulations suivantes:

?item wdt:P31 ?temp1.
?temp1 wdt:P279 ?temp2.
?temp2 wdt:P279 ?class.
?item wdt:P31 [ wdt:P279 [ wdt:P279 ?class ] ].

Exercice: ré-écrivez la question précédente sur les "petits-enfants" de Bach, en utilisant cette syntaxe.

Une astérisque (*) après un maillon de chemin signifie « zéro ou plus de ce maillon ».

?item wdt:P31/wdt:P279* ?class.
# means:
?item wdt:P31 ?class
# or
?item wdt:P31/wdt:P279 ?class
# or
?item wdt:P31/wdt:P279/wdt:P279 ?class
# or
?item wdt:P31/wdt:P279/wdt:P279/wdt:P279 ?class
# or ...

S'il n'y a pas d'autres maillons dans le chemin, ?a quelqueChose* ?b signifie que ?b peut suivre directement ?a, avec aucun maillon entre eux deux.

Un plus (+) est similaire à une astérisque, mais signifie "un" ou plus d'un maillon. La requête suivante trouve tous les descendants de Bach :

SELECT ?descendant ?descendantLabel
WHERE
{
  wd:Q1339 wdt:P40+ ?descendant.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Si nous avions utilisé une astérisque au lieu d'un plus, les résultats de la requête auraient aussi inclus Bach lui-même.

Un point d'interrogation (?) est similaire à une astérisque ou à un plus, mais a la signification "zéro ou un de ce maillon".

Vous pouvez séparer des maillons de chemin avec une barre verticale (|)au lieu d'un slash avant; ceci signifie "soit-soit" (une alternative). Le chemin peut utiliser l'une ou l'autre des propriétés. (Mais pas les deux - un maillon "soit-soit" est toujours en correspondance avec un chemin d'une seule propriété.)

Vous pouvez aussi grouper les maillons avec des parenthèses (()), et combiner librement toutes ces différentes syntaxes (/|*+?). Ceci signifie qu'une autre manière de trouver tous les descendants de Bach est:

SELECT ?descendant ?descendantLabel
WHERE
{
  ?descendant (wdt:P22|wdt:P25)+ wd:Q1339.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Au lieu d'utiliser la propriété "enfant" pour aller de Bach à ses descendants, nous utilisons la propriété "mère" et "père" pour aller des descendants jusqu'à Bach. Le chemin peut contenir deux mères et un père ou quatre pères ou père-mère-mère-père ou tout autre combinaison. (Bien que, évidemment, Bach ne peut être la mère de personne, donc le dernier maillon sera toujours père.)

Qualificatifs

Note pour la traduction française : Qualifiers ne font pas partie du SPARQL 1.1 https://www.w3.org/TR/sparql11-query/

Wikidata emploie le terme de qualificatifs, c'est celui qu'on utilise.

(D'abord, les bonnes nouvelles: cette section n'introduit pas de nouvelle syntaxe de SPARQL! Donc prends une petite respiration et relaxe-toi, ceci devrait être très simple.)

Nous avons jusqu'à présent seulement parlé des déclarations simples: sujet, propriété, objet. Mais les déclarations de Wikidata sont plus que cela: une déclaration aussi peut avoir des qualificatifs et des références. Par exemple, la Joconde (Q12418) a trois déclarations pour material used (P186):

  1. oil paint (Q296955), le matériel principal;
  2. poplar wood (Q291034), avec le qualificatif applies to part (P518) painting surface (Q861259) – c'est le matériel sur lequel a été peint le tableau; et
  3. wood (Q287), avec le qualificatif applies to part (P518) stretcher (Q1737943) et start time (P580) 1951 – c'est une partie qui a été ajoutée à la peinture plus tard.

Supposons que nous voulions trouver toutes les peintures avec le matériau de leur support de peinture, c'est-à-dire ayant une déclaration material used (P186) avec le qualificatif applies to part (P518) painting surface (Q861259). Comment faire ? C'est davantage d'informations qu'on peut représenter avec un seul triplet.

La réponse est: davantage de triplets! (Règle d'or: La solution de Wikidata pour la plupart des choses est "plus d'éléments", et la règle correspondante pour WDQS est "plus de triplets". Références, précision numérique, valeurs avec leurs unités, géolocalisation, etc. (dont nous n'allons pas parler ici) fonctionnent de cette façon). Nous avons jusqu'à présent utilisé le préfixe wdt: dans nos déclarations (triples), qui pointent directement vers l'objet d'une déclaration. Mais il existe aussi un autre préfixe p: qui ne pointe pas sur l'objet, mais sur le "nœud de déclaration". Ce nœud est alors le sujet d'autres triplets : le préfixe ps: (pour property -propriété- statement -déclaration-) pointe sur l'objet de la déclaration, le préfixe pq: (property -propriété- qualifier -alificatif-) sur les qualificatifs, et prov:wasDerivedFrom pointe sur les nœuds de références (que nous n'aborderons pas maintenant).

Ceci a été très abstrait. Nous allons prendre un exemple plus concret avec la Joconde :

wd:Q12418 p:P186 ?statement1.    # Mona Lisa: material used: ?statement1
?statement1 ps:P186 wd:Q296955.  # value: oil paint

wd:Q12418 p:P186 ?statement2.    # Mona Lisa: material used: ?statement2
?statement2 ps:P186 wd:Q291034.  # value: poplar wood
?statement2 pq:P518 wd:Q861259.  # qualifier: applies to part: painting surface

wd:Q12418 p:P186 ?statement3.    # Mona Lisa: material used: ?statement3
?statement3 ps:P186 wd:Q287.     # value: wood
?statement3 pq:P518 wd:Q1737943. # qualifier: applies to part: stretcher bar
?statement3 pq:P580 1951.        # qualifier: start time: 1951 (pseudo-syntax)

Nous pouvons abréger ceci si nous utilisons la syntaxe [], remplaçant les variables ?item

wd:Q12418 p:P186 [ ps:P186 wd:Q296955 ].

wd:Q12418 p:P186 [
            ps:P186 wd:Q291034;
            pq:P518 wd:Q861259
          ].

wd:Q12418 p:P186 [
            ps:P186 wd:Q287;
            pq:P518 wd:Q1737943;
            pq:P580 1951
          ].

Peux-tu utiliser cette connaissance pour écrire une requête pour toutes les peintures avec le matériel où elles ont été peintes?

Voici ma solution :

SELECT ?peinture ?peintureLabel ?materiel ?materielLabel
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

D'abord, nous limitons ?peinture à toutes les instances de painting (Q3305213) ou subclase de la même. Après, nous obtenons le matériel du nœud de déclaration p:P186, en limitant les déclarations à celles qui ont un qualificatif applies to part (P518) painting surface (Q861259).

ORDER and LIMIT

Nous revenons sur notre visite régulière des caractéristiques de SPARQL

Jusqu'à maintenant, nous avons écrit des requêtes dont l'ensemble des résultats nous intéressaient. Cependant il est fréquent de se soucier seulement de certains résultats: ceux qui sont extrêmes d'une manière ou d'une autre - la plus vieille, la plus jeune, la plus ancienne, la plus récente, la plus élevée parmi une population, la température de fusion la plus basse, le plus d'enfants, le matériel le plus souvent utilisé, etc. Le facteur commun ici est que les résultats sont "classés" d'une certaine manière, et qu'ensuite nous nous intéressons seulement aux premiers résultats (ceux avec le meilleur classement).

Ceci est contrôle par deux clauses, qui sont ajoutées au bloc WHERE {} (après les accolades, et non à l'intérieur!): ORDER BY and LIMIT.

ORDER BY quelqueChosetrie les résultats selon quelqueChose. quelqueChose peut être n'importe quelle expression– pour l'instant, le seul type d'expression que nous connaissons sont les simples variables (?quelqueChose), mais nous en verrons d'autres plus tard. Cette expression peut être caractérisée avec soit ASC() soit DESC() pour préciser l'ordre de classement (ascendant ou descendant). (Si vous ne précisez acun ordre, l'ordre par défaut est l'ordre ascendant, ainsi ASC(quelqueChose) est équivalent à quelqueChose.)

LIMIT compte coupe la liste de résultats à compte résultats, où compte est un nombre entier naturel. Par exemple, LIMIT 10 limite la requête à dix résultats. LIMIT 1 ne revoie qu'un seul résultat.

(Vous pouvez aussi utiliser LIMIT without ORDER BY. Dans ce cas, les résultatrs en sont pas triés, aussi vous n'avez aucune garantie sur les résultats que vous recevez. C'est bien s'il vous arrive de savoir qu'il n'y a qu'un certain nomre de résultats, ou bien que vous n'êtes intéressés que par "quelques" résultats, peu importe lesquels. Dans d'autres cas, ajouter LIMIT peut accélérer significativement le temps de traitement de la requête, puisque WDQS peut arrêter la recherche de résultats dès qu'il y en a assez pour la limite demandée.)

C'est le temps des exercices ! Essayez d'écrire une requête qui renvoie les dix nations les plus peuplées. (Une nation est un sovereign state (Q3624078), et la propriété pour la population est P:P1082.) Vous pouvez commencer par chercher les nations avec leur population, puis ajouter les clauses ORDER BY et LIMIT.

Voici ma solution :

SELECT ?nation ?nationLabel ?population
WHERE
{
  ?nation wdt:P31/wdt:P279* wd:Q3624078;
           wdt:P1082 ?population.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
ORDER BY DESC(?population)
LIMIT 10

Try it!

Notez que si nous voulons les nations les "plus" peuplées, nous avons à les ordonner par population "descendante", afin que les premiers résultats soient ceux avec les valeurs les plus élevées.

Exercice

Nous avons couvert beaucoup de matériel jusqu'ici - je pense qu'il est temps de faire quelques exercices. (Vous pouvez sauter cette section si vous êtes pressé.)

Les livres d'Arthur Conan Doyle

Écrire une requête qui renvoie tous les livres de Sir Arthur Conan Doyle.

Éléments chimiques

Écrire une requête qui renvoie tous les éléments chimiques avec leur symbole chimique et leur nombre atomique, dans l'ordre de leur nombre atomique.

Les rivières qui se jettent dans le Mississippi

Écrire une requête qui renvoie toutes les rivières qui se jettent directement dans le Mississippi. (La plus grande difficulté est de trouver la bonne propriété ...)

Les rivières qui se jettent dans le Mississippi II

Écrire une requête qui renvoie toutes les rivières qui se jettent dans le Mississippi, directement ou indirectement.

OPTIONAL

Dans les exercices ci-dessous, il y a une requête pour tous les livres de Sir Arthur Conan Doyle

SELECT ?book ?bookLabel
WHERE
{
  ?book wdt:P50 wd:Q35610.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Mais cela est un peu court. Il y a tellement plus de données disponibles sur livres et on montre seulement leur libellé ? Essayons de rédiger une requête qui inclut aussi les valeurs de title (P1476), illustrator (P110), publisher (P123) et publication date (P577).

Un premier jet peut ressembler à ceci :

SELECT ?livre ?titre ?illustrateurLabel ?editeurLabel ?publie
WHERE
{
  ?livre wdt:P50 wd:Q35610;
        wdt:P1476 ?titre;
        wdt:P110 ?illustrateur;
        wdt:P123 ?editeur;
        wdt:P577 ?publie.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Lancez cette requête. À l’heure ou ces lignes sont écrites, elle renvoie seulement deux résultat — un peu maigre ! Pourquoi donc alors que nous avons trouvé précédemment une centaine de livres ?

La raison est que pour faire correspondre cette requête, un résultat potentiel (un livre) doit correspondre à tous les triplets listés : il doit avoir un titre, et un illustrateur, et un éditeur, et une date de publication. Si un des livres a quelques unes de ces propriétés, mais pas toutes, elles ne seront pas sélectionnées. Et ce n'est pas ce que nous voulons ici : nous voulons lister avant tout tous les livres. Si des données supplémentaires, sont disponibles, on aimerait les inclure sans pour autant limiter notre liste des résultats.

La solution est de dire à WDQS que ces triplets sont optionnels (OPTIONAL) :

SELECT ?livre ?titre ?illustrateurLabel ?editeurLabel ?publie
WHERE
{
  ?livre wdt:P50 wd:Q35610.
  OPTIONAL { ?livre wdt:P1476 ?titre. }
  OPTIONAL { ?livre wdt:P110 ?illustrateur. }
  OPTIONAL { ?livre wdt:P123 ?editeur. }
  OPTIONAL { ?livre wdt:P577 ?publie. }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Cela nous donne des variables supplémentaires (?titre, ?editeur etc.) si l'instruction appropriée existe. Sinon, le résultat est ignoré et la variable n'est tout simplement pas définie.

Note : ici, il est très important d'utiliser de manière séparée les clauses OPTIONAL. Si vous mettez tous les triplets dans une seule, comme ici —

SELECT ?book ?title ?illustratorLabel ?publisherLabel ?published
WHERE
{
  ?book wdt:P50 wd:Q35610.
  OPTIONAL {
    ?book wdt:P1476 ?title;
          wdt:P110 ?illustrator;
          wdt:P123 ?publisher;
          wdt:P577 ?published.
  }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

Try it!

— vous remarquerez que la plupart des informations n'inclue pas d'information supplémentaire. Ceci est dû au fait qu'une clause optionnelle avec des triplets multiples correspond uniquement quand ces trois triplets correspondent à l'objet choisi. C'est Si un livre a un titre, un illustrateur, un éditeur et une date de publication alors la clause optionnelle identifie le livre et ses valeurs sont assignées aux variables appropriées. Mais si un livre a, par exemple, un titre mais pas d'illustrateur, la clause entière ne correspondra pas au livre, et bien que le résultat ne soit pas ignoré, les quatre variables restent vides.

Expressions, FILTER et BIND

Cette section peut paraître un peu plus désorganisée que les autres parce qu'elle couvre un sujet large et divers. Le concept basique est qu'on aimerait maintenant faire quelque chose avec les valeurs qu'on avait, alors, juste sélectionné et renvoyé sans distinction. "Expressions" est le moyen d'effectuer ces opérations sur des valeurs. Il existe de nombreux types d’expressions que vous pouvez utiliser, mais commençons par les notions de base : les types de données.

Types de données

Chaque valeur dans SPARQL a un type qui vous informe de la nature la valeur et ce que vous pouvez en faire. Les types les plus importants sont :

  • item, comme wd:Q42 pour Douglas Adams (Q42).
  • boolean (booléen) avec deux valeurs possibles true et false. Les booléens ne sont pas stockés dans les déclarations, mais beaucoup d'expressions renvoient un booléen comme 2 < 3 (qui renvoie true) ou "a" = "b" (false).
  • string, une chaîne de caractères. Les chaînes sont écrites entre des guillemets doubles ".
  • texte monolingue, une chaîne de caractères avec un tag informant la langue. Dans un littéral, vous pouvez ajouter le tag après la chaîne avec @ comme dans "Douglas Adams"@en.
  • nombres entiers (1) ou décimaux (1.23).
  • dates. Les littéraux de type date peuvent être écrits en ajoutant ^^xsd:dateTime (le code est sensible à la casse, ^^xsd:datetime ne marchera pas) à une date littérale ISO 8601 : "2012-10-29T00:00:00Z"^^xsd:dateTime.

Opérateurs

Les opérateurs mathématiques habituels sont disponibles: +, -, *, / pour additionner, soustraire, multiplier ou diviser des nombres, <, >, =, <=, >= pour les comparer. L'opérateur d'inégalité ≠ s'écrit !=. Les comparaisons sont définies aussi sur d'autres types ; par exemple, "abc" < "abd" est vrai (comparaison lexicographique), de même que "2016-01-01"^^xsd:dateTime > "2015-12-31"^^xsd:dateTime et wd:Q4653 != wd:Q283111. Les conditions booléennes de type ET peuvent être combinées avec && (le ET logique: a && b est vrai si a et b sont vrais) et celles de type OU avec || (le OU logique: a || b est vrai si soit l'un soit l'autre soit les deux a et b sont vrai(s)).

FILTER

  Info For a sometimes faster alternative to FILTER, you might also look at MINUS, see example.

FILTER(condition). est une clause qu'on peut insérer dans une requête SPARQL pour filtrer les résultats. Dans les parenthèses, on peut mettre n'importe quelle expression de type boolean, et seuls les résultats où l'expression rend true sont utilisés.

Par exemple, pour obtenir une liste de tous les êtres humains nés en 2015, nous obtenons d'abord tous les êtres humains avec leur date de naissance (dob : date of birthday).

SELECT ?person ?personLabel ?dob
WHERE
{
  ?person wdt:P31 wd:Q5;
          wdt:P569 ?dob.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". } 
}

- et après nous filtrons pour obtenir seulement les résultats où l'an de naissance est 2015. Il y a deux manières de faire : extraire l'an de la date avec la fonction YEAR et tester que c'est 2015

FILTER(YEAR(?dob) = 2015).

ou bien vérifier que la date est comprise entre le 1 janvier 2015(inclus) et le 1 janvier 2016 (exclus):

FILTER("2015-01-01"^^xsd:dateTime <= ?dob && ?dob < "2016-01-01"^^xsd:dateTime).

Je dirais que la première manière est plus simple, mais il se trouve que la seconde est beaucoup plus rapide, donc nous allons l'utiliser:

SELECT ?personne ?personneLabel ?dob
WHERE
{
  ?personne wdt:P31 wd:Q5;
          wdt:P569 ?dob.
  FILTER("2015-01-01"^^xsd:dateTime <= ?dob && ?dob < "2016-01-01"^^xsd:dateTime).
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". } 
}

Try it!

Une autre utilisation de FILTERest relative aux étiquettes (label). Le service de labels est très utile pour afficher le label d'une variable. Mais si on veut faire quelque chose avec l'étiquette, par exemple, vérifier s'il commence avec "M. "- vous verrez que ça ne fonctionne pas:

SELECT ?human ?humanLabel
WHERE
{
  ?human wdt:P31 wd:Q15632617.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
  #This FILTER does not work!
  FILTER(STRSTARTS(?humanLabel, "Mr. ")).
}

Try it!

Cette requête trouve toutes les instances de fictional human (Q15632617) et teste si son étiquette commence avec "Mr." (STRSTARTS est l'abréviation de "string starts [with]"; il y a aussi STRENDS et CONTAINS). La raison pour laquelle ceci ne fonctionne pas est vient du fait que le service d'étiquettes ajoute ses variables très tard pendant l'évaluation de la requête; au moment où nous essayons de filtrer ?humanLabel , le service d'étiquettes n'a pas encore créé cette variable.

Heureusement, le service d'étiquette n'est pas l'unique moyen d'obtenir l'étiquette d'un item. Les étiquettes sont aussi stockées comme des triplets réguliers, en utilisant le predicat rdfs:label. Bien sûr, ceci est vrai pour toutes les étiquettes, pas seulement celles en anglais; si nous voulons des étiquettes en anglais, nous devrons filtrer le langage de l'étiquette:

FILTER(LANG(?label) = "en").

La fonction LANG rend la langue d'une chaîne monolingue, et ici nous sélectionnons les étiquettes qui sont en anglais. La requête complète est:

SELECT ?humain ?humainLabel
WHERE
{
  ?humain wdt:P31 wd:Q15632617;
         rdfs:label ?humainLabel.
  FILTER(LANG(?label) = "[AUTO_LANGUAGE]").
  FILTER(STRSTARTS(?label, "Mr. ")).
}

Try it!

Nous obtenons l'étiquette avec le triplet ?humain rdfs:label ?humainLabel, nous la restreignons aux étiquettes en anglais et après nous vérifions si elle commence avec “Mr. ".

On peut aussi utiliser FILTER avec une expression régulière. Dans l'exemple suivant:

SELECT ?item ?itemLabel ?bblid
WHERE {  
    ?item wdt:P2580 ?bblid .
    SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en" }  
    FILTER(!REGEX(STR(?bblid), "[\\.q]")) 
}

Try it!

Si la contrainte de format pour un ID est [A-Za-z][-.0-9A-Za-z]{1,}:

SELECT ?item ?itemLabel ?bblid
WHERE {  
    ?item wdt:P2580 ?bblid .
    SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en" }  
    FILTER(!REGEX(STR(?bblid), "^[A-Za-z][-.0-9A-Za-z]{1,}$"))
}

Try it!

Il est possible de filtrer des éléments spécifiques comme ceci

FILTER ( ?item not in ( wd:Q4115189,wd:Q13406268,wd:Q15397819 ) )

It is possible to filter and have elements that aren't filled:

FILTER ( NOT EXISTS { ?item  wdt:P21 [] } )

BIND, BOUND, IF

Ces trois fonctionnalités sont souvent utilisées ensembles, par conséquent nous allons d’abord expliquer les trois, avant d’en montrer des exemples.

Une clause BIND(expression AS ?variable). s’utilise pour assigner à une variable la valeur d’une expression (en général une nouvelle variable mais il est également possible de changer la valeur de variables existantes).

BOUND(?variable) teste si une variable a effectivement une valeur assignée ou si elle est indéfinie. (la valeur de retour true (elle a une valeur) ou faux (elle n’est pas définie)). C’est principalement utile pour des variables introduites dans une clause OPTIONAL.

IF(condition, expressionAlors, expressionSinon) s’évalue en expressionAlors si condition est vraie, et on expressionSinon si condition est fausse. Par exemple, IF(true, "oui", "non") s’évalue en oui, et IF(false, "super", "terrible") s’évalue en "terrible".

BIND peut être utilisé pour assigner le résultat d’un calcul à une nouvelle variable. Il est possible de s’en servir comme résultat intermédiaire d’un calcul plus complexe ou directement dans le résultat de la requête. Par exemple, pour calculer l’âge des victimes de condamnation à mort :

SELECT ?personne ?personneLabel ?age
WHERE
{
  ?personne wdt:P31 wd:Q5;
          wdt:P569 ?ne;
          wdt:P570 ?Mort;
          wdt:P1196 wd:Q8454.
  BIND(?Mort - ?ne AS ?ageEnJours).
  BIND(?ageEnJours/365.2425 AS ?ageEnAnnees).
  BIND(FLOOR(?ageEnAnnees) AS ?age).
  # ou, avec une seule expression
  #BIND(FLOOR((?Mort - ?ne)/365.2425) AS ?age).
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

BIND peut également être utile pour assigner des valeurs particulières (constantes) à des variables pour aider à la clarté de la requête. Par exemple, dans une requête qui trouve toutes les femmes prêtres :

SELECT ?femme ?femmeLabel
WHERE
{
  ?Femme wdt:P31 wd:Q5;
         wdt:P21 wd:Q6581072;
         wdt:P106 wd:Q42603.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

peut être réécrit comme ceci:

SELECT ?femme ?femmeLabel
WHERE
{
  BIND(wdt:P31 AS ?nature).
  BIND(wd:Q5 AS ?humain).
  BIND(wdt:P21 AS ?sexeOuGenre).
  BIND(wd:Q6581072 AS ?femme).
  BIND(wdt:P106 AS ?occupation).
  BIND(wd:Q42603 AS ?prêtre).
  ?femme ?nature ?humain;
         ?sexeOuGenre ?femme;
         ?occupation ?prêtre.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

Le cœur de la requête, de ?femme à ?prêtre. est probablement raisonnablement lisible. En revanche le large bloc BIND en face de ce cœur est sans doute assez dérangeant, donc cette technique s’utilise avec modération. (Dans l’interface utilisateur WDQS, il est possible de passer la souris sur n’importe quel terme comme wd:Q123 ou wdt:P123 pour voir les libellés et descriptions des entités correspondantes, ce qui permet de se passer dans une certaine mesure de cette astuce.)

Les expressions IF s’utilisent souvent avec des conditions construites avec BOUND. Supposons par exemple que nous ayons une requête d’humains, et qu’au lieu d’afficher leur libellés, nous souhaitions afficher leur pseudonym (P742) si leurs entités en sont pourvues, et le libellés si nous ne leur connaissons pas de pseudonymes. Nous pouvons alors chercher leur pseudonyme dans une clause «  OPTIONAL » (car nous ne voulons pas les rejeter des résultats si ils n’ont pas de pseudo), puis le code BIND(IF(BOUND(… pour sélectionner soit leur pseudonyme soit le libellé.

SELECT ?writer ?label
WHERE
{
  # French writer born in the second half of the 18th century
  ?writer wdt:P31 wd:Q5;
          wdt:P27 wd:Q142;
          wdt:P106 wd:Q36180;
          wdt:P569 ?dob.
  FILTER("1751-01-01"^^xsd:dateTime <= ?dob && ?dob < "1801-01-01"^^xsd:dateTime).
  # get the English label
  ?writer rdfs:label ?writerLabel.
  FILTER(LANG(?writerLabel) = "en").
  # get the pseudonym, if it exists
  OPTIONAL { ?writer wdt:P742 ?pseudonym. }
  # bind the pseudonym, or if it doesn’t exist the English label, as ?label
  BIND(IF(BOUND(?pseudonym),?pseudonym,?writerLabel) AS ?label).
}

Try it!

D’autres propriétés peuvent s’utiliser sur le même motif, comme nickname (P1449), posthumous name (P1786) et taxon common name (P1843) — dans toutes les situations ou il est possible de trouver une alternative sensée en cas de défaut d’information ou d’information préférée à une autre, comme les dénominations.

On peut aussi combiner BOUND et FILTER pour s’assurer qu’au minimum un des blocs OPTIONAL est satisfait. Par exemple, récupérons tous les astronautes ayant fait le voyage vers la Lune, ainsi que les membres de Apollo 13 (Q182252) (ils ne sont pas passé loin, pas vrai ?) Cette restriction ne peut s’exprimer comme un unique chemin de propriété, nous avons donc besoin d’une clause OPTIONAL pour les «  membres d’une mission vers la Lune » et d’une autre pour les « membres d’Apollo 13 ». Mais nous voulons sélectionner uniquement ceux pour lesquels l’une de ces deux conditions est vraie.

SELECT ?astronaute ?astronauteLabel
WHERE
{
  ?Astronaute wdt:P31 wd:Q5;
             wdt:P106 wd:Q11631.
  OPTIONAL {
    ?Astronaute wdt:P450 ?mission.
    ?mission wdt:P31 wd:Q495307.
  }
  OPTIONAL {
    ?Astronaute wdt:P450 wd:Q182252.
    BIND(wd:Q182252 AS ?mission).
  }
  FILTER(BOUND(?mission)).
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}

Try it!

COALESCE

La fonction COALESCE est utile comme abbréviation du motif BIND(IF(BOUND(?x), ?x, ?y) AS ?z). pour les solutions de repli fallbacks mentionnées ci-dessus : elle prend un certain nombre d’expression et retourne la valeur de la première qui s’évalue sans erreur. En exemple, les replis pour l’exemple «  pseudonyme »

BIND(IF(BOUND(?pseudonyme),?pseudonyme,?ecrivainLabel) AS ?libelle).

peut être écrit plus concisément avec

BIND(COALESCE(?pseudonyme, ?ecrivainLabel) AS ?libelle).

il est aussi facile d’ajouter un autre libellé de repli au cas où ?ecrivainLabel n’est pas défini non plus :

BIND(COALESCE(?pseudonyme, ?ecrivainLabel, "<no label>") AS ?libelle).

Groupement

Jusqu’à présent toutes les requêtes que nous avons vues trouvaient l’ensemble des éléments qui satisfont des conditions ; dans certaines nous avons également ajouté des déclarations supplémentaires de ces éléments (tableaux avec leur matériaux, les livre d’Arthur Conan Doyle avec leur titre et leur illustrateur).

Mais il arrive souvent que l'on ne veuille pas une longue liste avec tous les résultats. À la place, nous pouvons poser des questions ainsi:

  • Combien de tableaux sont peints sur toile / sur peuplier / etc. ?
  • Quel est la plus grosse population des villes des différents pays ?
  • Quel est le nombre total d’armes à feux produit par chaque fabricant ?
  • Qui publie en moyenne les plus longs livres ?

Populations des villes

Regardons maintenant la seconde question. Il est relativement simple d’écrire une requête qui liste toutes les villes avec leur population et leur pays, triées par pays :

SELECT ?Pays ?Ville ?Population
WHERE
{
  ?Ville wdt:P31/wdt:P279* wd:Q515;
        wdt:P17 ?Pays;
        wdt:P1082 ?Population.
}
ORDER BY ?Pays

Try it!

(Note: cette requête retourne "beaucoup" de résultats, ce qui peut poser des problèmes à votre navigateur. Vous voudrez peut-être ajouter une contrainte LIMIT.)

Comme nous ordonnons les résultats par pays, toutes les villes d’un pays forment un bloc contigu dans les résultats. Pour trouver la plus grande population à l’intérieur de ce bloc, nous allons considérer ce bloc comme un « groupe » et agréger toutes les valeurs de population de ce groupe en une seule valeur : le maximum. C’est fait grâce à une clause GROUP BY au-dessous du bloc WHERE, et d’une fonction d’agrégation (MAX) dans la clause SELECT.

SELECT ?pays (MAX(?population) AS ?PopulationMax)
WHERE
{
  ?ville wdt:P31/wdt:P279* wd:Q515;
        wdt:P17 ?pays;
        wdt:P1082 ?population.
}
GROUP BY ?pays

Try it!

Nous avons remplacé ORDER BY par GROUP BY. Ça a pour effet que tous les résultats avec la même valeur de ?country sont regroupé dans un seul résultats. Nous devons en conséquence également changer la clause SELECT. Si nous avions conservé l’ancienne clause SELECT ?pays ?ville ?population, quelle ville et quelle population seraient choisis ? Rappelez vous que nous souhaitons regrouper plusieurs résultats en un unique résultat agrégé (on dira agrégat dans la suite), nous avons donc plusieurs valeurs différentes possibles dans un résultat regroupé ; tous les résultats regroupés ont le même pays, il n’y a donc pas de choix pour la valeur ?pays, mais pour ?ville et ?population, nous devons spécifier un choix de valeur. C’est le rôle des fonctions d’agrégation. Dans ce cas, nous avons utilisé MAX: de toutes les valeurs de ?population des résultats que nous regroupons, nous choisissons la valeur maximale pour le résultat de l’agrégat. (Nous devons également nommer la valeur calculée à partir des valeurs agrégées grâce à la construction AS, mais c’est un détail.)

C’est une technique générale d’écriture de requêtes agrégées: d’abord écrire des requêtes non agrégées qui retournent les résultats que vous souhaitez, puis rajouter une clause GROUP BY et une fonction d’agrégation pour chacune des variables non regroupées dans la clause SELECT.

Matériaux de peinture

Essayons ça sur un autre sujet : Combien de tableaux ont été peints en utilisant les différents matériaux ? D’abord, écrivons une requête pour trouver tous les tableaux et les matériaux desquels ils sont faits. (En faisant attention de ne choisir que les déclarations avec un qualificateur applies to part (P518) painting surface (Q861259).)

SELECT ?matériau ?tableau
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
}

Try it!

Ensuite, ajoutons une clause GROUP BY sur le ?materiau, puis une fonction d'agrégation sur l’autre variable choisie (?tableau). Dans ce cas, nous sommes intéressés par le nombre de tableaux ; la fonction nécessaire pour ceci est COUNT.

SELECT ?materiau (COUNT(?peinture) AS ?décompte)
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
}
GROUP BY ?materiel

Try it!

Ceci nous pose un problème car nous n'avons pas de label pour les matériaux, donc les résultats sont un peu difficiles à interpréter. Si nous ajoutons juste la variable de label, nous allons avoir une erreur:

SELECT ?matériau ?matériauLabel (COUNT(?peinture) AS ?décompte)
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}
GROUP BY ?materiel

Try it!

Bad aggregate

“Bad aggregate”

(mauvais agrégat) est un message d’erreur que vous rencontrerez probablement beaucoup en travaillant sur ce type de requêtes. Il signifie qu’une des variables utilisée dans la clause « select » a besoin d’une fonction d’agrégation mais est utilisée en dehors d’un appel à une telle fonction, ou à l’inverse qu’une variable utilisée dans un appel de fonction d’agrégation n’apparait pas dans group by. Dans notre cas, WDQS pense qu’il pourrait y avoir plusieurs ?materiauLabels par ?materiau (même si nous savons que ça ne peut pas arriver), et se plaint donc que vous avez utilisé cette variable en dehors d’une fonction d’agrégation.

Une solution est de regrouper à partir de plusieurs variables. Si vous listez plusieurs variables dans la clause GROUP BY, il y a un résultat pour chaque combinaisons de valeurs de ces variables, et vous pouvez utiliser toutes ces variables dans une fonction d’agrégation. Dans notre cas, nous allons agréger en utilisant à la fois ?materiau et ?materiauLabel.

SELECT ?matériau ?matériauLabel (COUNT(?peinture) AS ?décompte)
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}
GROUP BY ?matériau ?matériauLabel

Try it!

Nous avons presque terminé la requête — une dernière amélioration en touche finale : nous souhaiterions voir les matériaux les plus utilisé en premier. Heureusement, nous pouvons utiliser les nouvelles variables agrégées de la clause SELECT (ici, ?décompte) dans une clause ORDER BY, c’est donc très simple :

SELECT ?matériau ?matériauLabel (COUNT(?peinture) AS ?décompte)
WHERE
{
  ?peinture wdt:P31/wdt:P279* wd:Q3305213;
            p:P186 [ ps:P186 ?materiel; pq:P518 wd:Q861259 ].
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}
GROUP BY ?matériau ?matériauLabel
ORDER BY DESC(?décompte)

Try it!

Écrivez également les autres requêtes en guise d’exercice.

Fusils par fabricant

Quel est le nombre total d'armes produites par chaque fabricant?

Editeurs par nombre de pages

Quel est le nombre de pages moyen des livres des différents éditeur ? (fonction: AVG, de

average

, moyenne en anglais)

HAVING (qui ont)

Un petit addendum à la requête précédente — si vous regardez les résultats, vous remarquerez peut-être que le premier résultat à une valeur déraisonnablement grande, plus de 10 fois plus grande que pour le deuxième résultat. Une petite enquête révèle que c’est parce que cet éditeur a publié un unique livre dont l’élément a une déclaration number of pages (P1104), Grande dizionario della lingua italiana (Q3775610), ce qui biaise quelque peu les résultats. Pour supprimer ce style de valeur aberrantes, nous pouvons tenter de sélectionner les éditeurs qui ont publié au moins deux livres munis de déclarations number of pages (P1104) sur Wikidata.

Comment on fait ça ? En temps normal, nous filtrons les résultats à l’aide d’une clause FILTER, mais dans ce cas nous souhaitons filtrer en fonctions d’une valeur agrégée (le nombre de livres), et pas d’un résultat unique. Nous pouvons le faire grâce à une clause HAVING, qui doit être placée juste après la clause GROUP BY qui lui correspond, écrite grâce à des expressions similaire à n’importe quel autre clause FILTER :

SELECT ?éditeur ?éditeurLabel (AVG(?pages) AS ?moyennePages)
WHERE
{
  ?livre wdt:P123 ?éditeur;
        wdt:P1104 ?pages.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE]". }
}
GROUP BY ?éditeur ?éditeurLabel
HAVING(COUNT(?livre) > 1)
ORDER BY DESC(?moyennePages)

Try it!

Sommaire des fonctions d’agrégation

Voici un court sommaire des fonctions d’agrégation disponibles :

  • COUNT: le nombre de résultats agrégés. Vous pouvez également écrire COUNT(*) pour simplifier le décompte de tous les résultats.
  • SUM, AVG: la somme et la moyenne, respectivement, de tous les résultats agrégés. Si certains résultats ne sont pas des nombres, vous obtiendrez des résultats étranges.
  • MIN, MAX: respectivement les valeurs minimales et maximales de tous les résultats agrégés. Ces fonctions fonctionnent avec tous les types de données ; les nombres sont triés numériquement et les chaînes et autre type lexicographiquement.
  • SAMPLE: un élément quelquonque. Parfois utile si vous êtes certain qu’il y a un unique résultat dans l’agrégat, ou que vous n’avez pas de préférence sur l’élément à choisir.
  • GROUP_CONCAT: concatènes tous les résultats de l’agrégation. Rarement utile, mais si vous êtes curieux, vous pouvez en trouver la description dans la spécification SPARQL specification.

Il est de plus possible d’ajouter un modificateur DISTINCT pour n’importe laquelle de ces fonctions d’agrégation pour éliminer les résultats dupliqués. Par exemple, si nous avons deux résultats qui ont tous les deux la même valeur de ?var dans un agrégat, alors COUNT(?var) retournera 2 mais COUNT(DISTINCT ?var) retournera plutôt 1. Il est souvent nécessaire d’utiliser DISTINCT quand la requête peut retourner le même élément plusieurs fois — ça peut arriver exemple lorsque vous utilisez ?item wdt:P31/wdt:P279* ?class et qu’il y a plusieurs chemins de ?item vers ?class: vous allez avoir un résultat pour chacun de ces chemins, y compris si les valeurs sont identiques. Dans le cas ou vous n’agrégez rien, il est possible d’éliminer ces résultats en doublons en démarrant la requête par SELECT DISTINCT à la place de simplement SELECT.

wikibase:Label et le bogue d'agrégation

Un problème existe actuellement (février 2020) avec le service des requêtes lorsque vous voulez utiliser le service wikibase:label et les fonctions d'aggrégation. Une requête telle que la suivante, qui recherche toutes les personnes académiques avec plus de deux pays de citoyenneté dans Wikidata, et qui est sensée afficher les noms de ces pays dans une chaîne agrégée :

select ?person ?personLabel (group_concat(?citizenshipLabel;separator="/") as ?citizenships) {
  # find all academics
  ?person wdt:P106 wd:Q3400985 ;   
          wdt:P27  ?citizenship .
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
} group by ?person ?personLabel having (count(?citizenship) > 2)

Try it!

échoue pour afficher quelque chose dans la colonne ?citizenships . Un contournement possible consiste à nommer explicitement le ?personLabel et ?citizenshipLabel dans l'appel au service wikibase:label ainsi :

  SERVICE wikibase:label { 
    bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". 
    ?citizenship rdfs:label ?citizenshipLabel .
    ?person      rdfs:label ?personLabel .
  }

La requête suivante fonctionne comme on l'attendait :

select ?person ?personLabel (group_concat(?citizenshipLabel;separator="/") as ?citizenships) {
  ?person wdt:P106 wd:Q3400985 ;
          wdt:P27  ?citizenship .
  SERVICE wikibase:label { 
    bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". 
    ?citizenship rdfs:label ?citizenshipLabel .
    ?person      rdfs:label ?personLabel .
  }
} group by ?person ?personLabel having (count(?citizenship) > 2)

Try it!

VALEURS

On peut choisir les éléments à considérer à partir d’une liste d’éléments

SELECT ?item ?itemLabel ?mother ?motherLabel WHERE {
  VALUES ?item { wd:Q937 wd:Q1339 }
  OPTIONAL { ?item wdt:P25 ?mother. }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}

Try it!

On peut également choisir les valeurs de déclarations d’une propriété en les énumérant dans une liste

SELECT ?item ?itemLabel ?mother ?motherLabel ?ISNI WHERE {
  VALUES ?ISNI { "0000 0001 2281 955X" "0000 0001 2276 4157" }
  ?item wdt:P213 ?ISNI.
  OPTIONAL { ?item wdt:P25 ?mother. }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}

Try it!

VALUES peut également faire plus et construire des énumérations de valeurs possible pour un couple (ou un n-uplet) de variables. Par exemple imaginons que nous souhaitons utiliser des libellés préétablis pour les personnes énumérées dans le premier exemple « value ». Il est possible d’utiliser une clause valeur comme VALUES (?item ?customItemLabel) { (wd:Q937 "Einstein") (wd:Q1339 "Bach") } qui assurera qu’à chaque fois que ?item aura la valeur wd:Q937 dans un résultat, ?customItemLabel aura la valeur Einstein dans ce même résultat, et que quand ?item aura la valeur wd:Q1339 dans les autres résultats, ?customItemLabel vaudra Bach.

SELECT ?item ?customItemLabel ?mother ?motherLabel WHERE {
  VALUES (?item ?customItemLabel) { (wd:Q937 "Einstein") (wd:Q1339 "Bach") }
  OPTIONAL { ?item wdt:P25 ?mother. }
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}

Try it!

Et plus loin...

Ce guide se termine ici. SPARQL pas : il y a encore beaucoup que je n’ai pas montré — il n’a pas été promis que le guide serait complet ! Si vous êtes arrivés si loin, vous savez déjà pas mal de choses sur WDQS et devriez être capable d’écrire des requêtes très puissantes. Si vous voulez en savoir encore davantage, voici des ressources auxquelles vous pouvez jeter un œil :

  • Requêtes imbriquées. Vous pouvez ajouter une requête complète entourée d’accolades à l’intérieur d’une requête, et le résultat sera visible dans la requête extérieure. (Si vous êtes familier avec SQL, il existe une différence essentielle avec les requêtes imbriquées dans SQL – les variables de la requêtes externe ne sont pas visibles depuis la sous-requête, ce qui ne permet pas les «  sous-requêtes corrélées » de SQL.)
  • MINUS vous permets de sélectionner les résultats qui ne correspondent pas à un motif de graphe. FILTER NOT EXISTS est pour l’essentiel équivalent (voir la spec SPARQL pour un exemple ou les résultats diffèrent), mais – au moins avec WDQS – parfois significativement moins performant.
  • VALUES est un moyen simple d’énumérer des valeurs possibles pour une variable (ou un n-uplet de variables).

La principale référence pour ces sujets et d’autres est la spécification SPARQL.

On peut également consulter ce tutoriel SPARQL (en anglais?) sur Wikibooks et ce tutoriel par data.world.

Bien sur, il y a des fonctionnalités de Wikidata qui ne sont pas encore couvertes, comme les références, la précision des quantités (100±2.5), les unités de quantités ( 2 kilogrammes), les coordonnées géographiques, les liens de site, les déclarations sur les propriétés, et autres. Vous pouvez voir comment ceux ci sont modélisés en tant que triplets à la page mw:Wikibase/Indexing/RDF Dump Format.

See also