Benutzer:HvW/SPARQL
SPARQL und Wikidata
Grundlagen
[Bearbeiten]Wikidata
[Bearbeiten]Die Einträge in Wikidata sind als Datentripel abgelegt: Einem Datenobjekt wird mit einer Aussage ein Datenwert zugeordnet.
Im Beispiel ist
- Douglas Adams das Datenobjekt
- Tätigkeit die Aussage
- Schriftsteller der Datenwert
Wenn man sich also die typische Wikidata-Seite eines Datenobjekts anzeigen lässt, dann bekommt man alle Datentripel des Datenobjekts angezeigt, im Beispiel also alle vorhandenen Aussagen über Douglas Adams mit den zugehörigen Datenwerten. Qualifikatoren und Fundstellenangaben lassen wir vorerst beiseite.
Dabei ist Douglas Adams aber nur die Bezeichnung (Label) des Datenobjekts, die sich je nach Sprache/Schriftsystem unterscheiden und die beliebig geändert werden kann. Zur eindeutigen Identifikation des Objekts dient deshalb der Q-Wert, der im Titel in Klammern hinter der Bezeichnung steht, bei Douglas Adams also Q42.
Q42 ist eineindeutig, das heißt, es steht für genau eine Person unabhängig von der Bezeichnung und unter allen Personen mit der Bezeichnung „Douglas Adams“ steht sie genau für den britischen Schriftsteller, der Per Anhalter durch die Galaxis geschrieben hat.
Dasselbe gilt für Aussagen: „Tätigkeit“ ist nur die Bezeichnung der Aussage, P106 ist der eindeutige Identifikator dafür. Bei Aussagen beginnen sie mit P wie property (eigenlich „Eigenschaft“).
In der Datenbank ist also in Wirklichkeit das Tripel „Q42 – P106 – Q36810“ abgelegt. Erst durch die Bezeichnungen übersetzt es sich in „Douglas Adams – Tätigkeit – Schriftsteller“. Dies ist wichtig, weil eine Datenbankabfrage mit eindeutigen Werten arbeiten muss, also mit Q- und P-Werten, um die Objekte und Aussagen exakt identifizieren zu können.
Hinweis: Im Quellcode werden also nur Q- und P-Werte verwendet, keine Labels. Im Eingabefenster wird zwar das Label angezeigt, wenn man mit dem Mauszeiger über den Wert fährt, um den Quelltext aber auch ohne auf- und zuklappende Fenster erfassen zu können, empfiehlt es sich, die Textzeilen zu kommentieren. Dies macht man mit einem „#“: Alles was in einer Zeile nach diesem Zeichen folgt, wird bei der Ausführung ignoriert.
SPARQL
[Bearbeiten]SPARQL ist eine Abfragesprache (query language), mit der in einer Datenbank mit diesem Datenmodell gesucht werden kann.
Der Grundaufbau ist
SELECT ?item ?data ?plus
WHERE
{
?item (wdt:P999) wd:Q99999.
?item wdt:P9999 ?data.
OPTIONAL { ?item wdt:P9999 ?plus. }
}
SELECT („wähle“) startet die Abfrage. Die gefundenen Datenobjekte (item) und weitere Informationen (data, plus) sollen angezeigt werden. Das entspricht 3 Spalten einer Ausgabetabelle. Die Fragezeichen geben an, dass es sich um Variablen handelt, die im Weiteren definiert sind.
Mit WHERE („wobei“) beginnt der mit geschweiften Klammern umschlossene Definitionsbereich.
- Zuerst werden die Aussagen festgelegt, die die Datenobjekte erfüllen müssen: ?item erfüllt die Aussage P999 mit dem Wert Q99999. Das Vorangestellte wdt: kennzeichnet eine Aussage, wd: steht vor Wikidata-Objekten.
- Beispiel: ?item (wdt:P106) wd:Q36180 wählt alle Datenobjekte mit der Tätigkeit (P106) Schriftsteller (Q36180) aus.
- Dann werden die anzuzeigenden Informationen angegeben: Vom ?item wird die Aussage P9999 der Variable ?data zugeordnet
- Beispiel: ?item wdt:P27 ?data legt als zweite Ausgabe (Spalte data) das Land der Staatsangehörigkeit (P27) fest.
- Aber: ist keine Staatsangehörigkeit angegeben, dann wird das ganze Objekt ignoriert. Umschließt man die Informationen mit OPTIONAL { … }, ist das Vorhandensein der Aussage keine Voraussetzung.
- Beispiel: OPTIONAL { ?item wdt:P136 ?plus } zeigt als dritte Information das Genre an, in dem die Person als Schriftsteller tätig ist. Ist kein Genre angegeben, bleibt das Ausgabefeld leer.
Ausgegeben wird somit eine Tabelle mit drei Spalten: item, data und plus. Also die Person, die Nationalität und das literarische Genre. Noch ist das Ergebnis aber nicht brauchbar, da lediglich die Identifikatoren angezeigt werden, also nur Q-Werte. Um eine lesbare Ausgabe zu erhalten, muss man an die Variable „Label“ anhängen, damit stattdessen die Bezeichnung angezeigt wird. Außerdem muss man definieren, aus welcher Sprache die Bezeichnung ausgewählt werden soll. Das sieht dann folgendermaßen aus:
SELECT ?item ?itemLabel ?dataLabel ?plusLabel
WHERE
{
?item (wdt:P999) wd:Q99999.
?item wdt:P9999 ?data.
OPTIONAL { ?item wdt:P9999 ?plus. }
SERVICE wikibase:label { bd:serviceParam wikibase:language "de,en,[AUTO_LANGUAGE]". }
}
In der ersten Spalte bleibt das ?item stehen, der Q-Wert wird nämlich als Link auf das Datenobjekt angezeigt und man kommt so zum kompletten Eintrag. ?itemLabel zeigt vom selben Objekt die Bezeichnung an. Mit ?itemDescription kann man sich auch die zugehörige Beschreibung anzeigen lassen, mit ?itemAltLabel den Text unter „Auch bekannt als“. Als Sprachen sind Deutsch (de), falls die keine Bezeichnung enthält Englisch (en) und danach die intern im System unter [AUTO_LANGUAGE] festgelegte Usersprache eingestellt. Die genaue Syntax von SERVICE würde hier zu weit führen.
Abfragen verfeinern
Der Einstieg war noch etwas unrealistisch, eine Liste aller in Wikidata verzeichneten Schriftsteller ist sicherlich zu groß und unübersichtlich für den Gebrauch. Zwei weitere Informationen über diese Personen wurden aber bereits genannt, mit denen man die Suche einschränken kann. Man kann alle Kriterien, die erfüllt sein müssen, in der Form
- ?item (wdt:P999) wd:Q99999.
untereinander schreiben. Man kann es aber auch vereinfachen zu
- ?item (wdt:P999) wd:Q99999;
- (wdt:P998) wd:Q99998;
- (wdt:P997) wd:Q99997.
Ein Semikolon trennt die verschiedenen Aussagen.
Ein Schriftsteller aus dem Vereinigten Königreich erfüllt dann die Abfrage
- ?item (wdt:P27) wd:Q145; # Land der Staatsangehörigkeit (P27): Vereinigtes Königreich (Q145)
- (wdt:P106) wd:Q36180. # Tätigkeit (P106): Schriftsteller (Q36180)
Man kann aber auch dieselbe Aussage auf mehrere Werte testen, formal
- ?item (wdt:P999) wd:Q99999, wd:Q99998, wd:Q99997.
Beispielsweise kann man Schriftsteller suchen, die sowohl Science-Fiction-, als auch Fantasy-Werke geschrieben haben:
- ?item (wdt:P136) wd:Q24925, wd:Q132311. # Genre (P136): Science-Fiction (Q24925), Fantasy (Q132311)
Etwas komplizierter wird es, wenn man statt „sowohl – als auch“ (die Schnittmenge) die Abfrage „entweder – oder“ (die Vereinigungsmenge) haben will:
- {?item (wdt:P136) wd:Q24925.} # Genre (P136): Science-Fiction (Q24925)
- UNION
- {?item (wdt:P136) wd:Q132311.} # Genre (P136): Fantasy (Q132311)
Mit UNION („Vereinigung“) kann man beliebig viele Bedingungen nacheinander auflisten, wobei jeder Abfrageterm in {}-Klammern eingeschlossen sein muss. Von dieser Abfragenkette muss nur eine Bedingung erfüllt sein.
Die dritte Möglichkeit ist „das eine, aber nicht das andere“ (die Differenzmenge), also beispielsweise reine Science-Fiction-Autoren, die kein Fantasy schreiben:
- {?item (wdt:P136) wd:Q24925.} # Genre (P136): Science-Fiction (Q24925)
- FILTER NOT EXISTS
- {?item (wdt:P136) wd:Q132311.} # Genre (P136): Fantasy (Q132311)
Man filtert also diejenigen Funde heraus, für die ein bestimmter Datenwert nicht existiert.
FILTER ist aber ein noch viel mächtigerer Parameter, mit dem man nicht nur Wahr-falsch-Abfragen machen kann, sondern mit der man mithilfe von Funktionen viele verschiedene Bedingungen für die Aufnahme in das Ergebnis stellen kann, zum Beispiel Personen mit einem bestimmten Alter, Städte ab einer bestimmten Einwohnerzahl oder wissenschaftliche Artikel mit einem bestimmten Stichwort im Titel.
Ausgaben verfeinern
Unschön an der Ausgabe ist, dass für jeden einzelnen Wert eine neue Zeile angelegt wird. Wenn eine Aussage mehrere Werte hat, dann gibt es für dasselbe Objekt mehrere Zeilen. Wenn man nur eine Zeile haben und mehrere Werte in einem Tabellenfeld zusammenfassen will, dann verwendet man anstelle der Variablen ?var
- (group_concat(?varLabel;separator=", ") as ?vars)
Die Bezeichner von ?var (?varLabel) werden zusammengefasst (group concatenation, eigentlich Gruppenverkettung), als Trennzeichen (separator) ist das Komma festgelegt und das Ergebnis wird in der Spalte ?vars angezeigt.
Damit die Anzeige korrekt ausgeführt wird, muss ganz ans Ende nach dem „WHERE { … }“-Abschnitt noch
- GROUP BY ?item ?itemLabel
angefügt werden.
Will man also beispielsweise alle britischen Science-Fiction-Autoren finden, aber bei jedem in einer Zeile auch alle anderen Genre angezeigt bekommen, denen er noch zugeordnet wird, dann sieht das folgendermaßen aus:
SELECT ?Person ?PersonLabel (group_concat(?GenreLabel;separator=", ") as ?Genres)
WHERE
{
?Person (wdt:P27) wd:Q145; # Land der Staatsangehörigkeit (P27): Vereinigtes Königreich (Q145)
(wdt:P106) wd:Q36180; # Tätigkeit (P106): Schriftsteller (Q36180)
(wdt:P136) wd:Q24925. # Genre (P136): Science-Fiction (Q24925)
?Person wdt:P136 ?Genre.
SERVICE wikibase:label { bd:serviceParam wikibase:language "de,en,[AUTO_LANGUAGE]". }
}
GROUP BY ?Person ?PersonLabel
Grundsätzlich erfolgt die Ausgabe des Suchergebnisses in eine sortierbare Tabelle. Trotzdem kann es erwünscht sein, eine Ausgabereihenfolge vorher festzulegen. Dazu wird an die Abfrage nach dem „WHERE { … }“-Abschnitt angefügt
- ORDER BY ?var
wobei ?var für eine der SELECT aufgeführten Variablen und damit für eine der Spalten der Ausgabetabelle steht. Mit
- ORDER BY DESC(?var)
kann die Sortierreihenfolge umgekehrt werden: Standard ist aufsteigend (A bis Z, 1 bis 9), DESC steht für descending, also absteigend (Z bis A, 9 bis 1).
Des Weiteren kann man die Ausgabe auf eine bestimmte Anzahl von Elementen begrenzen mit
- LIMIT 999
In Kombination erlauben es die beiden Angaben, so etwas wie die Top 10 einer Rangliste oder die 25 größten, ältesten Objekte auszugeben. Konkretes Beispiel wäre eine Liste der Top 50 Personen aus Deutschland mit den meisten Abonnenten bei YouTube.