SPARQL

SPARQL je dotazovací jazyk pro psaní dotazů nad bázemi znalostí (knowledge base) definovanými pomocí ontologií. Syntaxe SPARQL je velmi jednoduchá, ale dotazy mohou být velmi silné.

Ve dnešních příkladech napřed použijeme SPARQL playground, později se podíváme na mnohem zajímavější a větší data z wikidata. Online verze SPARQL playground mi teď nefunguje, můžete si ho ale stáhnout a pustit i lokálně (na běžných OS, potřebujete Javu, s JDK16 na win mi nefungovalo, s JDK11 na Linuxu ve WSL ano).

Nejjednodušší SPARQL dotazy jsou SELECT dotazy. Základní forma tohoto dotazu je několik trojic každá tvaru <object> <relation> <subject>. Například ve SPARQL playground máme několik věcí, z nichž některé patří do třídy dbo:Person, všechny osoby tedy můžeme dostat pomocí dotazu

SELECT ?person WHERE {
    ?person rdf:type dbo:Person
}

Slova začínající ? jsou proměnné v našem dotazu. Proměnné mohou být použité místo libovolné části trojice a mohou být použity v jednom dotazu i vícekrát. Potom je jejich hodnota vždy stejná.

Jak dbo tak rdf jsou prefixy, které specifikují ontologii (pomocí URL) která definuje danou relaci. V playground nemusíme některé prefixy definovat, jsou nastavené globálně. Pokud bych to ale chtěli udělat, můžeme před dotazem použít klíčové slovo PREFIX. Např. řádka níže definuje rdf prefix.

PREFIX rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

Ve SPARQL můžeme ale vytvářet i složitější dotazy. Např. můžeme následovat více než jednu relaci. Zkusme napsat dotaz, který vybere pouze ženy (tj. osoby ženského pohlaví). Pokud se podíváme na schéma na obrázku v playground, vidíme, že objekty mají definovanou vlastnost tto:sex, která má hodnoty “male” nebo “female”. Pokud tedy chceme vybrat pouze ženy, můžeme napsat dotaz:

SELECT ?person WHERE {
    ?person rdf:type dbo:Person .
    ?person tto:sex "female"
}

Dotaz doslova říká, že nás zajímají pouze osoby (první řádka), které jsou ženského pohlaví (druhá řádka). Všimněte si ., která odděluje řádky.

Dotaz také může vracet např. seznam dvojíc. V dalším dotazu si ukážeme, jak vybrat osoby a zároveň jejich mazlíčky. Relace tto:pet specifikuje, že osoba má nějakého mazlíčka, dotaz potom je:

SELECT ?person ?pet WHERE {
    ?person tto:pet ?pet
}

Kdybych chtěli seznam všech osob (včetně těch, co žádného mazlíčka nemají) a k nim jmen jejich mazlíčků, můžeme specifikovat, že druhá řádka je nepovinná pomocí slova optional:

SELECT ?person ?pet WHERE {
    ?person rdf:type dbo:Person .
    optional {?person tto:pet ?pet}
}

Kromě toho můžeme také používat obecnější filter výrazy pro vybrání jen výsledků, které splňují nějakou podmínku. Např. pro vyhledání osob, které nemají žádného mazlíčka můžeme použít

SELECT ?person WHERE {
    ?person rdf:type dbo:Person .
    filter not exists {?person tto:pet ?pet}
}

Filtr je obecnější než jen tento not exists příklad. Můžeme napsat v zásadě libovolné relace na proměnných, které máme (např. aritmetické relace pro číselné hodnoty).

Zatím jsme se moc nebavili o prostředním prvku trojice (relaci mezi objekty). Ta může být také složitější a jsme schopni používat např. / pro zapsání zřetězení relací. Pokud bychom např. chtěli vědět, kdo je dědečkem Eve můžeme napsat

SELECT ?grandparent WHERE {
    ttr:Eve dbo:parent ?parent .
    ?parent dbo:parent ?grandparent
}

nebo také pomocí zřetězené relace jako

SELECT ?grandparent WHERE {
    ttr:Eve dbo:parent/dbo:parent ?grandparent
}

Kromě řetězení můžeme používat i složitější způsoby. Pro nalezení podtříd nějaké třídy můžeme použít relaci (rdfs:subclass). Všechny přímé podtřídy třídy tto:Creature dostaneme pomocí

select ?subclass where {
    ?subclass rdfs:subClassOf tto:Creature
}

a pokud nás zajímají úplně všechny podtřídy (včetně nepřímých) můžeme použít zápis s +, který znamená alespoň jedno opakovaní dané relace

select ?subclass where {
    ?subclass rdfs:subClassOf+ tto:Creature
}

Kromě + existuje i *, která znamená i 0 opakování, tedy přidá do výsledku i levou stranu.

Také můžeme použít ^ k invertování relace - ?a ^rel ?b je ekvivalentní s ?b rel ?a. Toto je extrémně užitečné v kombinaci s možností specifikovat disjunkci mezi relacemi pomocí |. Například můžeme specifikovat všechny lidi, kteří jsou buď předci nebo potomci Williama (ttr:William) pomocí

SELECT ?relative WHERE {
    ttr:William (dbo:parent | ^dbo:parent) ?relative
}

SPARQL také umožňuje další věci, které asi znáte z SQL. Např. můžeme třídit výsledky pomocí ORDER BY, omezit počet výsledků pomocí LIMIT a agregovat výsledky pomocí GROUP BY.

