blob: 6582d59c355e9ba01a1b74f8d0f535ba5933b035 [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<title>Apache Jena - Tutorial - Manipulating SPARQL using ARQ</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/css/bootstrap.min.css" rel="stylesheet" media="screen">
<link href="/css/bootstrap-icons.css" rel="stylesheet" media="screen"><link rel="stylesheet" type="text/css" href="https://jena.apache.org/sass/jena.1b17c39a117e22b46db4c66f6395dc27c134a60377d87d2d5745b8600eb69722.css" integrity="sha256-GxfDmhF&#43;IrRttMZvY5XcJ8E0pgN32H0tV0W4YA62lyI=">
<link rel="shortcut icon" href="/images/favicon.ico" />
</head>
<body>
<nav class="navbar navbar-expand-lg bg-body-tertiary" role="navigation">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<a class="navbar-brand" href="/index.html">
<img class="logo-menu" src="/images/jena-logo/jena-logo-notext-small.png" alt="jena logo">Apache Jena</a>
</div>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li id="homepage" class="nav-item"><a class="nav-link" href="/index.html"><span class="bi-house"></span> Home</a></li>
<li id="download" class="nav-item"><a class="nav-link" href="/download/index.cgi"><span class="bi-download"></span> Download</a></li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-journal"></span> Learn <b class="caret"></b></a>
<ul class="dropdown-menu">
<li class="dropdown-header">Tutorials</li>
<li><a class="dropdown-item" href="/tutorials/index.html">Overview</a></li>
<li><a class="dropdown-item" href="/documentation/fuseki2/index.html">Fuseki Triplestore</a></li>
<li><a class="dropdown-item" href="/documentation/notes/index.html">How-To's</a></li>
<li><a class="dropdown-item" href="/documentation/query/manipulating_sparql_using_arq.html">Manipulating SPARQL using ARQ</a></li>
<li><a class="dropdown-item" href="/tutorials/rdf_api.html">RDF core API tutorial</a></li>
<li><a class="dropdown-item" href="/tutorials/sparql.html">SPARQL tutorial</a></li>
<li><a class="dropdown-item" href="/tutorials/using_jena_with_eclipse.html">Using Jena with Eclipse</a></li>
<li class="dropdown-divider"></li>
<li class="dropdown-header">References</li>
<li><a class="dropdown-item" href="/documentation/index.html">Overview</a></li>
<li><a class="dropdown-item" href="/documentation/query/index.html">ARQ (SPARQL)</a></li>
<li><a class="dropdown-item" href="/documentation/io/">RDF I/O</a></li>
<li><a class="dropdown-item" href="/documentation/assembler/index.html">Assembler</a></li>
<li><a class="dropdown-item" href="/documentation/tools/index.html">Command-line tools</a></li>
<li><a class="dropdown-item" href="/documentation/rdfs/">Data with RDFS Inferencing</a></li>
<li><a class="dropdown-item" href="/documentation/geosparql/index.html">GeoSPARQL</a></li>
<li><a class="dropdown-item" href="/documentation/inference/index.html">Inference API</a></li>
<li><a class="dropdown-item" href="/documentation/ontology/">Ontology API</a></li>
<li><a class="dropdown-item" href="/documentation/permissions/index.html">Permissions</a></li>
<li><a class="dropdown-item" href="/documentation/extras/querybuilder/index.html">Query Builder</a></li>
<li><a class="dropdown-item" href="/documentation/rdf/index.html">RDF API</a></li>
<li><a class="dropdown-item" href="/documentation/rdfconnection/">RDF Connection - SPARQL API</a></li>
<li><a class="dropdown-item" href="/documentation/rdfstar/index.html">RDF-star</a></li>
<li><a class="dropdown-item" href="/documentation/shacl/index.html">SHACL</a></li>
<li><a class="dropdown-item" href="/documentation/shex/index.html">ShEx</a></li>
<li><a class="dropdown-item" href="/documentation/tdb/index.html">TDB</a></li>
<li><a class="dropdown-item" href="/documentation/tdb2/index.html">TDB2</a></li>
<li><a class="dropdown-item" href="/documentation/query/text-query.html">Text Search</a></li>
</ul>
</li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-journal-code"></span> Javadoc <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/documentation/javadoc.html">All Javadoc</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/arq/">ARQ</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/fuseki2/">Fuseki</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/geosparql/">GeoSPARQL</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/jena/">Jena Core</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/permissions/">Permissions</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/extras/querybuilder/">Query Builder</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/shacl/">SHACL</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/tdb/">TDB</a></li>
<li><a class="dropdown-item" href="/documentation/javadoc/text/">Text Search</a></li>
</ul>
</li>
</ul>
<form class="d-flex" role="search" action="/search" method="GET">
<div class="input-group">
<input class="form-control border-end-0 border m-0" type="search" name="q" id="search-query" placeholder="Search...." aria-label="Search" style="width: 10rem;">
<button class="btn btn-outline-secondary border-start-0 border" type="submit">
<i class="bi-search"></i>
</button>
</div>
</form>
<ul class="navbar-nav">
<li id="ask" class="nav-item"><a class="nav-link" href="/help_and_support/index.html" title="Ask"><span class="bi-patch-question"></span><span class="text-body d-none d-xxl-inline"> Ask</span></a></li>
<li class="nav-item dropdown">
<a href="#" title="Get involved" class="nav-link dropdown-toggle" role="button" data-bs-toggle="dropdown" aria-expanded="false"><span class="bi-megaphone"></span><span class="text-body d-none d-xxl-inline"> Get involved </span><b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="/getting_involved/index.html">Contribute</a></li>
<li><a class="dropdown-item" href="/help_and_support/bugs_and_suggestions.html">Report a bug</a></li>
<li class="dropdown-divider"></li>
<li class="dropdown-header">Project</li>
<li><a class="dropdown-item" href="/about_jena/about.html">About Jena</a></li>
<li><a class="dropdown-item" href="/about_jena/architecture.html">Architecture</a></li>
<li><a class="dropdown-item" href="/about_jena/citing.html">Citing</a></li>
<li><a class="dropdown-item" href="/about_jena/team.html">Project team</a></li>
<li><a class="dropdown-item" href="/about_jena/contributions.html">Related projects</a></li>
<li><a class="dropdown-item" href="/about_jena/roadmap.html">Roadmap</a></li>
<li><a class="dropdown-item" href="/about_jena/security-advisories.html">Security Advisories</a></li>
<li class="dropdown-divider"></li>
<li class="dropdown-header">ASF</li>
<li><a class="dropdown-item" href="https://www.apache.org/">Apache Software Foundation</a></li>
<li><a class="dropdown-item" href="https://www.apache.org/foundation/sponsorship.html">Become a Sponsor</a></li>
<li><a class="dropdown-item" href="https://www.apache.org/licenses/LICENSE-2.0">License</a></li>
<li><a class="dropdown-item" href="https://www.apache.org/security/">Security</a></li>
<li><a class="dropdown-item" href="https://www.apache.org/foundation/thanks.html">Thanks</a></li>
</ul>
</li>
<li class="nav-item" id="edit"><a class="nav-link" href="https://github.com/apache/jena-site/edit/main/source/documentation/query/manipulating_sparql_using_arq.md" title="Edit this page on GitHub"><span class="bi-pencil-square"></span><span class="text-body d-none d-xxl-inline"> Edit this page</span></a></li>
</ul>
</div>
</div>
</nav>
<div class="container">
<div class="row">
<div class="col-md-12">
<div id="breadcrumbs">
<ol class="breadcrumb mt-4 p-2 bg-body-tertiary">
<li class="breadcrumb-item"><a href='/documentation'>DOCUMENTATION</a></li>
<li class="breadcrumb-item"><a href='/documentation/query'>QUERY</a></li>
<li class="breadcrumb-item active">MANIPULATING SPARQL USING ARQ</li>
</ol>
</div>
<h1 class="title">Tutorial - Manipulating SPARQL using ARQ</h1>
<main class="d-flex flex-xl-row flex-column">
<aside class="text-muted align-self-start mb-3 p-0 d-xl-none d-block">
<h2 class="h6 sticky-top m-0 p-2 bg-body-tertiary">On this page</h2>
<nav id="TableOfContents">
<ul>
<li><a href="#inserting-values-simple-prepared-statements">Inserting values (simple prepared statements)</a></li>
<li><a href="#making-a-query-from-scratch">Making a Query from Scratch</a></li>
<li><a href="#navigating-and-tinkering-visitors">Navigating and Tinkering: Visitors</a></li>
<li><a href="#transforming-the-algebra">Transforming the Algebra</a></li>
</ul>
</nav>
</aside>
<article class="flex-column me-lg-4">
<p>When you&rsquo;ve been working with SPARQL you quickly find that static
queries are restrictive. Maybe you want to vary a value, perhaps add a
filter, alter the limit, etc etc. Being an impatient sort you dive in to
the query string, and it works. But what about <a href="http://xkcd.com/327/">little Bobby
Tables</a>? And, even if you
sanitise your inputs, string manipulation is a fraught process and
syntax errors await you. Although it might seem harder than string
munging, the ARQ API is your friend in the long run.</p>
<p><em>Originally published on the <a href="https://web.archive.org/web/20151107135044/http://researchrevealed.ilrt.bris.ac.uk/?p=35">Research Revealed project
blog</a></em></p>
<h2 id="inserting-values-simple-prepared-statements">Inserting values (simple prepared statements)</h2>
<p>Let&rsquo;s begin with something simple. Suppose we wanted to restrict the
following query to a particular person:</p>
<pre><code> select * { ?person &lt;http://xmlns.com/foaf/0.1/name&gt; ?name }
</code></pre>
<p><code>String#replaceAll</code> would work, but there is a safer way.
<code>QueryExecutionFactory</code> in most cases lets you supply a <code>QuerySolution</code>
with which you can prebind values.</p>
<pre><code> QuerySolutionMap initialBinding = new QuerySolutionMap();
initialBinding.add(&quot;name&quot;, personResource);
qe = QueryExecutionFactory.create(query, dataset, initialBinding);
</code></pre>
<p>This is often much simpler than the string equivalent since you don&rsquo;t
have to escape quotes in literals. (Beware that this doesn&rsquo;t work for
<code>sparqlService</code>, which is a great shame. It would be nice to spend some
time remedying that.)</p>
<h2 id="making-a-query-from-scratch">Making a Query from Scratch</h2>
<p>The previously mentioned limitation is due to the fact that prebinding
doesn&rsquo;t actually change the query at all, but the execution of that
query. So what how do we really alter queries?</p>
<p>ARQ provides two ways to work with queries: at the syntax level (<code>Query</code>
and <code>Element</code>), or the algebra level (<code>Op</code>). The distinction is clear in
filters:</p>
<pre><code> SELECT ?s { ?s &lt;http://example.com/val&gt; ?val . FILTER ( ?val &lt; 20 ) }
</code></pre>
<p>If you work at the syntax level you&rsquo;ll find that this looks (in pseudo
code) like:</p>
<pre><code> (GROUP (PATTERN ( ?s &lt;http://example.com/val&gt; ?val )) (FILTER ( &lt; ?val 20 ) ))
</code></pre>
<p>That is there&rsquo;s a group containing a triple pattern and a filter, just
as you see in the query. The algebra is different, and we can see it
using <code>arq.qparse --print op</code></p>
<pre><code> $ java arq.qparse --print op 'SELECT ?s { ?s &lt;http://example.com/val&gt; ?val . FILTER ( ?val &lt; 20 ) }'
(base &lt;file:///...&gt;
(project (?s)
(filter (&lt; ?val 20)
(bgp (triple ?s &lt;http://example.com/val&gt; ?val)))))
</code></pre>
<p>Here the filter contains the pattern, rather than sitting next to it.
This form makes it clear that the expression is filtering the pattern.</p>
<p>Let&rsquo;s create that query from scratch using ARQ. We begin with some
common pieces: the triple to match, and the expression for the filter.</p>
<pre><code> // ?s ?p ?o .
Triple pattern =
Triple.create(Var.alloc(&quot;s&quot;), Var.alloc(&quot;p&quot;), Var.alloc(&quot;o&quot;));
// ( ?s &lt; 20 )
Expr e = new E_LessThan(new ExprVar(&quot;s&quot;), new NodeValueInteger(20));
</code></pre>
<p><code>Triple</code> should be familiar from jena. <code>Var</code> is an extension of <code>Node</code>
for variables. <code>Expr</code> is the root interface for expressions, those
things that appear in <code>FILTER</code> and <code>LET</code>.</p>
<p>First the syntax route:</p>
<pre><code> ElementTriplesBlock block = new ElementTriplesBlock(); // Make a BGP
block.addTriple(pattern); // Add our pattern match
ElementFilter filter = new ElementFilter(e); // Make a filter matching the expression
ElementGroup body = new ElementGroup(); // Group our pattern match and filter
body.addElement(block);
body.addElement(filter);
Query q = QueryFactory.make();
q.setQueryPattern(body); // Set the body of the query to our group
q.setQuerySelectType(); // Make it a select query
q.addResultVar(&quot;s&quot;); // Select ?s
</code></pre>
<p>Now the algebra:</p>
<pre><code> Op op;
BasicPattern pat = new BasicPattern(); // Make a pattern
pat.add(pattern); // Add our pattern match
op = new OpBGP(pat); // Make a BGP from this pattern
op = OpFilter.filter(e, op); // Filter that pattern with our expression
op = new OpProject(op, Arrays.asList(Var.alloc(&quot;s&quot;))); // Reduce to just ?s
Query q = OpAsQuery.asQuery(op); // Convert to a query
q.setQuerySelectType(); // Make is a select query
</code></pre>
<p>Notice that the query form (<code>SELECT, CONSTRUCT, DESCRIBE, ASK</code>) isn&rsquo;t
part of the algebra, and we have to set this in the query (although
SELECT is the default). <code>FROM</code> and <code>FROM NAMED</code> are similarly absent.</p>
<h2 id="navigating-and-tinkering-visitors">Navigating and Tinkering: Visitors</h2>
<p>You can also look around the algebra and syntax using visitors. Start by
extending <code>OpVisitorBase</code> (<code>ElementVisitorBase</code>) which stubs out the
interface so you can concentrate on the parts of interest, then walk
using <code>OpWalker.walk(Op, OpVisitor)</code>
(<code>ElementWalker.walk(Element, ElementVisitor)</code>). These work bottom up.</p>
<p>For some alterations, like manipulating triple matches in place,
visitors will do the trick. They provide a simple way to get to the
right parts of the query, and you can alter the pattern backing BGPs in
both the algebra and syntax. Mutation isn&rsquo;t consistently available,
however, so don&rsquo;t depend on it.</p>
<h2 id="transforming-the-algebra">Transforming the Algebra</h2>
<p>So far there is no obvious advantage in using the algebra. The real
power is visible in transformers, which allow you to reorganise an
algebra completely. ARQ makes extensive use of transformations to
simplify and optimise query execution.</p>
<p>In Research Revealed I wrote some code to take a number of constraints
and produce a query. There were a number of ways to do this, but one way
I found was to generate ops from each constraint and join the results:</p>
<pre><code> for (Constraint con: cons) {
op = OpJoin.create(op, consToOp(cons)); // join
}
</code></pre>
<p>The result was a perfectly correct mess, which is only barely readable
with just three conditions:</p>
<pre><code> (join
(join
(filter (&lt; ?o0 20) (bgp (triple ?s &lt;urn:ex:prop0&gt; ?o0)))
(filter (&lt; ?o1 20) (bgp (triple ?s &lt;urn:ex:prop1&gt; ?o1))))
(filter (&lt; ?o2 20) (bgp (triple ?s &lt;urn:ex:prop2&gt; ?o2))))
</code></pre>
<p>Each of the constraints is a filter on a bgp. This can be made much more
readable by moving the filters out, and merging the triple patterns. We
can do this with the following <code>Transform</code>:</p>
<pre><code> class QueryCleaner extends TransformBase
{
@Override
public Op transform(OpJoin join, Op left, Op right) {
// Bail if not of the right form
if (!(left instanceof OpFilter &amp;&amp; right instanceof OpFilter)) return join;
OpFilter leftF = (OpFilter) left;
OpFilter rightF = (OpFilter) right;
// Add all of the triple matches to the LHS BGP
((OpBGP) leftF.getSubOp()).getPattern().addAll(((OpBGP) rightF.getSubOp()).getPattern());
// Add the RHS filter to the LHS
leftF.getExprs().addAll(rightF.getExprs());
return leftF;
}
}
...
op = Transformer.transform(new QueryCleaner(), op); // clean query
</code></pre>
<p>This looks for joins of the form:</p>
<pre><code> (join
(filter (exp1) (bgp1))
(filter (exp2) (bgp2)))
</code></pre>
<p>And replaces it with:</p>
<pre><code> (filter (exp1 &amp;&amp; exp2) (bgp1 &amp;&amp; bgp2))
</code></pre>
<p>As we go through the original query all joins are removed, and the
result is:</p>
<pre><code> (filter (exprlist (&lt; ?o0 20) (&lt; ?o1 20) (&lt; ?o2 20))
(bgp
(triple ?s &lt;urn:ex:prop0&gt; ?o0)
(triple ?s &lt;urn:ex:prop1&gt; ?o1)
(triple ?s &lt;urn:ex:prop2&gt; ?o2)
))
</code></pre>
<p>That completes this brief introduction. There is much more to ARQ, of
course, but hopefully you now have a taste for what it can do.</p>
</article>
<aside class="text-muted align-self-start mb-3 mb-xl-5 p-0 d-none d-xl-flex flex-column sticky-top">
<h2 class="h6 sticky-top m-0 p-2 bg-body-tertiary">On this page</h2>
<nav id="TableOfContents">
<ul>
<li><a href="#inserting-values-simple-prepared-statements">Inserting values (simple prepared statements)</a></li>
<li><a href="#making-a-query-from-scratch">Making a Query from Scratch</a></li>
<li><a href="#navigating-and-tinkering-visitors">Navigating and Tinkering: Visitors</a></li>
<li><a href="#transforming-the-algebra">Transforming the Algebra</a></li>
</ul>
</nav>
</aside>
</main>
</div>
</div>
</div>
<footer class="bd-footer py-4 py-md-5 mt-4 mt-lg-5 bg-body-tertiary">
<div class="container" style="font-size:80%" >
<p>
Copyright &copy; 2011&ndash;2024 The Apache Software Foundation, Licensed under the
<a href="https://www.apache.org/licenses/LICENSE-2.0">Apache License, Version 2.0</a>.
</p>
<p>
Apache Jena, Jena, the Apache Jena project logo, Apache and the Apache feather logos are trademarks of
The Apache Software Foundation.
<br/>
<a href="https://privacy.apache.org/policies/privacy-policy-public.html"
>Apache Software Foundation Privacy Policy</a>.
</p>
</div>
</footer>
<script src="/js/popper.min.js.js" type="text/javascript"></script>
<script src="/js/bootstrap.min.js" type="text/javascript"></script>
<script src="/js/improve.js" type="text/javascript"></script>
<script type="text/javascript">
(function() {
'use strict'
const links = document.querySelectorAll(`a[href="${window.location.pathname}"]`)
if (links !== undefined && links !== null) {
for (const link of links) {
link.classList.add('active')
let parentElement = link.parentElement
let count = 0
const levelsLimit = 4
while (['UL', 'LI'].includes(parentElement.tagName) && count <= levelsLimit) {
if (parentElement.tagName === 'LI') {
parentElement.querySelector('a:first-child').classList.add('active')
}
parentElement = parentElement.parentElement
count++
}
}
}
})()
</script>
</body>
</html>