検索したおしたディ!
QiitaのSPARQL界隈でははじめまして、kochizufanと申します。
SPARQLは5年以上前に東京で行われたLDイベントで少し触った経験がありますが、その後は今年11月の大阪「検索したおしたんデイ」までほぼ無経験です。
今回は同日に作ってみたクエリを書いてみます。
クエリの内容は、タイトルの通りです。
旧日本海軍艦船の命名は地名からのものばかりではないですが、地名を由来とする(とおぼしき)艦名と、その元となった(とおぼしきもの)地名をDBpediaからリストアップするクエリです1。
以下の感じのクエリを作りました。
PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>
PREFIX dcterms: <http://purl.org/dc/terms/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX dbpedia-ja: <http://ja.dbpedia.org/resource/>
PREFIX prop-ja: <http://ja.dbpedia.org/property/>
PREFIX category-ja: <http://ja.dbpedia.org/resource/Category:>
select distinct ?basho ?kan where {
{?kan dcterms:subject category-ja:日本海軍の艦船}
union
{?kan dcterms:subject category-ja:日本の戦艦}
union
{?kan dcterms:subject category-ja:日本の航空母艦}
union
{?kan dcterms:subject category-ja:日本の巡洋艦}
union
{?kan dcterms:subject category-ja:日本の潜水母艦}
union
{?kan dcterms:subject category-ja:日本の砲艦}
union
{?kan dcterms:subject category-ja:日本の水上機母艦}
?kan rdfs:label ?kanmei_orig;
BIND(if(strbefore(?kanmei_orig," ") = "", ?kanmei_orig, strbefore(?kanmei_orig," ")) AS ?kanmei).
?basho rdf:type dbpedia-owl:Place;
rdfs:label ?chimei_orig;
BIND(if(strbefore(?chimei_orig," ") = "", ?chimei_orig, strbefore(?chimei_orig," ")) AS ?chimei)
FILTER(substr(?chimei, 1, if(strlen(?kanmei) > strlen(?chimei), strlen(?chimei), strlen(?kanmei))) = ?kanmei && strlen(?chimei) - strlen(?kanmei) < 2).
}
短い時間で作ったクエリなので無駄もありますが、とりあえずそのまま引用しました。
簡単に説明すると、
- 「日本海軍の艦船」カテゴリに属する艦の項目リストを作成。
が、それだけだとサブカテゴリに含まれる艦がリストアップされないのが判明したので、サブカテゴリである「日本の戦艦」「日本の航空母艦」etc.も追加。 - Wikipediaの項目名は、名称が重複する項目については、判別用のカテゴリ名が付与されているものが多数あります。
この項目名の名付け方規則は、「名称:半角スペース:半角括弧:判別用カテゴリ名:半角括弧閉じ」となるルールなので、項目名中に半角スペースを含むものについては、そこより前だけを取り出して艦名として定義。 -
rdfs:typeの値がdbpedia-owl:Placeとなる項目リストを、地名リストとして取得。
こちらも2.と同様に、半角スペース前だけを取得して、名称を正規化。 - こうして得た地名と艦名のリストの間で、艦名と同じ文字列から始まり、かつ艦名より2文字まで長いものだけをピックアップして、リスト化しました。
長さでフィルタした理由は、元になった地名は大和国とか富士山とか加古川とか、艦名+山、川などの短い文字列で、大和高田市とか富士小学校(なんてのがあるのかは知りませんが)なんてものではないと推察されるので、あまり艦名との長さの差が著しいものは削除するためのものです。
こういう方針で作ったクエリを投げてやると、こんな感じの結果が返ってきました。
先頭いくつかだけピックアップすると、
正直、微妙ですが...でも正解が出るかどうかは別として、こんな面倒な条件の検索でもSPARQLで実現できる、という点を面白いと思いました。
使ってみて感じたこと
少なくともこのユースケースを使ってみて、こういう点が改善されるといいなと思ったことを挙げてみると、
- DBpediaはWikipediaの項目名をrdfs:labelの値としていますが、Wikipediaの項目名はWikipedia全体で一意であることを保証するため、重複する名称の項目については括弧書きで判別のための情報が加えられており2、情報の正確な実名称を得る意味では役に立ちません。
今回のクエリで行ったように、半角スペース+括弧より後ろを削れば正確な情報名になりますし、カテゴリによっては実際その名称をなんらかのプロパティで持っている項目もありますが、ないものもたくさんありますし全体で統一されてないので、「半角スペース+括弧より後ろを削った項目名」を、rdfs:label以外の何らかのプロパティに含んでおけば、(クエリで削る必要がなくなるので)高速に検索できるようになってよいのではないかと感じました。 - また、Wikipediaでは、複数の呼び名を持つ項目については、各項目で項目を立ててリダイレクトで繋いでいます3。
DBpediaでもこの構成をそのままデータ項目化してますので4、今回のように複雑な条件でクエリした場合、名称のうち1つはクエリ条件に含めることができますが、他のいくつもの呼び名についてはクエリ条件に含める事ができない or クエリ条件が無駄に複雑になります。
これについても、1.で書いたような「半角スペース+括弧より後ろを削った項目名」を格納するプロパティを加えるならば、ついでにそれを配列化して、リダイレクト元の項目名を列挙してやれば、別名を使ったクエリも簡潔に、かつ高速に検索できるようになるので、よいかなと思いました。
この辺、DBpediaの目的が、学術的に論理的に正しいものを作ればOKの試験サービスなのか、実用に供される事を目的としているのかがわかりませんし、また私は研究者ではなくサービス提供者のためどうしてもサービスよりの見方をしてしまうので、偏った視点からの提案かもしれませんが...。
サービス的には、今回の私が作ったクエリは実行してみると結果が出るまでに5分〜10分近くかかるので、クエリももっと最適化できるとは思いますが、データ的にも構成を工夫しないと、サービス提供に絶える感じがしません。
SQLデータベースの世界では、昔よく言われたテーブル構成の論理的正規化等はサービス実務の場ではほぼ顧みられなくなっており、サービス提供高速化のためならばキャッシュとして重複データを持つテーブルも普通に作られますし、重複データを持つ事による不整合の回避はシステム的に保証する考え方が主流になってると思います(少なくとも私の周囲では)。
SPARQLデータベースも、実用に耐えるものを提供するならば、考えうるユースケースに対してはデータ構造の方を工夫して、正規化?を崩すような形でもあえて加工する事で、高速化を図るような事はあっていいのかな、という感想を持ちました。