This document provides an introduction to querying RDF through AllegroGraph's built-in SPARQL endpoint.
The code samples provided assume that they are evaluated with the sparql.server
, db.agraph.sparql
, and db.agraph
packages visible. If you are following along in a REPL, you can evaluate the following forms:
(defpackage sparql-server-example
(:use :cl :excl :db.agraph
:db.agraph.sparql
:sparql.server))
(in-package :sparql-server-example)
Basic concepts
A SPARQL endpoint is a service (typically exposed via HTTP, though other protocols are possible) that accepts a SPARQL query as input and returns results as output. The protocol is described in http://www.w3.org/TR/rdf-sparql-protocol/.
The protocol provides arguments to override certain elements of a query: dataset specifications (FROM
and FROM NAMED
) for example. Consult the specification itself for more details.
There are a few necessary areas of extension in AllegroGraph's implementation.
Because AllegroGraph allows you to open multiple triple stores, and conceivably serve different sets of triple stores over different URLs, the concept of a server is introduced. Servers are associated with a URL, and map ids to triple stores. The suggested URL, /sparql
, is the default. Databases are exported into a server.
The protocol specification, however, defines no implementation-independent way to handle multiple triple stores. If no id
argument is provided, the current value of *db*
is used. If *db*
is not a valid triple-store, the server will return an error unless an id
argument is provided.
This guide will demonstrate the instantiation of a server, exporting triple stores, and making queries over HTTP.
Making a server
The function make-sparql-protocol-server creates a new sparql-protocol-server
instance. It takes keyword arguments path
, which must be unique (if a server already exists on that path, it will be returned instead of a new server), and start
, which is provided to AllegroServe's start
function. You can use start
to specify a port.
Let's try it. First, we'll create and populate a triple store. You should adjust the paths according to your system.
The commands you enter are set in bold.
sparql-server-example(8): (create-triple-store "/tmp/protocol-example")
#<db.agraph::triple-db /tmp/protocol-example, open @ #x111f7c12>
sparql-server-example(9): (load-ntriples "sys:agraph;tutorial-files;wilburwine.ntriples")
2012
#(0 0 0 0 0 0 0 0 0 0 0 31)
sparql-server-example(10): (index-all-triples)
1
Now we'll make a server at the conventional location, but on a high port.
sparql-server-example(11): (make-sparql-protocol-server :start '(:port 8080))
#<sparql-protocol-server @ #x1135d462>
This new server is automatically assigned to *sparql-protocol-server*
:
sparql-server-example(12): *sparql-protocol-server*
#<sparql-protocol-server @ #x1135d462>
You can now do conventional SPARQL protocol queries against *db*
, using either AllegroGraph's SPARQL protocol client, or another library of your choosing. We can test this out quite simply by using AllegroServe's HTTP client library, providing just a query parameter. (You'll see AllegroServe log messages appear in the output, because the same Lisp process is both running the server and making the request.)
sparql-server-example(13): (net.aserve.client:do-http-request "http://localhost:8080/sparql"
:query '((query . "SELECT ?s ?o { ?s a ?o . } LIMIT 5")))
aserve-accept-6: 06/24/07 - 12:45:13 - Maximum socket file descriptor number is now 38
127.0.0.1 - - [Sun, 24 Jun 2007 19:45:13 GMT] "GET /sparql?query=SELECT+%3fs+%3fo+%7b+%3fs+a+%3fo+.+%7d+LIMIT+5 HTTP/1.1" 200 1592
"<?xml version="1.0\"?>
<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\">
<head>
<variable name=\"s\"/>
<variable name=\"o\"/>
</head>
<results ordered=\"false\" distinct=\"false\">
<result>
<binding name=\"s\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#SchlossRothermel</uri>
</binding>
<binding name=\"o\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Winery</uri>
</binding>
</result>
<result>
<binding name=\"s\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#SchlossRothermel</uri>
</binding>
<binding name=\"o\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Winery</uri>
</binding>
</result>
<result>
<binding name=\"s\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Mountadam</uri>
</binding>
<binding name=\"o\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Winery</uri>
</binding>
</result>
<result>
<binding name=\"s\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Mountadam</uri>
</binding>
<binding name=\"o\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Winery</uri>
</binding>
</result>
<result>
<binding name=\"s\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#SchlossVolrad</uri>
</binding>
<binding name=\"o\">
<uri>http://www.w3.org/TR/2003/CR-owl-guide-20030818/wine#Winery</uri>
</binding>
</result>
</results>
</sparql>"
200
((:connection . "Close") (:content-type . "application/sparql-results+xml; charset=utf-8")
(:date . "Sun, 24 Jun 2007 19:45:13 GMT") (:server . "AllegroServe/1.2.50")
(:transfer-encoding . "chunked"))
#<uri http://localhost:8080/sparql?query=SELECT+%3fs+%3fo+%7b+%3fs+a+%3fo+.+%7d+LIMIT+5>
You'll see that AllegroServe returned the string response from the server: the SPARQL XML results format for the five results. Observe also the Content-Type
header returned by the server.
Serving individual triple stores
Of course, you aren't always going to have only one triple store open. Let's open another, and keep track of the original.
sparql-server-example(14): (setf *wine-db* *db*)
#<db.agraph::triple-db /tmp/protocol-example, open @ #x11178c6a>
sparql-server-example(15): (create-triple-store "/tmp/nearly-empty")
#<db.agraph::triple-db /tmp/nearly-empty, open @ #x1148a7ea>
sparql-server-example(16): (add-triple !rdf:type !rdfs:label !"type")
1
sparql-server-example(17): (setf *nearly-empty-db* *db*)
#<db.agraph::triple-db /tmp/nearly-empty, open @ #x1148a7ea>
Now we can export these two triple stores to the running protocol server, giving each of them a descriptive ID.
sparql-server-example(18): (export-to-sparql-protocol *sparql-protocol-server* :db *wine-db* :id "wine")
(("wine" . #<db.agraph::triple-db /tmp/protocol-example, open @ #x11178c6a>))
sparql-server-example(19): (export-to-sparql-protocol *sparql-protocol-server* :db *nearly-empty-db* :id "empty")
(("empty" . #<db.agraph::triple-db /tmp/nearly-empty, open @ #x1148a7ea>)
("wine" . #<db.agraph::triple-db /tmp/protocol-example, open @ #x11178c6a>))
Now we can make a query against a specific triple store by providing the id
parameter. Here we'll run the original query against the nearly empty triple store, and correctly get no results.
sparql-server-example(20): (net.aserve.client:do-http-request "http://localhost:8080/sparql"
:query '((query . "SELECT ?s ?o { ?s a ?o . } LIMIT 5")
(id . "empty")))¯
aserve-accept-6: 06/24/07 - 16:32:41 - Maximum socket file descriptor number is now 49
127.0.0.1 - - [Sun, 24 Jun 2007 23:32:41 GMT] "GET /sparql?query=SELECT+%3fs+%3fo+%7b+%3fs+a+%3fo+.+%7d+LIMIT+5&id=empty HTTP/1.1" 200 214
"<?xml version="1.0\"?>
<sparql xmlns=\"http://www.w3.org/2005/sparql-results#\">
<head>
<variable name=\"s\"/>
<variable name=\"o\"/>
</head>
<results ordered=\"false\" distinct=\"false\">
</results>
</sparql>"
200
((:connection . "Close") (:content-type . "application/sparql-results+xml; charset=utf-8")
(:date . "Sun, 24 Jun 2007 23:32:41 GMT") (:server . "AllegroServe/1.2.50")
(:transfer-encoding . "chunked"))
#<uri http://localhost:8080/sparql?query=SELECT+%3fs+%3fo+%7b+%3fs+a+%3fo+.+%7d+LIMIT+5&id=empty>
Other parameters
As well as id
and query
, you can provide a few protocol parameters in your requests. The specification allows for named-graph-uri
and default-graph-uri
. AllegroGraph also permits extended
, to enable extended SPARQL behavior, but be aware that such results cannot always be encoded in SPARQL XML.
Base URIs
The protocol expects an endpoint accessed via HTTP to have a default base URI, which is used when parsing queries that contain relative URIs but no BASE
declaration. By default, AllegroGraph will use the scheme, host, port, and path of the request URI to construct a base URI.
If you wish to override this behavior, you can provide a string as the :base-uri
argument to make-sparql-protocol-server
, or use the sparql-protocol-server-base-uri
accessor on a server instance (such as *sparql-protocol-server*
).
In most cases, queries for which no base URI is specified will produce unpredictable results, so this feature is a last resort.
Using the protocol client
AllegroGraph also includes a SPARQL protocol client, which handles the entire process of remote querying seamlessly, translating results into native formats for you.
See sparql-protocol-client-guide.html for more information.
API reference
Create a sparql-protocol-server
instance and optionally start the HTTP server. Returns the server instance. This value becomes the default server (the value of the *sparql-protocol-server*
variable).
The path
argument (which defaults to "/sparql") is the server URL for the protocol handler. This is provided to the AllegroServe publish
function.
If a server already exists and is mapped to the given path, that server is re-used.
The start
argument, if non-nil
, ensures that the HTTP server is running. If the value is a list, it is passed to the AllegroServe start
function. The most common value is (list :port nnn)
. You are strongly advised to include :external-format :utf-8
to comply with the SPARQL protocol specification.
(The default behavior with start
as t
is to start a server on port 80 with the recommended external format. If you already have a server running, pass :start nil
.)
The wserver
argument may be an AllegroServe wserver
instance.
You can specify the default base URI to use when handling SPARQL Protocol requests by using the base-uri
argument. This value should be a string naming an absolute URI.
db
available to SPARQL protocol queries. id
is used to specify which store should be queried. If server
is null, the default SPARQL protocol server instance is used for the operation. Otherwise, server
is used.