3
# query.pl - Redland RDF Query demo
5
# $Id: query.pl 10586 2006-03-05 07:49:32Z cmdjb $
7
# Copyright (C) 2004-2006, David Beckett http://purl.org/net/dajobe/
8
# Copyright (C) 2004-2005, University of Bristol, UK http://www.bristol.ac.uk/
10
# This package is Free Software and part of Redland http://librdf.org/
12
# It is licensed under the following three licenses as alternatives:
13
# 1. GNU Lesser General Public License (LGPL) V2.1 or any newer version
14
# 2. GNU General Public License (GPL) V2 or any newer version
15
# 3. Apache License, V2.0 or any newer version
17
# You may not use this file except in compliance with at least one of
18
# the above three licenses.
20
# See LICENSE.html or LICENSE.txt at the top of this package for the
21
# complete terms and further detail along with the license texts for
22
# the licenses in COPYING.LIB, COPYING and LICENSE-2.0.txt respectively.
27
# CHANGE THIS FOR YOUR CONFIGURATION
28
$::ROOT_DIR='/somewhere';
32
# Helps with broken web requests (missing headers)
33
$ENV{'Content-Length'}||=0;
35
# Tainting, dontcha know
36
$ENV{'PATH'}="/bin:/usr/bin:/usr/local/bin";
38
# Standard perl modules
45
my(@query_languages)=qw(rdql sparql),
46
my(%query_language_labels)=('rdql' =>'RDQL',
48
my $default_query_language='sparql';
50
my $example_foaf_uri='http://purl.org/net/dajobe/webwho.xrdf';
51
my $example_rss_uri='http://www.w3.org/2000/08/w3c-synd/home.rss';
58
(?x rdf:type foaf:Person)
60
using foaf for <http://xmlns.com/foaf/0.1/>
62
, D => $example_foaf_uri
63
, T => 'A FOAF query that finds the names of all the people in the graph'
66
select ?nick, ?name where
67
(?x rdf:type foaf:Person) (?x foaf:nick ?nick) (?x foaf:name ?name)
68
using foaf for <http://xmlns.com/foaf/0.1/>
70
, D => $example_foaf_uri
71
, T => 'A FOAF query that finds all people with a name and an IRC nick'
74
select ?name, ?archives
76
(?list rdf:type doaml:MailingList)
77
(?list doaml:name ?name)
78
(?list doaml:archives ?archives)
80
using doaml for <http://ns.balbinus.net/doaml#>
82
, D => 'http://www.doaml.net/doaml/w3ml/Lists.rdf'
83
, T => 'Print the name and archive URIs of W3C mailing lists about P3P as described by <a href="http://www.doaml.net/">DOAML</a>'
89
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
90
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
93
?x rdf:type foaf:Person .
98
, D => $example_foaf_uri
99
, T => 'A FOAF query that finds the names of all the people in the graph'
103
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
104
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
106
WHERE { ?x rdf:type foaf:Person . ?x foaf:nick ?nick . ?x foaf:name ?name }
108
, D => $example_foaf_uri
109
, T => 'A FOAF query that finds all people with a name and an IRC nick'
113
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
114
PREFIX dc: <http://purl.org/dc/elements/1.1/>
115
PREFIX rss: <http://purl.org/rss/1.0/>
116
SELECT ?title ?description
117
WHERE { ?item rdf:type rss:item .
118
?item rss:title ?title .
119
?item rss:description ?description }
121
, D => $example_rss_uri
122
, T => 'An RSS 1.0 query that finds all the items in the feed'
125
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
126
PREFIX iemsr: <http://www.ukoln.ac.uk/projects/iemsr/terms/>
127
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
129
SELECT $number $name $description
131
$r rdf:type iemsr:RootDataElement .
132
$n iemsr:isChildOf $r .
133
$n iemsr:refNumber $number .
134
$n rdfs:label $name .
135
$n rdfs:comment $description
138
, D => 'http://www.ukoln.ac.uk/projects/iemsr/terms/LOM/'
139
, T => 'Find all LOM root elements in the LOM encoded for the <a href="http://www.ukoln.ac.uk/projects/iemsr/">JISC IE Schema Registry</a> (my current project)'
142
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
143
PREFIX doap: <http://usefulinc.com/ns/doap#>
144
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
146
SELECT $description $maintainerName
148
$project rdf:type doap:Project .
149
$project doap:description $description .
150
$project doap:maintainer $m .
151
$m foaf:name $maintainerName
154
, D => 'http://svn.usefulinc.com/svn/repos/trunk/doap/examples/gnome-bluetooth-doap.rdf'
155
, T => 'Print the description of a project and maintainer(s) using <a href="http://usefulinc.com/doap">DOAP</a>'
158
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
159
PREFIX doaml: <http://ns.balbinus.net/doaml#>
161
SELECT ?name ?archives
163
?list rdf:type doaml:MailingList .
164
?list doaml:name ?name .
165
?list doaml:archives ?archives .
166
FILTER REGEX(?name, "p3p")
169
, D => 'http://www.doaml.net/doaml/w3ml/Lists.rdf'
170
, T => 'Print the name and archive URIs of W3C mailing lists about P3P as described by <a href="http://www.doaml.net/">DOAML</a>'
172
{ Q => <<'optional-example1'
173
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
174
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
177
?x rdf:type foaf:Person .
179
OPTIONAL { ?x foaf:nick ?nick }
182
, D => $example_foaf_uri,
183
, T => 'Print the names and optional nicks of people in my FOAF file where available'
186
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
187
PREFIX dc: <http://purl.org/dc/elements/1.1/>
188
PREFIX rss: <http://purl.org/rss/1.0/>
189
PREFIX enc: <http://purl.oclc.org/net/rss_2.0/enc#>
190
SELECT ?title ?enc ?len
192
?item rdf:type rss:item .
193
?item rss:title ?title .
194
?enclosure rdf:type enc:Enclosure .
195
?item enc:enclosure ?enclosure .
196
?enclosure enc:url ?enc .
197
?enclosure enc:type ?type .
198
?enclosure enc:length ?len .
199
FILTER regex(?type, "audio/mpeg")
203
, D => 'http://B4mad.Net/datenbrei/feed/rdf',
204
, T => 'What podcasts have you got in your RSS feed? (you will need an RSS feed using the enclosures vocab) ',
207
PREFIX table: <http://www.daml.org/2003/01/periodictable/PeriodicTable#>
208
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
209
SELECT ?name ?symbol ?weight ?number
211
?element table:group ?group .
212
?group table:name "Noble gas"^^xsd:string .
213
?element table:name ?name .
214
?element table:symbol ?symbol .
215
?element table:atomicWeight ?weight .
216
?element table:atomicNumber ?number
220
, D => 'http://www.daml.org/2003/01/periodictable/PeriodicTable.owl',
221
, T => 'What are the Noble Gases?',
224
PREFIX collab: <http://www.w3.org/2000/10/swap/pim/collab@@#>
227
?issue collab:shortDesc ?desc;
228
collab:resolveRecord ?R
231
, D => 'http://www.w3.org/2000/06/webdata/xslt?xslfile=http%3A%2F%2Fwww.w3.org%2F2003%2F11%2Frdf-in-xhtml-processor&xmlfile=http%3A%2F%2Fwww.w3.org%2F2001%2Fsw%2FDataAccess%2Fissues',
232
, T => 'What are the RDF DAWG issues?',
235
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
236
PREFIX dc: <http://purl.org/dc/elements/1.1/>
237
PREFIX rss: <http://purl.org/rss/1.0/>
238
PREFIX atom: <http://www.w3.org/2005/Atom>
239
SELECT ?item ?title ?date
240
WHERE { ?item rdf:type rss:item .
241
?item rss:title ?title .
242
?item atom:updated ?date }
246
, D => 'http://www.tbray.org/ongoing/ongoing.atom',
247
, T => 'What are the last 10 updated items in an atom feed?',
250
PREFIX : <http://www.commonobjects.example.org/gmlrss>
251
PREFIX bio: <http://purl.org/vocab/bio/0.1/>
252
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
254
SELECT ?name ?birthDate ?deathDate
270
, D => 'http://www.w3.org/2003/01/geo/rdfgml/tests/mixing-eg1.xml',
271
, T => 'Who made a bridge in Bristol and what birth/death dates did they have?',
273
{ Q => <<'REDLAND-NEWS',
274
PREFIX dc: <http://purl.org/dc/elements/1.1/>
275
PREFIX rss: <http://purl.org/rss/1.0/>
276
SELECT ?item ?title ?date
285
D => 'http://librdf.org/NEWS.rdf http://librdf.org/raptor/NEWS.rdf http://librdf.org/rasqal/NEWS.rdf http://librdf.org/bindings/NEWS.rdf',
286
T => 'Find the most recent 10 news items about Redland from multiple RSS 1.0 feeds',
293
<p>Documentation on RDQL is available in the <a href="http://www.w3.org/Submission/2004/SUBM-RDQL-20040109/">specification</a> and the <a href="http://www.hpl.hp.com/semweb/doc/tutorial/RDQL/">Jena RDQL tutorial</a></p>
295
<p>See the <a href="http://librdf.org/rasqal/TODO.html#rdql">status of RDQL support in Rasqal</a></p>
299
<p>Documentation on SPARQL is available in the <a href="http://www.w3.org/TR/rdf-sparql-query/">SPARQL Query Language for RDF</a>, W3C Working Draft, 12 October 2004</p>
301
<p><b>NOTE</b> Not all of SPARQL is implemented. See the <a href="http://librdf.org/rasqal/TODO.html#sparql">status of SPARQL support in Rasqal</a>. Current unimplemented features include <code>UNION</code> and full XSD datatype comparisons (dates, decimal) and promotions, <code>GRAPH</code> and deeply grouped graph patterns <code>{</code>...<code>}</code>.</p>
307
my $log_file="$::ROOT_DIR/logs/query.log";
311
my $max_stream_size=200;
312
my $max_result_size=200;
315
'rdf' => 'http://www.w3.org/1999/02/22-rdf-syntax-ns#',
316
'rdfs' => 'http://www.w3.org/2000/01/rdf-schema#',
317
'dc' => 'http://purl.org/dc/elements/1.1/',
318
'owl' => 'http://www.w3.org/2002/07/owl#',
319
# 'xsd' => 'http://www.w3.org/2001/XMLSchema#',
320
'foaf' => 'http://xmlns.com/foaf/0.1/',
321
'dcterms' => 'http://purl.org/dc/terms/',
322
'bot' => 'http://www.w3.org/2001/sw/Europe/200401/bot/terms\#',
326
# Redland perl modules
333
######################################################################
336
sub log_action ($$$;$) {
337
my($host, $db, $message, $now)=@_;
339
return unless open (LOG, ">>$log_file");
340
my($sec,$min,$hour,$mday,$mon,$year)=gmtime $now;
341
my $date=sprintf("%04d-%02d-%02dT%02d:%02d:%02dZ",1900+$year,$mon+1,$mday,$hour,$min,$sec);
342
$message =~ s/[\n\s]+/ /gs
344
print LOG "$host $date $db $message\n";
352
<p>The source code of this demonstration is available in the Redland
353
bindings distribution as <tt>demos/query.pl</tt> or from the
354
<a href="http://librdf.org/">Redland</a> website</p>
357
print qq{<hr />\n\n<p class="copyright"><a href="http://purl.org/net/dajobe/">Dave Beckett</a></p>\n\n</div></body>\n</html>\n};
360
sub html_escape ($) {
362
$str =~ s/\&/\&/g;
373
$node_label=" ";
374
} elsif($node->is_resource) {
375
$node_label=$node->uri->as_string;
376
$node_label=qq{<a href="$node_label">$node_label</a>};
377
} elsif ($node->is_literal) {
378
$node_label=$node->literal_value_as_latin1;
379
if($node->literal_value_language) {
380
$node_label.="@".$node->literal_value_language;
382
if($node->literal_datatype &&
383
!$node->literal_value_is_wf_xml) {
384
$node_label.="^^<".$node->literal_datatype->as_string.">";
386
} elsif ($node->is_blank) {
387
$node_label="blank node ".$node->blank_identifier;
389
$node_label=$node ? $node->as_string : "undef";
394
sub print_bindings_results($) {
397
print qq{<p>Variable bindings result format</p>\n\n};
399
my $width=$results->bindings_count;
402
for(my $i=0; $i < $width; $i++) {
403
my $name=$results->binding_name($i);
404
push(@variables, $name) if $name;
408
my $t=join('</th> <th>', @variables);
411
<table align="center" border="1">
419
while(!$results->finished) {
422
print qq{<tr><td>$count</td>\n};
423
for(my $i=0; $i < $results->bindings_count; $i++) {
424
my $label=format_node($results->binding_value($i));
425
print "<td>$label</td>";
428
$results->next_result;
430
if ($count >= $max_result_size) {
434
<td colspan="$w">Truncated at $max_result_size items.</td>
448
my $pl=($count != 1) ? 's' : '';
449
print "\n\n<p>Found $count result$pl</p>\n";
453
sub print_graph_results($) {
455
my $stream=$results->as_stream;
457
print qq{<p>Graph result format</p>\n\n};
461
<table align="center" border="1">
470
for(;!$stream->end ; $stream->next) {
471
my $statement=$stream->current;
475
my $subject_label=format_node($statement->subject);
476
my $predicate_label=format_node($statement->predicate);
477
my $object_label=format_node($statement->object);
481
<td>$subject_label</td>
482
<td>$predicate_label</td>
483
<td>$object_label</td>
488
if ($count >= $max_stream_size) {
491
<td colspan="3">Truncated at $max_stream_size items - sorry, this is just a demonstration.</td>
505
my $pl=($count != 1) ? 's' : '';
506
print "\n\n<p>Found $count triple$pl</p>\n";
511
sub print_boolean_results($) {
514
print qq{<p>Boolean result format</p>\n\n};
516
my $bresult=$results->get_boolean;
517
print "<p>Result: ",$bresult ? "True" : "False","</p>\n\n";
521
######################################################################
526
# CGI parameter paranoia
530
$val=$q->param('uri');
531
if(defined $val && $val =~ /^([ -~]+)$/) {
538
$val=$q->param('query');
539
if(defined $val && $val =~ /^(.*)$/s) {
543
my $query_language=$default_query_language;
544
$val=$q->param('language');
545
if(defined $val && $val =~ /^(rdql|sparql)$/s) {
550
$val=$q->param('raw');
551
if(defined $val && $val =~ /^(0|1)$/s) {
557
# End of parameter decoding
561
$val=$q->param('execute');
562
if(defined $val && $val eq 'no') {
567
my $host=$q->remote_host;
570
######################################################################
573
print $q->header(-type => 'text/html', -charset=>'utf-8');
576
# Always print header
578
<?xml version="1.0" encoding="utf-8"?>
579
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
580
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
582
<title>Redland Rasqal RDF Query Demonstration</title>
588
<h1>Redland Rasqal RDF Query Demonstration</h1>
591
<p>This is a demonstration of using
592
<a href="http://librdf.org/rasqal/">Rasqal</a>
593
to perform queries in either of the RDQL or SPARQL languages over RDF data.
594
The data is loaded into a <a href="/">Redland</a> model and then
595
queried and results accessed via the
596
<a href="http://librdf.org/docs/perl.html">Redland Perl</a>
602
# use q->url() to get URL of this script without any query parameters
603
# since we are using a POST here and don't want them added to the
605
#my $action_url="/".$q->url(-relative=>1);
606
my $action_url="/query";
608
my $query_language_label=$query_language_labels{$query_language};
610
if($execute eq 'no') {
613
<table border="0" cellspacing="0" cellpadding="0" summary="Main navigation" width="100%" summary="" class="headingbox">
615
<td class="mainnavback"><a class="mainnavlink" href="/query?language=rdql">Query with RDQL</a></td>
616
<td class="mainnavback"><a class="mainnavlink" href="/query?language=sparql">Query with SPARQL</a></td>
624
<p>Firstly choose whether to query in
625
<a href="/query?language=rdql">RDQL</a> or
626
<a href="/query?language=sparql">SPARQL</a>, then
627
enter a URI of some RDF content to query and run it.
628
The example query is for FOAF and finds the
629
names of all the people in the graph. There are other example
630
queries further down the page.</p>
632
<p>Some RDF content you can use could be my FOAF file at:
633
$example_foaf_uri<br />
634
or the W3C\'s RSS 1.0 file at:
638
print $q->start_form(-method=>'GET', -action => $action_url, -name => 'myform'),"\n";
639
print "<p><em>RDF content URIs</em><br/>\n";
640
print $q->textfield(-name=>'uri',
644
my $examples=$query_examples{$query_language};
646
print "</p>\n\n<p><em>Query</em><br/>\n";
647
print $q->textarea(-name=>'query',
648
-default=>$examples->[0]->{Q},
652
print "</p>\n\n<p>in query language:\n";
653
print $q->radio_group(-name=>'language',
654
-values=>\@query_languages,
655
-default=>$default_query_language,
656
-labels=>\%query_language_labels);
658
print " ";
659
print $q->checkbox(-name=>'raw',
662
-label=>'raw syntax output');
666
print $q->submit('Run Query'),"\n";
667
print qq{<input type="button" value="Clear Query" onclick="document.myform.query.value=''" />\n};
670
print $q->endform,"\n";
673
if($query_string && $uri_string) {
675
<p>See example queries at the
676
<a href="$action_url">Rasqal Query Demonstration home</a></p>
681
my $query_blurb=$query_blurbs{$query_language};
686
<p>Some other $query_language_label example queries:</p>
689
for my $qe (@{$examples}) {
690
my $t=$qe->{T} || '';
692
$t="<em>$t</em><br />\n" if $t;
693
print "<li>${t}Data: $d<br />\nQuery:";
694
print "<pre>\n". html_escape($qe->{Q}) ."</pre>\n";
696
$q->param('uri', $d);
697
$q->param('query', $qe->{Q});
698
$q->param('language', $query_language);
699
my $query_uri=$q->self_url;
700
print qq{<a href="$query_uri">Run this query</a>\n};
719
if(!$query_string || !$uri_string) {
720
if ($query_string || $uri_string) {
721
print "<hr />\n\n<h2>Error</h2>\n";
722
print "\n\n<p>Got a query string but no RDF content URIs.</p>\n"
724
print "\n\n<p>Got RDF content URIs but no query string.</p>\n"
731
print "<hr />\n\n<h2>Results</h2>\n";
733
######################################################################
736
if($execute eq 'no') {
745
my $store_type="hashes";
746
my $options=join(',' , "contexts='yes'", "hash-type='memory'");
752
my($code, $level, $facility, $message, $line, $column, $byte, $file, $uri)=@_;
754
push(@warnings, [$message, $line]);
756
push(@errors, [$message, $line]);
759
#print "code $code\n";
760
#print "level $level\n";
761
#print "facility $facility\n";
762
#print "message: $message\n" if defined $message;
763
#print "line $line\n";
764
#print "column $column\n";
765
#print "byte $byte\n";
766
#print "file $file\n" if defined $file;
767
#print "uri $uri\n" if defined $uri;
770
RDF::Redland::set_log_handler($handler);
773
$storage=new RDF::Redland::Storage($store_type, $db, $options);
775
$model=new RDF::Redland::Model($storage, "");
777
if(!$storage && !$model) {
778
log_action($host, $db, "Failed to open database");
779
print "\n\n<p>Sorry - failed to open RDF Database. This problem has been recorded.</p>\n";
787
for my $u (split(/ /, $uri_string)) {
788
if($u !~ /^(ftp|http):/i) {
789
push(@warnings, ["Ignored non-ftp/http URI $u", 0]);
793
$uri=new RDF::Redland::URI($u);
794
my $parser=new RDF::Redland::Parser("guess");
795
eval { $parser->parse_into_model($uri, $uri, $model); };
797
my $err=join("<br />", map {$_->[0]} @errors);
798
print "\n\n<p><b>Loading URI $u failed with errors:</b><br />\n$err</p>\n";
811
if(!$query_string || !$uri_string) {
816
log_action($host, $db, "Data $uri_string with query '$query_string' in language $query_language");
817
if($query_string =~ m%\s+(from)\s+%i) {
819
log_action($host, $db, "Query '$query_string' contains 'from' - ignoring it");
820
print "\n\n<p>The query string contains '$fr'; ignoring it</p>\n";
829
my $base_uri=new RDF::Redland::URI("http://librdf.org/query");
830
eval '$query=new RDF::Redland::Query($query_string, $base_uri, undef, $query_language);';
831
if($@ || @errors || !$query) {
832
my $err=join("<br />", map {$_->[1].":".$_->[0]} @errors);
833
print "\n\n<p><b>$query_language_label query construction failed with errors:</b><br />\n$err</p>\n";
839
$q->delete('command');
841
$q->delete('language');
844
eval '$results=$model->query_execute($query);';
845
if($@ || @errors || !$results) {
846
my $err=join("<br />", map {$_->[1].":".$_->[0]} @errors);
847
print "\n\n<p><b>$query_language_label query execution failed with errors:</b><br />\n$err</p>\n";
853
my $w=join("<br />", map {$_->[1].":".$_->[0]} @warnings);
854
print "<p><b>$query_language_label warnings:</b><br />\n$w</p>\n";
859
my $base_uri=new RDF::Redland::URI('http://librdf.org/query/');
860
my $str=$results->to_string;
863
print qq{<pre>\n} . html_escape($str) .qq{\n</pre>\n};
865
print qq{<p>No raw format returned\n</p>};
870
if($results->is_bindings) {
871
print_bindings_results($results);
872
} elsif($results->is_graph) {
873
print_graph_results($results);
874
} elsif($results->is_boolean) {
875
print_boolean_results($results);
877
print qq{<p>Unknown results format - cannot display</p>\n\n};