メリークリスマス🎅🎂🎄🎉
Ateam Finergy Inc. Advent Calendar 2020の最終日、12/25を担当します@Adacchi3です。
クリスマスツリー🎄のてっぺんを飾っているのはスター⭐ですよね!
そんな星⭐にちなんで、RDF*/SPARQLについて紹介していきます。RDFをRDF-starと表記することがありますからね。
はじめに
皆さんはセマンティックWeb(Semantic Web)をご存知でしょうか?
SEO対策をしていると、JSON-LDやmicrodata, RDFaといった「構造化データ」を目にする機会があるでしょう。
構造化データは、ページ概要、著者、公開日といったWebページ(URL)を説明するデータ、いわゆるメタデータをコンピュータが理解しやすい(機械可読性が高い)表現で構成されています。
これは意味あるデータのWebの実現、セマンティックWebの試みの1つだったりします。
セマンティックWebは、World Wide Webの考案者Tim Berners-Leeが提唱する、Web上にデータをただ置くのではなく、データ同士を結ぶことで、人やコンピュータがそのデータを探索できるようにしようというデータのWebの試みです1。
今回はセマンティックWebを実現するための技術の1つであるデータ記述言語のRDFと、そのクエリ言語であるSPARQLを概観して、RDF*/SPARQL*について紹介していきます。
RDF(Resource Description Framework)
まずは実際にRDFデータの一例を眺めてみましょう。
<https://en.wikipedia.org/wiki/The_Idolmaster> a schema:Article ;
schema:about wd:Q20739875 ;
schema:inLanguage "en" ;
schema:isPartOf <https://en.wikipedia.org/> ;
schema:name "The Idolmaster"@en .
こちらはwikidataにあるアイドルマスターシリーズのページからRDF/Turtle形式で抜粋してきたものになります。https://www.wikidata.org/wiki/Special:EntityData/Q20739875.ttl
RDFは主語、述語、目的語の3つの要素で構成されており、それをトリプルと呼んでいます。それぞれの要素はURLで表現されており、目的語はしばしばリテラル(string, integer, dateTime等)で表現されます。
例えば<https://en.wikipedia.org/wiki/The_Idolmaster> a schema:Article .
の読み方を説明します。
主語は<https://en.wikipedia.org/wiki/The_Idolmaster>
で、述語はa
、目的語はschema:Article
になります。
a
はRDFでの記述でrdf:type
の省略語彙でis-aを表現してます。
schema:Article
のschema:
はschema.orgのnamespaceになっており、省略せずに記述するとhttp://schema.org/Article
になります。
これを読み解くと、主語のURLはschama.orgでいうところの記事に属する、という意味になります。
末尾を;
で記述すると、次のトリプルの主語を省略して記述することができるため、例として挙げているトリプルの主語はすべて<https://en.wikipedia.org/wiki/The_Idolmaster>
になります。
RDFはこのようなトリプルがいくつも組み合わせてグラフを表現し、データ同士が結びつくことによって、データのWebができるわけです。
こうしたデータとデータがリンクして、公開されているデータ(オープンデータ)をLOD(Linked Open Data)と呼びます。
2020年11月時点では世界で1,269個のデータセットがLinked Open Dataとして公開されています2。
SPARQL(SPARQL Protocol and RDF Query Language)
RDFで記述されたデータから特定の情報を取得したいとき、クエリ言語のSPARQLを使います。こちらも一例から確認していきましょう。
PREFIX schema: <http://schema.org/>
PREFIX imas: <https://sparql.crssnky.xyz/imasrdf/URIs/imas-schema.ttl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT DISTINCT ?name
WHERE {
?person a imas:Idol ;
schema:name ?name ;
imas:Title "283Pro"@en ;
schema:birthDate "--12-25"^^xsd:gMonthDay .
FILTER ( lang(?name) = "ja" )
} limit 10
こちらはimas@sparqlで実際に問い合わせすることができるSPARQLクエリになります。
SPARQLでは、トリプルを構成する3つの要素をそれぞれ変数を割り当てることができ、その3つ組をトリプルパターンと呼びます。
一例では、?person
や?name
が変数に当たります。
SQLライクにクエリを書くことができるので、実際に触って覚えるのが良いと思います。
今回のクエリの場合は、283Proというタイトルに登場し、誕生日が12月25日で、アイドルであるキャラの名前を日本語で10件取得するというものになります。クリスマスが誕生日のアイドルはいるのでしょうか。
実際にimas@sparqlのエンドポイントで問い合わせした結果がこちらになります。
{
"head": {
"vars": [ "name" ]
} ,
"results": {
"bindings": [
{
"name": { "type": "literal" , "xml:lang": "ja" , "value": "大崎甘奈" }
} ,
{
"name": { "type": "literal" , "xml:lang": "ja" , "value": "大崎甜花" }
}
]
}
}
大崎甘奈と大崎甜花がヒットしました。誕生日おめでとうございます🎉🎂🎉
このようにRDFデータが公開され、誰もがSPARQLで必要な情報を取得できるようになると、同じ指標を見ながら議論できるようになり、データの透明性や根拠の信頼性の向上が期待できます。
RDF*/SPARQL*
RDF*/SPARQL*(RDF-star/SPARQL-star)は、statement(状態)に対してメタデータの付与を省略して表現する方法を提案しています。
2017年6月にWorkshopで提案され3、2020年12月18日時点でW3C Community Groupにて草案が作成されています4。
では、実際にどのような状態を表現することが容易になったのかを見ていきましょう。
一例として下記のデータがあるとします。こちらはAliceとBobが人であり、AliceはBobを知っていることを表現しています。
ex:Alice a schema:Person .
ex:Bob a schema:Person .
ex:Alice schema:knows ex:Bob .
では、「Aliceが人である根拠」や「AliceがBobを知っている根拠」をデータとして示したい場合はどのように表現すればよいでしょうか。
従来のやり方で「Aliceが人である根拠」を示しているURLを追加したデータは、以下のようになります。
_:s rdf:type rdf:Statement ;
rdf:subject ex:Alice ;
rdf:predicate rdf:type ;
rdf:Object schema:Person ;
ex:source ex:Alice_Profile .
従来のStatementの表現は_:s
は空白ノードであり、その空白ノードに対して、主語や述語、目的語、それ以外のメタ情報を付与していきます。
ここで、RDF*の表現を用いると、以下のようになります。
<<ex:Alice a schema:Person>> ex:source <http://example.com/Alice_Profile> .
すっきりしましたね。
RDF*では、1つのトリプルを主語または目的語の要素として表現できます。
トリプルをネストして表現することにより、より直感的にトリプルに対してメタデータを付与することができそうです。トリプルの数も少なくなるのも利点です。
また、SPARQL*はRDF*を検索できるようにしたクエリ言語になります。RDF*と同様にトリプルパターンの主語(または目的語)にトリプルパターンを用いることができます。
例えば、人物を示しているURLとその根拠を調べるクエリが以下のようになります。
PREFIX schema: <http://schema.org/>
PREFIX ex: <http://example.com/>
SELECT DISTINCT ?person ?source
WHERE {
<<?person a schema:Person>> ex:source ?source .
} LIMIT 10
こちらも従来の表現を用いたRDFデータからクエリを作成するより、より直感的に記述できるように見受けられます。
まだW3C Community Groupにて草案段階ではありますが、ゆくゆくはW3C勧告されるのではないでしょうか。
実際にRDF*/SPARQL*を触ってみる
今回はProvisional supportとしてRDF*/SPARQL*を実装しているRDF.rbとSPARQL.rbを触っていきます。
サンプルプログラムを実行するために使用したRubyのバージョンは以下のとおりです。
$ ruby -v
ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-darwin18]
はじめに、RDF.rbのライブラリと語彙辞書にあたるrdf-vocabをインストールします。
$ gem install rdf rdf-vocab
$ gem list | grep rdf
rdf (3.1.7)
rdf-aggregate-repo (3.1.0)
rdf-vocab (3.1.9)
rdf-xsd (3.1.0)
続いて、RDF.rbに記載されているRDF*のサンプルコードを実行していきます。
$ irb
irb(main):001:0> require 'rdf'
=> true
irb(main):002:0> require 'rdf/vocab'
=> true
irb(main):003:0> statement = RDF::Statement(RDF::URI('bob'), RDF::Vocab::FOAF.age, RDF::Literal(23))
=> #<RDF::Statement:0x6cc(<bob> <http://xmlns.com/foaf/0.1/age> "23"^^<http://www.w3.org/2001/XMLSchema#integer> .)>
irb(main):004:0> graph = RDF::Graph.new << [statement, RDF::URI("ex:certainty"), RDF::Literal(0.9)]
=> #<RDF::Graph:0x974(default)>
irb(main):005:0> graph.dump(:ntriples, validate: false)
=> "<<<bob> <http://xmlns.com/foaf/0.1/age> \"23\"^^<http://www.w3.org/2001/XMLSchema#integer>>> <ex:certainty> \"0.9\"^^<http://www.w3.org/2001/XMLSchema#double> .\n"
実際にRDF*データをturtle形式で出力されることを確認することができました。
続いて、SPARQL*を実行してみます。SPARQL.rbをインストールしていきます。
$ gem install sparql
$ gem list | grep sparql
sparql (3.1.3)
sparql-client (3.1.0)
RDF*データを作成した後、SPARQL*クエリを作成、実行可能な形式(SPARQL Algebra)にパースし、クエリを実行してみます。
$ irb
irb(main):001:0> require 'sparql'
=> true
irb(main):002:1* data = RDF::Graph.new do |g|
irb(main):003:2" g << RDF::NTriples::Reader.new(%(
irb(main):004:2" <http://bigdata.com/bob> <http://xmlns.com/foaf/0.1/name> "Bob" .
irb(main):005:2" <<<http://bigdata.com/bob> <http://xmlns.com/foaf/0.1/age> "23"^^<http://www.w3.org/2001/XMLSchema#integer>>> <http://example.org/certainty> "0.9"
^^<http://www.w3.org/2001/XMLSchema#decimal> .
irb(main):006:1* ), rdfstar: :PG)
irb(main):007:0> end
=> #<RDF::Graph:0x104(default)>
irb(main):008:0> query_str = "PREFIX : <http://bigdata.com/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX ex: <http://example.org/> SELECT ?age ?c WHERE {\n
?bob foaf:name \"Bob\" . <<?bob foaf:age ?age>> ex:certainty ?c . }"
=> "PREFIX : <http://bigdata.com/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> PREFIX ex: <http://example.org/> SELECT ?age ?c WHERE {\n ?bob foaf:name \"Bo...
irb(main):009:0> query = SPARQL.parse(query_str)
=> #<SPARQL::Algebra::Operator::Prefix:0x80c(( ( (: <http://bigdata.com/>) (foaf: <http://xmlns.com/foaf/0.1/>) (ex: <http://example.org/>)) (project (?age ?c) (bgp...
irb(main):010:0> data.query(query).to_json
=> "{\"head\":{\"vars\":[\"age\",\"c\"]},\"results\":{\"bindings\":[{\"age\":{\"type\":\"typed-literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#integer\",\"value\":\"23\"},\"c\":{\"type\":\"typed-literal\",\"datatype\":\"http://www.w3.org/2001/XMLSchema#decimal\",\"value\":\"0.9\"}}]}}"
RDF*データに対してSPARQL*クエリを実行し、結果を取得できました。
今回はサンプルプログラムを動かしたのみとはなりますが、取り扱うデータの内容やデータ活用を検討することで、いろいろと楽しいことができそうな気がしますね。
おわりに
データ記述言語のRDFとそのクエリ言語のSPARQL、新たに提案されているRDF*/SPARQL*を紹介し、先行実装されているライブラリにてサンプルコードを実行してみました。
今回はRDF*/SPARQL*のほんの触りだけを紹介しています。RDF*/SPARQL*の数学的定義や処理プロセスが気にある方は、ぜひ論文や実装を確認してみてください。
今後もセマンティックWeb技術の発展を追っていけたらなと思います。
これにて、Ateam Finergy Inc. Advent Calendar 2020、無事完走です!
ご愛読していただき、ありがとうございました!
良い年末年始をお過ごしください。
参考
- https://www.w3.org/RDF/
- https://www.w3.org/TR/sparql11-protocol/
- https://schema.org/
- https://w3c.github.io/rdf-star/
- https://youtu.be/xWgWMxx_qkA