SPARQL S-Expression Syntax | AllegroGraph 9.0.1
Author: Franz Incorporated

Copyright (c) 2005 - 2026 Franz, Incorporated
Last updated 27 May 2026 at 10:34

# SPARQL S-Expression Syntax

- Documentation Index
  - Release Notes
  - Quick Start
  - SSL/TLS Quick Start
  - Introduction
  - Setup and Configuration
    - Download
    - Server Installation
    - Server Configuration and Control
    - WebView
    - Database Upgrading
    - Managing Users
  - Server Management
    - Performance Tuning
    - Deleting Duplicate Triples
    - Purging Deleted Triples
    - Auditing
    - Managing Users
    - Replication
    - Point-in-Time Recovery
    - Transaction Log Archiving
    - Security Overview
    - Security Implementation
  - Tools
    - agtool
    - Data Loading
    - Data Export
    - Backup and Restore
    - Querying
    - RDFS++
    - Materializer
    - Event Scheduler
    - SHACL
  - Details
    - Triple Indices
    - Freetext Indices
    - Encoded IDs
    - Connection Pooling
    - N-dimensional Geospatial Overview
    - 2-D Geospatial
    - Temporal
    - Social Network
    - JavaScript
    - Datatypes
    - Data Conversion (Rapper)
    - Stored Procedures
    - Triple Attributes
    - Lineparse Format
  - Querying
    - SPARQL documentation summary
    - SPARQL Reference
    - SPIN
    - SPARQL Magic Properties
    - Prolog
  - 3rd-party tools
    - Solr text Indices
    - MongoDB integration
    - TopBraid Composer Plugin
    - Cloudera
  - Client APIs
    - Gruff
    - GraphTalker
    - REST/HTTP interface
    - HTTP reference
    - Javadocs (Sesame and Jena)
    - Python API
    - Lisp Reference
    - JavaScript
  - Tutorials
    - N-dimensional Geospatial
    - SPARQL Tutorial
    - Java Sesame
    - Java Jena
    - Python Sesame
    - Lisp
    - Prolog
    - More...
  - Other
    - Conferences and Seminars
    - Source Code on Github
    - Amazon EC2
    - Copyrights
  - Franz, Inc.

- Introduction
  - Example
  - Filters and Expressions
  - Packages
  - ASK, CONSTRUCT, DESCRIBE and SELECT
    - distinct, from, limit, offset, order
    - aggregates, group, having
    - construct-pattern, targets
    - in-line-data
    - query-options
    - vars
    - default-base
    - where
  - Update
    - Graph manipulation
    - Data manipulation
    - Loading from external sources
  - Filter Summary

Documentation Index

## Introduction

The Lisp client's run-sparql function can accept a pre-built query plan, a string in the SPARQL query syntax, or a Lisp s-expression that encodes the query. The s-expression can be created via a call to parse-sparql or by hand. This document describes the s-expression format.

Note that the s-expression syntax is stable but is still subject to change as the query engine it supports improves and the SPARQL language continues to mature. Franz intends to provide backward compatibility for all existing features.

### Example

As an example, the s-expression for query 8 from from the SP2 benchmark:

    SELECT DISTINCT ?name
    WHERE {
      ?erdoes rdf:type foaf:Person .
      ?erdoes foaf:name 'Paul Erdoes'^^xsd:string .
      {
        ?document dc:creator ?erdoes .
        ?document dc:creator ?author .
        ?document2 dc:creator ?author .
        ?document2 dc:creator ?author2 .
        ?author2 foaf:name ?name
        FILTER (?author!=?erdoes &&
                ?document2!=?document &&
                ?author2!=?erdoes &&
                ?author2!=?author)
      } UNION {
        ?document dc:creator ?erdoes.
        ?document dc:creator ?author.
        ?author foaf:name ?name
        FILTER (?author!=?erdoes)
      }
    }

