1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380
|
<html>
<head>
<title>SemWeb: Querying</title>
<link rel="stylesheet" type="text/css" href="stylesheet.css"/>
</head>
<body>
<p><a href="index.html">SemWeb Documentation</a></p>
<h1>Querying: Simple Entailment and SPARQL</h1>
<p>This page provides an example for running queries against RDF data
using SemWeb. Three query methods are supported. The first is the
GraphMatch engine which does simple entailment, matching a simple graph
with no disjunctions or optional edges against target data. The second method
runs SPARQL queries against any data source supported by SemWeb using
a SPARQL query engine. The third method passes SPARQL queries to a remote
SPARQL endpoint over HTTP.</p>
<h2>Getting the data</h2>
<p>We'll use an RDF description of the people in the U.S. Congress
for this example. Download the data files at
<a href="http://www.govtrack.us/data/rdf/people.rdf.gz">http://www.govtrack.us/data/rdf/people.rdf.gz</a>,
<a href="http://www.govtrack.us/data/rdf/bills.108.rdf.gz">http://www.govtrack.us/data/rdf/bills.108.rdf.gz</a>, and
<a href="http://www.govtrack.us/data/rdf/bills.108.cosponsors.rdf.gz">http://www.govtrack.us/data/rdf/bills.108.cosponsors.rdf.gz</a>
and un-gzip them (on Windows use WinZip).</p>
<p>To simply some things, we'll put the contents of these three files into a single Notation3 file using the following command. (You may need to adjust the path to <tt>rdfstorage.exe</tt>. It should be in SemWeb's <tt>bin</tt> directory.)</p>
<pre class="code">
$ mono rdfstorage.exe --out n3:congress.n3 people.rdf bills.108.rdf bills.108.cosponsors.rdf
</pre>
<p><tt>rdfstorage.exe</tt> reads RDF files into
a StatementSink, either an RdfWriter or a Store. The default is to read files in RDF/XML format (with the RdfXmlReader). We specified the output as <tt>n3:congress.n3</tt>, which means to write the data in Notation 3 (N3) format to the file <tt>congress.n3</tt>. The command outputs the following:</p>
<pre class="code">
people.rdf 0m5s, 106423 statements, 19041 st/sec
bills.108.rdf 0m13s, 212142 statements, 15866 st/sec
bills.108.cosponsors.rdf 0m8s, 145743 statements, 16814 st/sec
Total Time: 0m27s, 464308 statements, 16787 st/sec
</pre>
<h2>Writing the GraphMatch Query</h2>
<p>The first query method is the GraphMatch method using my own "RSquary" query format, which is actually just plain RDF (think RDF-squared query because it's an RDF query over RDF data). A simple RSquary query is just a graph to be matched against the target data model, here in N3 format:</p>
<pre class="code">
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix bill: <tag:govshare.info,2005:rdf/usbill/> .
?bill rdf:type bill:SenateBill .
?bill bill:congress "108" .
?bill bill:number "1024" .
?bill bill:cosponsor ?person .
?person foaf:name ?name .
</pre>
<p>A benefit of using N3 is that it allows entity names starting with "?" which are read in by the N3Reader as Variable objects. (The Variable class is a subclass of BNode.) Actually, in queries
BNodes are treated as variables too. This makes sense because a BNode in the query graph could not possibly match a BNode in the target data model since a BNode cannot appear in two documents. So only named entities (with URIs) and literals are used to match against the target data model</p>
<p>The query above says: Find all bindings
for the variables <tt>?bill</tt>, <tt>?person</tt>, and <tt>?name</tt>
such that 1) <tt>?bill</tt> is a Senate bill identified by congress 108 and number 1024, 2) <tt>?bill</tt> has <tt>?person</tt> as one of its cosponsors,
and 3) <tt>?name</tt> is a name of <tt>?person</tt>.</p>
<p>Save the above query as <tt>congress_query.n3</tt>.</p>
<h2>Running the Query</h2>
<p>SemWeb contains a program called <tt>rdfquery.exe</tt> which runs
a query against a target data model. To run the query execute:</p>
<pre class="code">
$ mono rdfquery.exe n3:congress.n3 < congress_query.n3
</pre>
<p><tt>rdfquery.exe</tt> reads a query from standard input (hence the redirect) and matches it against the data sources listed in arguments on the command line. It will take a few moments to load in the 710k statements from the congress.n3 file before it outputs the results. The output is by default in the standard SPARQL result XML format. Here it is, below. (Some XML comments appear at the top to tell you how the query was executed, but that is not repeated below.)</p>
<pre class="code">
<sparql xmlns="http://www.w3.org/2005/sparql-results#">
<head>
<variable name="bill" />
<variable name="person" />
<variable name="name" />
</head>
<results ordered="false" distinct="false">
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/C001041</uri>
</binding>
<binding name="name">
<literal>Hillary Clinton</literal>
</binding>
</result>
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/C000880</uri>
</binding>
<binding name="name">
<literal>Michael Crapo</literal>
</binding>
</result>
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/L000174</uri>
</binding>
<binding name="name">
<literal>Patrick Leahy</literal>
</binding>
</result>
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/M001153</uri>
</binding>
<binding name="name">
<literal>Lisa Murkowski</literal>
</binding>
</result>
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/M001111</uri>
</binding>
<binding name="name">
<literal>Patty Murray</literal>
</binding>
</result>
<result>
<binding name="bill">
<uri>tag:govshare.info,2005:data/us/congress/108/bills/s1024</uri>
</binding>
<binding name="person">
<uri>tag:govshare.info,2005:data/us/congress/people/S000148</uri>
</binding>
<binding name="name">
<literal>Charles Schumer</literal>
</binding>
</result>
</results>
</sparql>
</pre>
<p>The query took 15 seconds to execute on my machine, with a good portion of that just loading the data from the file into memory. We could speed things up by first putting the RDF data into a SQL database and then querying the database directly. This way, the data is not loaded into memory and queries against the database make use of indexes already present.</p>
<!--<p>To load the data into a database, I'll use Sqlite again, so see the section of the documentation on Databases if you haven't looked at that already. To get the data into a database, I'll modify the <tt>rdfstorage.exe</tt> command at the start as follows:</p>
<pre class="code">
$ mono rdfstorage.exe --out "sqlite:rdf:Uri=file:congress.sqlite;version=3" people.rdf bills.108.rdf bills.108.cosponsors.rdf
</pre>
<p>(We could have also read the <tt>congress.n3</tt> file directly, but to read N3 files you must add the option <tt>-in n3</tt>.) Loading the data into Sqlite is much slower than loading it into memory (MySQL would be somewhere in the middle), but the speed-up will be worth it in the end when queries are much faster.</p>-->
<h2>SPARQL Queries</h2>
<p>For executing SPARQL queries over data sources that don't support SPARQL themselves (i.e. the MemoryStore, SQLStore, etc.),
SemWeb uses the <a href="http://sourceforge.net/projects/sparql/">SPARQL query engine by Ryan Levering</a>. The library is written in Java, but for SemWeb I convert it to a .NET assembly using <a href="http://www.ikvm.net/">IKVM</a>.</p>
<p>The advantage of SPARQL over RSquary is that it supports much more complex queries, including
optional statements, disjunctions/unions, and special filters.</p>
<p>The query above equivalently in SPARQL is:</p>
<pre class="code">
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX bill: <tag:govshare.info,2005:rdf/usbill/>
SELECT ?bill ?person ?name
WHERE {
?bill rdf:type bill:SenateBill .
?bill bill:congress "108" .
?bill bill:number "1024" .
?bill bill:cosponsor ?person .
?person foaf:name ?name .
}
</pre>
<p>Put that in <tt>congress_query.sparql</tt> and then run it with:</p>
<pre class="code">
$ mono rdfquery.exe -type sparql n3:congress.n3 < congress_query.sparql
</pre>
<p>This has the same output as above.</p>
<p>You can also use the <tt>rdfquery</tt> tool to query a remote SPARQL end point. Invoke it like this:</p>
<pre class="code">
echo "DESCRIBE <tag:govshare.info,2005:data/us>" | \
mono bin/rdfquery.exe -type sparql sparql-http:http://www.govtrack.us/sparql
</pre>
<h2>Running Queries Programmatically</h2>
<p>To run a query from a program, you need to 1) create a <tt>Query</tt> object, 2) create a <tt>QueryResultSink</tt> object that will receive the results of the query, and 3) run <tt>Run</tt> on the <tt>Query</tt> object.</p>
<p>There are two types of <tt>Query</tt> objects, <tt>GraphMatch</tt> objects that perform simple entailment queries (i.e. RSquary) and <tt>SPARQL</tt> objects that perform SPARQL queries.</p>
<p>The <tt>GraphMatch</tt> classs takes a graph with variables and figures out all of the ways the variables can be assigned to ("bound") to values in the target data model so that the statements in the query are all found in the target data model. Each set of variable assignments becomes a result.</p>
<p>Create a <tt>GraphMatch</tt> class (in the <tt>SemWeb.Query</tt> namespace) by passing to its constructor a <tt>StatementSource</tt>. Remember that <tt>RdfReader</tt>s and <tt>MemoryStore</tt>s are <tt>StatementSource</tt>s, so you can either pass it a reader over a file or a store in which you've programmatically constructed the query.</p>
<pre class="code">
Query query = new GraphMatch(new N3Reader(queryfile));
</pre>
<p>Then load the data that the query will be run against:</p>
<pre class="code">
MemoryStore data = new MemoryStore();
data.Import(new N3Reader(datafile));
</pre>
<p>Next, create a <tt>QueryResultSink</tt>. This class has an <tt>Add</tt> method that receives an array of variable bindings which is called for each query result. The variable bindings say how each variable in the query was bound to a resource in the target data model. There is one implementation of this class in SemWeb, then <tt>SparqlXmlQuerySink</tt> which is the standardized XML output format for SPARQL results. Note that you can use this output format with any <tt>Query</tt> object, not just the <tt>Sparql</tt> class. The constructor takes a <tt>TextWriter</tt> or <tt>XmlWriter</tt> to which the results are written.</p>
<pre class="code">
QueryResultSink sink = new SparqlXmlQuerySink(Console.Out);
</pre>
<p>You can, of course, create your own subclass of <tt>QueryResultSink</tt> which you will have to do if you want to do anything interesting with the results of the query. Here's an example <tt>QueryResultSink</tt> which simply prints the variable bindings to the Console. (Note that there are several other methods that can be overridden which are executed at the start and end of the query.)</p>
<pre class="code">
public class PrintQuerySink : QueryResultSink {
public override bool Add(VariableBindings result) {
foreach (Variable var in result.Variables) {
if (var.LocalName != null && result[var] != null) {
Console.WriteLine(var.LocalName + " ==> " + result[var].ToString());
}
Console.WriteLine();
}
return true;
}
}
</pre>
<p>Lastly, run the query with <tt>Run</tt>, passing it the target data model and the result sink.</p>
<pre class="code">
query.Run(data, sink);
</pre>
<p>To create a SPARQL query instead, construct a new <tt>SparqlEngine</tt> object (in the <tt>SemWeb.Query</tt> namespace but in the separate <tt>SemWeb.Sparql.dll</tt> assembly!).</p>
<pre class="code">
Query query = new SparqlEngine(new StreamReader(queryfile));
</pre>
<p>Run the query the same as with <tt>GraphMatch</tt>. There are several types of SPARQL queries, not all of which result a list of variable bindings. For instance, the DESCRIBE and CONSTRUCT query types return RDF triples. You can run queries generically and output the results to a <tt>TextWriter</tt> just by passing a <tt>TextWriter</tt> to <tt>Run</tt> instead of a <tt>QueryResultSink</tt>. Or, see the API documentation on the <tt>Sparql</tt> class for more control over the output of SPARQL queries.</p>
<p>An entire program for querying is below:</p>
<pre class="code" file="../examples/query.cs">// This example runs a query.
using System;
using System.IO;
using SemWeb;
using SemWeb.Query;
public class Example {
public static void Main(string[] argv) {
if (argv.Length < 3) {
Console.WriteLine("Usage: query.exe format queryfile datafile");
return;
}
string format = argv[0];
string queryfile = argv[1];
string datafile = argv[2];
Query query;
if (format == "rsquary") {
// Create a simple-entailment "RSquary" query
// from the N3 file.
query = new GraphMatch(new N3Reader(queryfile));
} else {
// Create a SPARQL query by reading the file's
// contents.
query = new SparqlEngine(new StreamReader(queryfile));
}
// Load the data file from disk
MemoryStore data = new MemoryStore();
data.Import(new N3Reader(datafile));
// First, print results in SPARQL XML Results format...
// Create a result sink where results are written to.
QueryResultSink sink = new SparqlXmlQuerySink(Console.Out);
// Run the query.
query.Run(data, sink);
// Second, print the results via our own custom QueryResultSink...
query.Run(data, new PrintQuerySink());
}
public class PrintQuerySink : QueryResultSink {
public override bool Add(VariableBindings result) {
foreach (Variable var in result.Variables) {
if (var.LocalName != null && result[var] != null) {
Console.WriteLine(var.LocalName + " ==> " + result[var].ToString());
}
Console.WriteLine();
}
return true;
}
}
}
</pre>
<h2>Querying Remote SPARQL Endpoints</h2>
<p>It is also possible to run SPARQL queries directly against remote
HTTP endpoints. The <tt>rdfquery.exe</tt> command-line program can be
used to run queries directly. Take the following query in the file
"dbp.q" to query the <a href="http://dbpedia.org">DBpedia</a> database (a semantified Wikipedia)
for all statements that use the literal "John McCain":</p>
<pre class="code">SELECT * WHERE { ?s ?p "John McCain" . }</pre>
<p>Run this against the remote SPARQL endpoint at <tt>http://DBpedia.org/sparql</tt>
using:</p>
<pre class="code">mono bin/rdfquery.exe sparql-http:http://DBpedia.org/sparql -type sparql < dbp.q</pre>
<p>The output is given below:</p>
<pre class="code"><sparql>
<head>
<variable name="s"/>
<variable name="p"/>
</head>
<results distinct="false" ordered="true">
<result>
<binding name="s"><uri>http://dbpedia.org/resource/John_McCain</uri></binding>
<binding name="p"><uri>rdfs:label</uri></binding>
</result>
<result>
<binding name="s"><uri>http://dbpedia.org/resource/John_McCain</uri></binding>
<binding name="p"><uri>http://dbpedia.org/property/name</uri></binding>
</result>
</results>
</sparql></pre>
<p>It is also possible to query remote endpoints programmatically using
the <tt>SemWeb.Remote.SparqlHttpSource</tt> class. For example:</p>
<pre class="code">SparqlHttpSource source = new SparqlHttpSource("http://DBpedia.org/sparql");
source.RunSparqlQuery("SELECT * WHERE { ?s ?p \"John McCain\" . }", Console.Out);
(or)
source.RunSparqlQuery("SELECT * WHERE { ?s ?p \"John McCain\" . }", new SparqlXmlQuerySink(Console.Out));</pre>
<p>There are other overloads of <tt>RunSparqlQuery</tt> that provide better access
to the results than dumping the output to a TextWriter. See the API documentation
for details.</p>
</body>
</html>
|