RAPでSPARQLクエリを投げるとき,FILTERで使えるデータ型に制限がある?

今日は「RAP(RDF API for PHP)」を使って,オントロジーにSPARQLクエリを投下中.
ただクエリのFILTER句でうまく動作せず困ったので,例を挙げながらまとめておく.

  • -

動作確認に用いるオントロジー例とその可視化例を示す.
簡単に言えば,BobとTomの誕生日情報を,xsd:date型とxsd:dateTime型で定義した感じ.


<?xml version="1.0"?>
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
    xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
    xmlns:owl="http://www.w3.org/2002/07/owl#"
    xmlns="http://www.owl-ontologies.com/Ontology1000000000.owl#"
    xml:base="http://www.owl-ontologies.com/Ontology1000000000.owl">
  <owl:Class rdf:ID="People"/>
  <owl:DatatypeProperty rdf:ID="birthdayIs_date">
    <rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#date"/>
    <rdfs:domain rdf:resource="#People"/>
  </owl:DatatypeProperty>
  <owl:DatatypeProperty rdf:ID="birthdayIs_dateTime">
    <rdfs:range rdf:resource="http://www.w3.org/2001/XMLSchema#dateTime"/>
    <rdfs:domain rdf:resource="#People"/>
  </owl:DatatypeProperty>
  <People rdf:ID="Bob">
    <birthdayIs_dateTime rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2002-10-01T16:30:30</birthdayIs_dateTime>
    <birthdayIs_date rdf:datatype="http://www.w3.org/2001/XMLSchema#date"
    >2002-10-01</birthdayIs_date>
  </People>
  <People rdf:ID="Tom">
    <birthdayIs_dateTime rdf:datatype="http://www.w3.org/2001/XMLSchema#dateTime"
    >2008-10-01T16:30:30</birthdayIs_dateTime>
    <birthdayIs_date rdf:datatype="http://www.w3.org/2001/XMLSchema#date"
    >2008-10-01</birthdayIs_date>
  </People>
</rdf:RDF>
  • -

まず「Protege」を用いて上のオントロジーにクエリを投げてみる.

クエリ_1 : 全員の誕生日(xsd:date型/xsd:dateTime型)を推論する

PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
SELECT ?name ?b_dT ?b_d
WHERE { 
    ?name rdf:type ex:People .
    ?name ex:birthdayIs_dateTime ?b_dT .
    ?name ex:birthdayIs_date ?b_d
}
?name ?b_dT ?b_d
Bob   2002-10-01T16:30:30   2002-10-01
Tom   2008-10-01T16:30:30   2008-10-01

I made it!!!

クエリ_2 : FILTERを使って,誕生日(xsd:dateTime型)が「2007-01-01T12:00:00」以降の人を推論する

PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?name ?b_dT ?b_d
WHERE { 
    ?name rdf:type ex:People .
    ?name ex:birthdayIs_dateTime ?b_dT .
    ?name ex:birthdayIs_date ?b_d
    FILTER(xsd:dateTime(?b_dT) > "2007-01-01T12:00:00"^^xsd:dateTime)
}
※ FILTER(xsd:dateTime(?b_dT) > xsd:dateTime("2007-01-01T12:00:00")) でもOK.
?name ?b_dT ?b_d
Tom   2008-10-01T16:30:30   2008-10-01

I made it!!!

クエリ_3 : FILTERを使って,誕生日(xsd:date型)が「2007-01-01」以降の人を推論する

PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT ?name ?b_dT ?b_d
WHERE { 
    ?name rdf:type ex:People .
    ?name ex:birthdayIs_dateTime ?b_dT .
    ?name ex:birthdayIs_date ?b_d
    FILTER(xsd:date(?b_d) > "2007-01-01"^^xsd:date)
}
※ FILTER(xsd:date(?b_d) > xsd:date("2007-01-01")) でもOK.
?name ?b_dT ?b_d
Tom   2008-10-01T16:30:30   2008-10-01

I made it!!!
どのクエリも,しっかりとFILTERで限定された結果が返ってきている.

  • -

そこで,上記のクエリをRAPを用いてPHP上で投げてみることにする.
RAPの実装を超簡略化するとこんな感じになる.

<?php
	define("RDFAPI_INCLUDE_DIR", "./../rap/api/");
	include(RDFAPI_INCLUDE_DIR . "RDFAPI.php");
	
	// 読み込むオントロジーファイル
	$base = "";
	$model = ModelFactory::getDefaultModel();
	$model->load($base);
	
	// 投げるSPARQLクエリ
	$querystring = '';
	
	// クエリの投下
	$result = $model->sparqlQuery($querystring);
	
	// クエリ結果をHTMLのテーブルで表示
	echo $model->sparqlQuery($querystring,'HTML');