is

    (sparql.parser:sparql :select :vars (?name) :where
       (:join
        (:join nil
    	   (:bgp #(?erdoes !rdf:type !foaf:Person)
    		 #(?erdoes !foaf:name !"Paul Erdoes"^^<http://www.w3.org/2001/XMLSchema#string>)))
        (:union
         (:filter
          (:join nil
    	     (:bgp
    	      #(?document !dc:creator ?erdoes)
    	      #(?document !dc:creator ?author)
    	      #(?document2 !dc:creator ?author)
    	      #(?document2 !dc:creator ?author2)
    	      #(?author2 !foaf:name ?name)))
          (and (sparql.sop:!= ?author ?erdoes)
    	   (and (sparql.sop:!= ?document2 ?document)
    		(and (sparql.sop:!= ?author2 ?erdoes)
    		     (sparql.sop:!= ?author2 ?author)))))
         (:filter
          (:join nil
    	     (:bgp
    	      #(?document !dc:creator ?erdoes)
    	      #(?document !dc:creator ?author)
    	      #(?author !foaf:name ?name)))
          (sparql.sop:!= ?author ?erdoes))))
       :distinct :distinct)

This is the raw output of AllegroGraph's parse-sparql function 1 . There are many possible s-expressions for a given query. Before execution, the query engine does additional analysis and simplification. This means that you can be flexible in the way you generate queries and let the query engine convert them into its preferred form.

Every SPARQL s-expression starts with a description of the query type and verb:

    (query-type query-verb &key)

where `query-type` can be either `sparql.parser:sparql-update` or `sparql.parser:sparql` and `query-verb` must be one of:

- :ask
- :construct
- :describe
- :select
- :update

The rest of the s-expression is a keyword list whose permitted values vary depending on the verb. In the example above, the `:vars` argument specifies which variables to project and the `:where` argument describes the query plan to be executed. It shows that the first step of the query is a `:join` between another `:join` and a `:union`.

Note that `query-verb` and `query-type` are redundant but both are kept for historical reasons.

### Filters and Expressions

Many query clauses involve expressions that calculate values or that are used to filter the result set. These are also s-expressions that use a restricted set of functions and constants. For example, the SPARQL string filter "?a + ?b < 34" becomes the filter expression:

    (< (+ ?a ?b) !"34"^^<http://www.w3.org/2001/XMLSchema#integer>)

The complete list of expressions is outlined below.

### Packages

Note that the SPARQL parser places all query variables into the `query.variables` package for consistency during parsing. Your s-expressions do not need to do this and you are free to use any package that you would like.

### ASK, CONSTRUCT, DESCRIBE and SELECT

These four SPARQL verbs share the same basic query algebra (though some verbs have their own unique parameters).

The following keyword parameters and arguments are allowed in these verbs' s-expressions (these will be described in more detail below): 2

- aggregates aggregate-assignment-list
- construct-pattern triple-template
- default-base nil | URI
- distinct nil | `:distinct` | `:reduced`
- from dataset-list
- group expression-list
- having filter-expression
- in-line-data in-line-data
- limit number
- offset number
- order expression-list
- query-options option-list
- targets URI-list
- vars nil | variable-list
- where query-algebra

#### distinct, from, limit, offset, order

These forms control the number and order of the solutions as well as the SPARQL dataset used for the query and whether or not the solutions should be distinct (or reduced). For example a query like:

    select distinct * { ?A ?b ?c } order by ?c desc(?a) limit 10 offset 20

would be represented by the s-expression:

    (sparql.parser:sparql :select :vars (?A ?b ?c) :where (:bgp #(?A ?b ?c))
     :distinct :distinct
     :order ((:asc ?c) (:desc ?a))
     :limit 10
     :offset 20) :distinct
    This can be nil or one of the keywords :reduced or :distinct and controls whether the query returns all results, is allowed to omit duplicate results or must omit duplicates. :from dataset-list
    from describes the SPARQL dataset to use in the query. It is in the form of a dataset-list which is a list of pairs where each pair has the car :from or :from-named and the cdr is the name of the graph to include in the dataset (either a UPI or a future-part). :limit number
    limit indicates the maximum number of solutions to emit.:offset number
    offset indicates how many solutions to skip before beginning to return them.:order expression-list
    order controls the order of the results returned from the query. It is list of lists. Each sub-list must have the first element of either :asc or :desc (for ascending or descending). The second element of the sublist is a SPARQL expression.

#### aggregates, group, having

These forms control aggregation. As an example, this SPARQL query:

    select ?account (count(?order) as ?count) (sum(?total) as ?grand) {
      ?account :hasOrder ?order .
      ?order :hasTotal ?total .
    } group by ?account
    having (?grand > 10000)

could be expressed as:

    (sparql.parser:sparql :select :vars (?account ?count ?grand)
      :where
      (:bgp
       #(?account !<eh://hasOrder> ?order)
       #(?order !<eh://hasTotal> ?total))
      :aggregates
      ((?count (:count ?order))
       (?grand (:sum ?total))
       (?account (:identity ?account)))
      :group (?account)
      :having ((> ?grand !"10000"^^<http://www.w3.org/2001/XMLSchema#integer>))) :aggregates aggregate-expression-list
    Aggregation is described by an ordered list of instructions. Each instruction consists of a list in the form (variable-name (instruction expression &key distinct)). The instruction can be one of :_assign, :avg, :count, :group_concat, :identity, :max, :min, :sample, or :sum. Most of these correspond to the obvious SPARQL aggregation operator. :identity is used for variables from the group by expression and means that the value will come from the group. :_assign is used to handle temporary aggregation variables introduced by the parser. Consider a modified version of the query above where we now use an aggregate expression in the having clause:
    select (sum(?total) as ?grand) {
      ?account :hasOrder ?order .
      ?order :hasTotal ?total .
    } group by ?account
    having (sum(?total) > 10000) This will parse as:
    (sparql.parser:sparql :select :vars (?grand) :where
      (:bgp
       #(?account !<eh://hasOrder> ?order)
       #(?order !<eh://hasTotal> ?total))
      :aggregates
      ((?@aggregate-1 (:sum ?total))
       (?account (:identity ?account))
       (?grand (:_assign ?@aggregate-1)))
      :group (?account)
      :having ((> ?@aggregate-1 !"10000"^^<http://www.w3.org/2001/XMLSchema#integer>)) Where the expression in the having clause has been replaced by a newly introduced variable and :_assign is used to bind that variable to the query variable ?grand. :group expression-list
    This is simply a list of expressions that will be evaluated to determine the grouping. :having filter-expression
    This is a filter expression (which can include aggregation operators) used to filter out solutions.

#### construct-pattern, targets

:construct-pattern triple-pattern
  construct-pattern is used only by SPARQL CONSTRUCT queries and is a list of triple-pattern arrays (as in a BGP). For example, this construct query:

      construct {
        ?s rdf:type <eh://fruit> .
        ?s ?p ?o }
      where {
        ?s a <eh://fruityThing> .
        ?s ?p ?o .
      }

would be equivalent to the following s-expression:

    (sparql.parser:sparql :construct :vars
     (?o ?p ?s)
     :where
     (:bgp #(?s !rdf:type !<eh://fruityThing>) #(?s ?p ?o))
     :construct-pattern
     (#(?s !rdf:type !<eh://fruit>)
      #(?s ?p ?o)))

:targets list
  targets is used only by SPARQL DESCRIBE. It must be a list of IRIs (strings, UPIs or future-parts). This s-expression, for example, would be used to DESCRIBE two URIs:

      (sparql.parser:sparql :describe :vars nil :where (:bgp)
        :targets (!<http://www.example.com#one> !<http://www.example.com#two>)

#### in-line-data

The `in-line-data` parameter provides the data from the outermost `VALUES` expression. It consists of two lists. The first list is an ordered list of the variables involved and the second is a list of lists of the values for these variables. `nil` is used for an undefined value. For example, the values clause:

    values (?account ?order) { (undef 2) (3 4) }

would be represented by the s-expression:

    :in-line-data
     ((?account ?order)
      ((nil
        !"2"^^<http://www.w3.org/2001/XMLSchema#integer>)
       (!"3"^^<http://www.w3.org/2001/XMLSchema#integer>
        !"4"^^<http://www.w3.org/2001/XMLSchema#integer>)))

#### query-options

`query-options` is a association-list of values that control various switches during query execution. These include the query engine, whether or not to use Chunk-at-a-Time processing and more. The reference guide includes more information on the available query options.

#### vars

A list of the variables projected by the query. If omitted, then all variables will be returned in an unspecified order 3 .

#### default-base

Used as the default-base by the SPARQL `uri` function when constructing URIs.

#### where

The `where` clause describes the body of the query and is the most complex parameter. It is a tree of commands where each command is named by the head of its list and obtains its parameters from the tail. The commands are:

- :bgp triple-pattern
- :bind assignments query
- :exists left-query right-query
- :filter filter query
- :graph graph query
- :in-line-data in-line-data query
- :join left-query right-query filter
- :left-join left-query right-query filter
- :minus left-query right-query
- :not-exists left-query right-query
- :select &key vars distinct where order limit offset from group having bind aggregates
- :service &key host query-string silent
- :union left-query right-query

SPARQL queries work by obtaining bindings for variables and then combining these bindings in different ways. The `:bgp`, `:bind`, and `:in-line-data` clauses generate bindings and all the rest combine them or remove them from consideration.

:bgp triple-pattern
  The BGP (or basic graph pattern) form has a single argument which must be a list of arrays where each array has three elements corresponding to a subject, predicate, object pattern. Each element in the array can be a symbol denoting a variable, a gensym denoting a blank node or a UPI or future-part denoting a constant. For example:

      (:bgp
        #(?inproc !rdf:type !bench:Inproceedings)
        #(?inproc !dc:creator #:?bnode_0)
        #(?inproc !bench:booktitle ?booktitle)
        #(#:?bnode_0 !foaf:name ?name))

:bind assignments query
  The `:bind` form has two arguments: a list of assignments and the rest of the query. Each assignment in the `assignments` list is a list of two elements whose first element is the variable being assigned and whose second element is the expression to evaluate in order to make the assignment. For example:

      (:bind
        ((?b !"2"^^<http://www.w3.org/2001/XMLSchema#integer>)
         (?a !"1"^^<http://www.w3.org/2001/XMLSchema#integer>))
       (:bgp #(?d ?e ?f))))

query is evaluated before the assignments are made so the above would first evaluate the BGP and then bind `?b` to 2, `?a` to 1.

:in-line-data data query
  This is the in-query counterpart to the `:in-line-data` described above. The syntax is the same: it consists of two lists. The first is an ordered list of the variables involved and the second is a list of lists of the values for these variables. `nil` is used for an undefined value. For example, a query like

      select ((?total * 1.056) as ?markup) {
        values (?account ?order) { (undef 2) (3 4) }
        ?account :hasOrder ?order .
        ?order :hasTotal ?total .
      }

would produce an `:in-line-data` clause like this:

    (sparql.parser:sparql :select :vars (?markup) :where
     (:bind
      ((?markup
        (* ?total !"1.056"^^<http://www.w3.org/2001/XMLSchema#decimal>)))
      (:join
       (:join nil
        (:in-line-data :data
         ((?account ?order)
          ((nil !"2"^^<http://www.w3.org/2001/XMLSchema#integer>)
           (!"3"^^<http://www.w3.org/2001/XMLSchema#integer>
            !"4"^^<http://www.w3.org/2001/XMLSchema#integer>)))
         :rhs (:bgp)))
       (:bgp
        #(?account !<eh://hasOrder> ?order)
        #(?order !<eh://hasTotal> ?total)))))

In this case, the query portion of the `:in-line-data` clause is the empty `:bgp`. Note that the positioning of the `:in-line-data` clause is significant as it alters how the join is processed.

##### joining groups of bindings

You can combine two sets of bindings (created from other query terms) using `:join`, `:left-join` and `:union`. You can remove a set bindings based on the contents of another set using `:exists`, `:minus`, and `:not-exists`. In the descriptions below, left-query and right-query are sub-trees with the same structure as the main `:where` clause and filter is a filter expression.

:exists left-query right-query
  Evaluates left-query and right-query and keeps bindings in left-query if and only if there is a corresponding binding in right-query.
:join left-query right-query &optional filter
  join evaluates left-query and right-query and then joins the two results using the variables that the two sides have in common. If there are no common variables, then join becomes a cross-product (which is usually a bad thing) 4 . The optional filter is evaluated for each row produced by the join and can be used as short-hand for

      (:filter (:join A B) filter)

:left-join left-query right-query &optional filter
  left-join evaluates left-query and right-query and performs a left join using the optional filter to further remove results.
:minus left-query right-query
  Evaluates left-query and right-query and removes any bindings in left-query if and only if there is a corresponding binding in right-query.
:not-exists left-query right-query
  Evaluates left-query and right-query and keeps bindings in left-query if and only if there is not a corresponding binding in right-query.
:union left-query right-query
  union evaluates each query piece and joins then with the current set of bindings in turn.

##### Miscelaneous operations

:filter query filter
  The `:filter` clause removes bindings based on an expression. It first evaluates query and then iterates over the bindings applying the filter to each one.
:graph graph query
  The graph clause changes the current graph used during query evaluation and then evaluates query. graph can be a variable, an IRI (string, UPI or future-part), or one of the keywords: `:from`, `:from-named`, or `:default`. These last three mean:

  - `:from` - iterate over the graphs in the default-graph portion of the dataset. I.e., the ones that come from the `FROM` clauses in the SPARQL query.

  - `:from-named` - iterate over the named graphs in the dataset. I.e., the ones that come from the `FROM NAMED` clauses in the SPARQL query.

  - `:default` - use the current graph.

:select query
  `:select` is used for SPARQL 1.1 sub-query. The `:select` clause takes all of the arguments that the main SELECT query supports except for `:from` (sub-queries cannot change the dataset). For example, the (rather contrived) query:

      select * { { select * { ?a ?b ?c } order by ?c limit 1000 }}

is equivalent to the s-expression

    (sparql.parser:sparql :select
      :vars (?a ?b ?c)
      :where (:join nil
               (:select :vars (?a ?b ?c)
                  :where (:bgp #(?a ?b ?c))
                  :order ((:asc ?c))
                  :limit 1000)))

(In passing, note that the inner ORDER BY will be used to order the results of the sub-query before applying the limit but there is no guarantee that the final result set will retain the ordering).

:service &key host query-string silent
  The service clause sends the query-string to host and merges the result into the rest of the query. If there is an error and silent is false, then the entire query will fail. 5

### Update

A SPARQL update s-expression has the following keywords: `steps`, `default-base` and `query-options`.

As in the other SPARQL query forms, `default-base` is used by the SPARQL `uri` function when constructing URIs and `query-options` is a association-list of values that control various switches during query execution. These include the query engine, whether or not to use Chunk-at-a-time processing and more. They are described in more detail in the reference guide.

`steps` is a list of actions to take. Each action is a list whose head names the action and whose tail specifies any parameters. The actions are broken down into graph manipulation, data manipulation and loading from external data sources.

All of the non data-manipulation update steps use keyword arguments in their specification. They all have a silent argument which controls whether or not errors are signaled back to the caller.

#### Graph manipulation

The graph manipulation commands allow you to alter sets of triples contained in particular named graphs.

:add &key first second silent
  This is the add command.

      ADD ( SILENT )? ( ( GRAPH )? IRIref_from | DEFAULT) TO ( ( GRAPH )? IRIref_to | DEFAULT)

first and second correspond to `from` and `to` and can be an IRI or the keyword `:default`. Unless silent is true, `:add` will fail if the first graph is not present in the triple-store.

:clear &key graph silent
  This is the clear command.

      CLEAR ( SILENT )? (GRAPH IRIref | DEFAULT | NAMED | ALL )

graph can be an IRI (UPI or future-part) or one of keywords: `:default`, `:named` or `:all`. Obviously, care should be taken before executing this command! The `:clear` command cannot fail (i.e., it is not an error to clear a graph that does not exist) so the silent parameter is ignored.

:copy &key first second silent
  This is the copy command.

      COPY ( SILENT )? ( ( GRAPH )? IRIref_from | DEFAULT) TO ( ( GRAPH )? IRIref_to | DEFAULT )

first and second correspond to `from` and `to` and can be an IRI or the keyword `:default`. If the source graph does not exist, then the `:copy` command will fail unless silent is true.

:create &key graph silent
  This is the create command.

      CREATE ( SILENT )? GRAPH IRIref

Where graph must be an IRI (string, future-part or UPI). AllegroGraph does not record the existence of empty graphs so the create operation will never change the contents of a triple-store. If the graph already exists in the store, however, then it will fail (unless silent is specified).

:drop &key graph silent
  This is the drop command.

      DROP  ( SILENT )? (GRAPH IRIref | DEFAULT | NAMED | ALL )

graph can be an IRI (string, future-part or UPI) or one of the keywords `:default`, `:named`, or `:all`. Because AllegroGraph does not keep track of empty graphs, the `:drop` command is equivalent to the `:clear` command.

:move first second silent
  This is the move command.

      MOVE (SILENT)? ( ( GRAPH )? IRIref_from | DEFAULT) TO ( ( GRAPH )? IRIref_to | DEFAULT)

first and second correspond to `from` and `to` and can be an IRI or the keyword `:default`. As in `:copy`, if the source graph does not exist, then the command will fail unless silent is true.

#### Data manipulation

The data manipulation commands let you add and remove triples from the triple-store. Each uses quad-templates to specify what to add and remove. A quad-template is like a `bgp` (i.e., a list of arrays) extended with additional syntax to allow you to specify the graph(s) that should be altered. I.e., it looks like:

    (#(s1 p1 o1)
     #(s2 p2 o2)
     (:graph g1 (#(s3 p3 o3)))
     #(s4 p4 o4))

Note that the logic for determining the graph(s) to alter is complex and depends on graphs specified in the templates, the graphs specified in the USING or WITH clauses and various parameters passed to the run-sparql function. Please contact support if you need additional information.

:delete quad-template
  This corresponds to the DELETE DATA command. Note that quad-template needs to be in an additional list. The template used is not allowed to contain either variables nor blank nodes 6 .
:insert quad-template
  This corresponds to the INSERT DATA command. Note that quad-template needs to be in an additional list. Note that this template is not allowed to include variables 7 .
:modify &key graph delete insert where using
  This corresponds to the DELETE/INSERT command. The delete and insert parameters must be quad-templates (or `nil`). The where clause uses the same grammar as it does in the query language above.

  using is like from (above). I.e., it is a dataset-list and it should be a list of pairs where each pair has the `car` `:from` or `:from-named` and the `cdr` is the name of the graph to include in the dataset (either a UPI or a future-part).

The graph parameter represents the graph specified in the `WITH` clause of the SPARQL grammar.

#### Loading from external sources

:load &key from graph silent
  The `:load` command tries to read data from the URI from (which can be a `file://`, `http://`, or `https://` URI) and load it into the graph graph. graph can be `nil` or a URI. If it is nil, the data is loaded into the default-graph of the destination triple-store. The format of the data at the other end of the from URI is determined via its extension (treating it as if it names a file). If the type cannot be guessed, then RDf/XML is assumed. If there is a problem during loading, then an error will be signaled unless silent is true.

### Filter Summary

Filter expressions can be combined with the boolean operators `and` and `or` and negated with `not`. We support all of the functions in the SPARQL standard. We will augment this document with a complete listing in a future version. In the meantime, please contact support if you need additional information.

## Footnotes

1. The query can be further simplified which occurs during the query planning process. ↩
2. Note that the previously deprecated `:geo` clause has been removed. All SPARQL Geospatial queries must now be done using the 2D and nD magic properties. ↩
3. You can use the third return value of run-sparql to obtain a list of the variables in the order they occur in the result set. ↩
4. Cross products almost always indicate that a query is asking two different and unrelated things. Because there is no join, the answer will contain a row for every pair of elements on the right-hand side and the left-hand. ↩
5. Note that the service keyword requires the query to be specified as a string. At some point, AllegroGraph may allow s-expressions to be used here too. ↩
6. At this time the check for variables and blank nodes happens in the parser and the behavior is unspecified if templates using them are passed in as parameters to the delete data command. ↩
7. At this time the check for this happens in the parser and the behavior is unspecified if templates with variables are used ↩