SPARQLって結局何なの?実際にRDFデータに問い合わせしてみて勉強しよう!
0. この記事の目的
「SPARQL」に関して書いてある記事等には、こういうコードが、特に説明もなく登場したりしますよね。
PREFIX saas: <https://example.com/ontology/saas#>
SELECT ?workspace ?workspaceName
WHERE {
<https://example.com/data/tenant-abc>
saas:containsWorkspace ?workspace .
?workspace saas:name ?workspaceName .
}
PREFIX、SELECT、WHERE、?workspace。見た感じSQLに似ている気もしますが、確信が持てないまま読み進めるのは、なかなかつらいものです。
この記事は、その手前の部分を埋めるための記事です。SPARQLだけに絞って、それが何であり、どうやって実際に手元で動かすのかを、説明します!
※この記事は、RDF・RDFS・OWL・Turtleについて説明済みの前提で書きます。まだの方は、先にRDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門を読んでおいてください。
1. SPARQLとは何か
一言でいうと、SPARQLは次のようなものです。
SPARQL:RDFグラフに問い合わせるための検索言語
正式名称はSPARQL Protocol and RDF Query Languageです。読み方は「スパークル」です。
プログラミングやデータベースに触れたことがある方には、次のたとえが分かりやすいと思います。
SQL :テーブルに対して問い合わせる
SPARQL :RDFグラフに対して問い合わせる
RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門で説明した技術を、もう一度並べてみます。
RDF
情報をトリプルとして表す
RDFS・OWL
データが何を意味するかを定義する
SHACL
データが期待する形になっているかを検証する
SPARQL
RDFグラフから、必要な情報を取り出す
RDFで情報を表し、RDFS・OWLで意味を定義し、SHACLで形を検証し、SPARQLで情報を取り出す。この記事は、最後のSPARQLの部分を担当します。
2. SPARQLも、Turtleに似た書き方をする
SPARQLのクエリは、Turtleのトリプルパターンとよく似た見た目をしています。これは偶然ではなく、意図的にそう設計されています。
ex:yamada ex:worksFor ex:abc .
これは、Turtleで書いた、確定した事実です。
?employee ex:worksFor ex:abc .
これは、SPARQLで書いた、問い合わせの条件です。
違いは、主語の部分がex:yamadaという具体的な値ではなく、?employeeという変数になっている点だけです。
実際のデータ
ex:yamada ex:worksFor ex:abc .
問い合わせの条件
?employee ex:worksFor ex:abc .
?で始まる名前は変数です。「ここに当てはまる値を教えてほしい」という、いわば穴あき問題です。
3. 最初のクエリを書く
SPARQLのSELECTクエリは、次の3つの部分でできています。
PREFIX ex: <https://example.com/company#>
SELECT ?employee
WHERE {
?employee a ex:Employee .
}
順番に見ていきます。
PREFIX ex: <https://example.com/company#>
これは、Turtleの@prefixと同じ役割です。ex:を、このIRIの省略形として使う、という宣言です。書き方が少し違うだけで(@が無い、末尾に.が無い)、意味はまったく同じです。
SELECT ?employee
「?employeeという変数の値を、結果として返してください」という指定です。
WHERE {
?employee a ex:Employee .
}
「?employeeが、ex:Employeeという型を持つ、というパターンに一致するものを探してください」という条件です。
aは、rdf:typeの省略記法です。これもTurtleと同じ書き方です。
このクエリは、日本語にすると次のような問い合わせです。
Employeeという型を持つものを、すべて教えてください
4. データを用意する
具体的な例で確認するために、次のデータを用意します。RDF・RDFS・OWL・Turtleの違いを整理して、ファイルで理解するオントロジー入門の山田・鈴木・ABC社に、もう1人、田中を加えます。
# data.ttl
@prefix ex: <https://example.com/company#> .
ex:yamada a ex:Employee ;
ex:employeeId "E001" ;
ex:worksFor ex:abc ;
ex:manages ex:suzuki .
ex:suzuki a ex:Employee ;
ex:employeeId "E002" ;
ex:worksFor ex:abc .
ex:tanaka a ex:Employee ;
ex:employeeId "E003" ;
ex:worksFor ex:abc .
ex:abc a ex:Organization .
山田はABC社に勤務し、鈴木を管理しています。鈴木と田中も、同じくABC社に勤務しています。
5. 具体例でクエリを書く
5.1 山田の勤務先を取得する
PREFIX ex: <https://example.com/company#>
SELECT ?org
WHERE {
ex:yamada ex:worksFor ?org .
}
主語(ex:yamada)と述語(ex:worksFor)を固定し、目的語だけを変数にしています。結果は、ex:abcです。
5.2 ABC社に勤務する全員を取得する
今度は逆に、主語を変数にします。
PREFIX ex: <https://example.com/company#>
SELECT ?employee
WHERE {
?employee ex:worksFor ex:abc .
}
このクエリは、「ex:worksFor ex:abcという関係の、主語側に来るものをすべて教えてください」という意味です。データを逆方向にたどっていることになります。
RDFのトリプルには、あらかじめ決まった「たどってよい方向」はありません。主語を固定して目的語を探すこともできますし、目的語を固定して主語を探すこともできます。
5.3 複数の条件をつなげる
山田が管理している人の、社員番号を取得したいとします。1つのトリプルパターンだけでは表せません。
PREFIX ex: <https://example.com/company#>
SELECT ?subordinateId
WHERE {
ex:yamada ex:manages ?subordinate .
?subordinate ex:employeeId ?subordinateId .
}
WHEREの中に、2つのトリプルパターンが並んでいます。
1つ目:ex:yamada ex:manages ?subordinate .
2つ目:?subordinate ex:employeeId ?subordinateId .
1つ目のパターンで?subordinateに当てはまった値が、2つ目のパターンの主語としてそのまま使われます。同じ変数名を使うことで、2つのパターンが連動します。
SQLに慣れている方には、これがJOINに相当する、と考えると分かりやすいと思います。1つ目のパターンで?subordinateがex:suzukiに決まり、2つ目のパターンによってex:suzukiのemployeeIdである"E002"が引き出されます。
6. 実際に問い合わせを実行してみる
ここまでは、クエリの書き方の説明でした。ここからは、実際にこのクエリを動かしてみます。
6.1 準備
pip install rdflib 'pyparsing>=3.1'
rdflibは、RDFをPythonで扱うための、最も基本的なライブラリです。これをインストールすると、sparqlqueryというコマンドラインツールも一緒に使えるようになります。
6.2 データとクエリをファイルに保存する
4.のデータをdata.ttlに、5.1のクエリをquery.rqに保存します。
# query.rq
PREFIX ex: <https://example.com/company#>
SELECT ?org
WHERE {
ex:yamada ex:worksFor ?org .
}
6.3 実行する
同じフォルダにdata.ttlとquery.rqを置いた状態で、次のコマンドを実行します。
sparqlquery data.ttl -qf query.rq -f csv
-qfのあとにクエリファイル、-f csvで結果をCSV形式(見やすい表形式)で出力するよう指定しています。
結果は、次のように表示されます。
org
https://example.com/company#abc
1行目が列名(SELECTで指定した変数名)、2行目以降が実際の値です。この出力は、実際にこの記事のとおりに実行して得られたものです。
6.4 もう1つ試してみる
query.rqの内容を、5.2のクエリに書き換えて、もう一度実行してみます。
# query.rq
PREFIX ex: <https://example.com/company#>
SELECT ?employee
WHERE {
?employee ex:worksFor ex:abc .
}
ORDER BY ?employee
sparqlquery data.ttl -qf query.rq -f csv
employee
https://example.com/company#suzuki
https://example.com/company#tanaka
https://example.com/company#yamada
鈴木・田中・山田の3人が、ABC社に勤務している全員として返ってきました。
7. DISTINCTとORDER BY
SPARQLのクエリには、DISTINCTやORDER BYもよく登場します。どちらも、SQLと同じ意味です。
ORDER BY ?変数
結果を、その変数の値で並べ替える
DISTINCT
同じ組み合わせの結果が複数出たとき、重複を1つにまとめる
ORDER BYは、先ほどの6.4のクエリでも使いました。?employeeのIRI文字列の順番(アルファベット順)で並んでいたのは、ORDER BY ?employeeを指定していたためです。
DISTINCTは、次のように使います。
PREFIX ex: <https://example.com/company#>
SELECT DISTINCT ?org
WHERE {
?employee ex:worksFor ?org .
}
SELECTのすぐ後ろにDISTINCTを書きます。3人の社員が同じex:abcに勤務しているため、DISTINCTが無ければex:abcが3回、あれば1回だけ返ります。手元で両方を実行して、違いを確認してみてください。
8. もう少し複雑な問い合わせについて
ここまでで説明したのは、SELECT・WHERE・トリプルパターンの組み合わせ・ORDER BY・DISTINCTという、SPARQLの中でも基本的な部分です。
さらに発展的なクエリには、次のような機能も登場します。
OPTIONAL
無くてもエラーにせず、あれば結果に含める
FILTER
値を比較して、条件に合わないものを除外する
たとえば、OPTIONALは次のように使います。
SELECT ?employee ?org
WHERE {
?employee a ex:Employee .
OPTIONAL { ?employee ex:worksFor ?org . }
}
これは、「Employeeはすべて返すが、worksForが書かれていない場合は、?orgを空(未束縛)のままにする」という意味です。OPTIONALを使わずに?employee ex:worksFor ?org .を普通に書くと、worksForが無いEmployeeはそもそも結果に出てきません。
FILTERは、次のように使います。
SELECT ?employee ?id
WHERE {
?employee ex:employeeId ?id .
FILTER (?id != "E001")
}
これは、「社員番号が"E001"ではない人だけを返す」という意味です。
この記事では、OPTIONALとFILTERの説明までは踏み込みません。ここで大事なのは、次の1点だけです。
基本のSELECT/WHEREだけでは、
「無い場合の扱い」や「値による絞り込み」を
うまく表現できない場面がある。
そのために OPTIONAL と FILTER がある。
これだけ知っておけば、OPTIONALやFILTERが出てきても、「ああ、無い場合の扱いや絞り込みをしているんだな」と読めるはずです。
まとめ
最後に、この記事の内容を整理します。
SPARQLとは
RDFグラフに問い合わせるための検索言語
PREFIX
TurtleのPREFIXと同じ、IRIの省略形の宣言
SELECT ?変数
結果として返す変数を指定する
WHERE { トリプルパターン }
?変数を含むトリプルパターンで、条件を書く
同じ変数名でつなげる
複数のトリプルパターンを連動させる(SQLのJOINに近い)
ORDER BY / DISTINCT
結果の並べ替え / 重複の除去
OPTIONAL / FILTER
より複雑な問い合わせのための機能(この記事では扱わない)
実際に問い合わせを動かす手順は、次の3ステップでした。
1. pip install rdflib 'pyparsing>=3.1'
2. データを data.ttl に、クエリを query.rq に保存する
3. sparqlquery data.ttl -qf query.rq -f csv を実行する
ここまで読めば、突然出てくるSPARQLの例も、初見のコードではなくなっているはずです。ぜひ、ハマってしまった記事に戻って読んでみてください!なんとなくわかるようになってる気がします!
こちらからは以上です。