Pokud bychom například chtěli zjistit počet osob podle pohlaví, můžeme použít následující dotaz:

select ?sex (COUNT(?people) as ?peopleCount) where {
  ?people rdf:type dbo:Person .
  ?people tto:sex ?sex .
}
GROUP BY ?sex

Kromě COUNT existuje celá řada dalších agregačních funkcí, které mohou být použity v dotazech.

Pokud chcete o SPARQL vědět více, doporučuji projít i další příklady ve SPARQL playground.

Podívejme se teď na zajímavější bázi znalostí - Wikidata. Wikidata má rozhraní po pokládání dotazů na query.wikidata.org. Můžete si tam také prohlédnout řadu příkladů. My se pokusíme získat seznam všech řek v ČR společně s velikostí jejich povodí. Objekty a relace ve Wikidata mají číselné kódy, se kterými se ne úplně dobře pracuje, rozhraní ale umí vyhledávat i podle jmen a popisků vlastností a objektů. Důležité je vědět, že vlastnosti mají většinou prefix wdt: a objekty mají prefix wd:. Pokud ve výsledcích klikneme na nějaký odkaz, uvidíme i jeho ostatní vlastnosti (objekt si také můžeme vyhledat na hlavní stránce Wikidata).

Pro náš dotaz (řeky v ČR) potřebujeme napřed zjistit, jaké je jméno třídy pro řeky, a jak zjistit, že něco je řeka. Pokud začneme psát dotaz, zjistíme, že jakmile napíšeme wdt: lze dohledat vlastnost wdt:P31, která znamená “instance of”, potom můžeme začít psát wd: a zjistíme, že třída pro řeky je wd:Q4022. Začátek našeho dotazu tedy je

SELECT ?river ?riverLabel WHERE
{
  ?river wdt:P31 wd:Q4022
         
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
LIMIT 10

Použili jsme LIMIT k omezení počtu výsledků (abychom se třeba mohli podívat na další vlastnosti řek). Také jsme použili SERVICE, který vrací užitečná čitelná jména vrácených objektů. Například, pokud se zeptáme na ?river, service nám doplní ?riverLabel, který obsahuje rozumně pochopitelné jméno. Proměnná se jménem se dá definovat v nastavení service i ručně. Defaultně je stejná jako dotazovaná proměnná s přidaným “Label”. Změnit toto jméno můžeme tak, že přidáme ?river rdfs:label ?otherRiverLabel za . v popisu service. Taková definice se také dá použít pokud chceme label i pro proměnné, které přímo nevracíme. Pokud nadefinujeme jméno labelu pro jednu proměnnou, musíme specifikovat i všechny ostatní proměnné, kde label chceme.

Pokud klikneme na jeden z výsledků, můžeme zjistit například to, že řeky mají vlastnost “country”, která obsahuje země, ve kterých řeka teče. Pokud na tuto vlastnost klikneme, uvidíme i to, že má kód P17, můžeme ji tedy použít v dotazu jako wdt:P17, a můžeme pokračovat v našem dotazu.

SELECT DISTINCT ?river ?riverLabel ?area WHERE
{
  ?river wdt:P31 wd:Q4022 .
  ?river wdt:P17 wd:Q213 .
  ?river wdt:P2053 ?area
         
  SERVICE wikibase:label { bd:serviceParam wikibase:language "[AUTO_LANGUAGE],en". }
}
ORDER BY DESC(?area)

Na Wikidata můžeme pokládat celou řadu dalších dotazů, doporučuji si také projít některé příklady na webu. Wikidata ale není jediná velká báze znalostí dostupná pomocí SPARQL rozhraní. Další podobnou bází je např. dbpedia.org, která má rozhraní na adrese dbpedia.org/sparql.

Domácí úkol

Dnešní domácí úkol má dvě nezávislé části - vytvoření ontologie v Protégé a napsání několika SPARQL dotazů nad Wikidata (nebo dbpedia).

Body: 5 bodů za každou část
Termín: 25. 4. 2023

Návrh ontologie

Navrhněte ontologii vhodnou pro reprezentaci knihovního katalogu. V ontologii chceme mít třídy pro knihy a autory. Knihy by měly mít vlastnosti jako např. žánr, název, autor atd. Autoři by měli mít vlastnosti jako jméno, datum narození, země, kde žijí, apod. Uvažujte také o nějaké hierarchii tříd minimálně pro země.

Po navržení tříd definujte vhodné relace mezi třídami pro autory a knihy a autory a země. Vytvořte také několik definovaných tříd jako např. knihy od Evropských autorů, nebo knihy napsané před rokem 1900 apod.

Schválně nedefinuji ontologii přesně, cílem je i to, abyste si promysleli, co v ontologii potřebujete. Není cílem vytvářet desítky tříd, stačí vždy pár příkladů (například nemusíte vytvářet všechny možné země, ale stačí pár příkladů).

Dotazy nad Wikidata

Napište dotazy nad Wikidata, které vrací následující informace

  1. Herci, kteří získali Oskara (Academy Award) seřazení podle počtu jakýchkoliv cen, které získali, sestupně, společně se seznamem těchto cen.
  2. Všechny řeky, které tečou (přímo nebo nepřímo) do Vltavy setříděné sestupně podle velikosti jejich povodí.
  3. Napište ještě jeden netriviální dotaz nad Wikidata nebo dbpedia o čemkoliv, co vás zajímá. Složitost dotazu by měla být podobná těm dvěma nahoře.