?>
  • -

クエリ_1(RAP利用) : 全員の誕生日(xsd:date型/xsd:dateTime型)を推論する

<?php	
	$querystring = '
		PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
		PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
		SELECT ?name ?b_dT ?b_d
		WHERE { 
		    ?name rdf:type ex:People .
		    ?name ex:birthdayIs_dateTime ?b_dT .
		    ?name ex:birthdayIs_date ?b_d
		}';


I made it!!!

クエリ_2(RAP利用) : FILTERを使って,誕生日(xsd:dateTime型)が「2007-01-01T12:00:00」以降の人を推論する

<?php	
	$querystring = '
		PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
		PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
		SELECT ?name ?b_dT ?b_d
		WHERE { 
		    ?name rdf:type ex:People .
		    ?name ex:birthdayIs_dateTime ?b_dT .
		    ?name ex:birthdayIs_date ?b_d
		    FILTER( ?b_dT > xsd:dateTime("2007-01-01T12:00:00"))
		}';


I made it!!!

ちなみにRAPで以下の形式だと動作しなかった.

FILTER(xsd:dateTime(?b_dT) > "2007-01-01T12:00:00"^^xsd:dateTime)

ただ,
rap/api/sparql/SparqlEngine.php[943-951行目(改行は省略)]
を見ると

<?php

// replace xsd:date expressions
$pattern = "/\"(.[^\"]*)\"\^\^".$xsd."dateTime/";
preg_match_all($pattern,$evalString,$hits);
foreach($hits[1] as $dummy)
$evalString = preg_replace("/\".[^\"]*\"\^\^".$xsd."dateTime/",strtotime($dummy),$evalString,1);
$evalString = preg_replace("/(\'\<".$xsd."dateTime\()(.[^\)]*\))\>\'/","dateTime($2",$evalString);

こういう記述があるので,「"2007-01-01T12:00:00"^^xsd:dateTime」のような形式でも使えるような気がするのだが.
ちなみに,コメントの「// replace xsd:date expressions」は「// replace xsd:dateTime expressions」の間違いかな?

クエリ_3(RAP利用) : FILTERを使って,誕生日(xsd:date型)が「2007-01-01」以降の人を推論する

<?php	
	$querystring = '
		PREFIX ex: <http://www.owl-ontologies.com/Ontology1000000000.owl#>
		PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
		SELECT ?name ?b_dT ?b_d
		WHERE { 
		    ?name rdf:type ex:People .
		    ?name ex:birthdayIs_dateTime ?b_dT .
		    ?name ex:birthdayIs_date ?b_d
		    FILTER( ?b_d > xsd:date("2007-01-01"))
		}';

No result rows.

Oh No!
RAPだと,FILTERでxsd:date型での比較が実装されてないのかも?


rap/api/sparql/SparqlEngine.php[883-898行目(改行は省略)]

<?php

if($res[$var] instanceof Literal){
	if($res[$var]->getDatatype()!= null){
		if($res[$var]->getDatatype() == XML_SCHEMA.'boolean')
		$replacement = $res[$var]->getLabel();
		if($res[$var]->getDatatype() == XML_SCHEMA.'double')
		$replacement = $res[$var]->getLabel();
		if($res[$var]->getDatatype() == XML_SCHEMA.'integer')
 		$replacement = $res[$var]->getLabel();
		if($res[$var]->getDatatype() == XML_SCHEMA.'dateTime')
		$replacement = strtotime($res[$var]->getLabel());
	}else{
		if($res[$var]->getLabel()=="")
		$replacement = 'false';
		else
		$replacement = "'str_".$res[$var]->getLabel()."'";
	}


こんなコードを発見したので,やっぱり「boolean」「double」「integer」「dateTime」だけの実装で,「date」は使えないのかも.
rap/test/unit/Sparql/filterCases.php
を見てもFILTERでdate型を使ったクエリ例が書かれていないし.

  • -

まとめ

RAPを使う場合,FILTERで指定するデータ型によっては,動作しない場合がある.
なので,その場合はアプリケーション側で対応する(面倒だけど).
出来るならRAPのソースを拡張するのがベストなんだけど,把握できるような量じゃないので,V0.9.6以降にアップデートされることを期待します.