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

make-sparql-protocol-server &key  class  wserver  path  base-uri  start
function

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.

export-to-sparql-protocol server  &key  id  db
function
Make the triple store 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.