SPARQL: construct, ask, describe and federation¶
The three chapters before this one have used select, which returns a table. The other
three query forms return something else, and one of them — construct — is the
reason SPARQL is a transformation language and not only a query language.
This chapter also covers federation: querying data that lives on someone else's server as if it were your own.
It assumes SPARQL: the basics and graph patterns.
construct: a query that returns triples¶
construct matches a pattern like any other query, but instead of filling a
table it builds triples. Because its output is a graph, that output can be loaded
straight back into a dataset. Matching and writing use the same language.
There are two kinds.
construct where returns exactly what it matched. The graph that comes out
has the same shape as the graph that went in:
prefix pokemon: <https://triplydb.com/academy/pokemon/id/pokemon/>
construct where {
pokemon:pikachu ?p ?o.
}
That returns every direct property of Pikachu. Reach deeper by adding patterns — here, following into the audio resource as well:
prefix pokemon: <https://triplydb.com/academy/pokemon/id/pokemon/>
prefix schema: <http://schema.org/>
construct where {
pokemon:pikachu
?p ?o;
schema:audio ?audio.
?audio ?y ?z.
}
A templated construct separates the two graphs. The where clause says
what to match; the construct clause says what to build. They need not look
alike, which is what makes this a translation:
prefix species: <https://triplydb.com/academy/pokemon/id/species/>
prefix vocab: <https://triplydb.com/academy/pokemon/vocab/>
construct {
?pokemon a ?species.
}
where {
?pokemon vocab:species ?species.
filter(?species in (species:dragon, species:mouse))
}
The source data relates a Pokémon to its species through vocab:species. The
output states the same fact as a class membership. Nothing was edited; a new
shape was derived from an old one.
This is the mechanism behind most data migration in linked data. When a model
changes, you write a construct that reads the old shape and emits the new one.
ask: a yes or no¶
ask returns true or false: does anything match this pattern? It does not
return the matches themselves, which makes it cheap.
Use it for checks. "Does any product lack a manufacturer?" is an ask query, and
a scheduled ask is a serviceable data quality alarm.
For structural quality checking at scale, SHACL is the better tool.
describe: triples about a resource¶
describe takes an IRI and returns triples about it. It is the quickest way to
find out what a dataset holds about something.
The catch is that what describe returns is not standardised. Every
implementation decides how much of the surrounding graph to include, and that is
intentional — the form exists as a lookup for IRIs you cannot otherwise resolve.
That matters as soon as you build on it. When you need to be certain which
triples come back, use construct, which states exactly that.
And if you are reaching for describe to resolve an IRI, note that a plain HTTP
request already does that when the IRI is set up properly:
curl -H "Accept: application/n-triples" -L https://triplydb.com/academy/pokemon/id/pokemon/pikachu
Which is a reason to make your own IRIs resolvable.
Changing stored data: SPARQL Update¶
construct returns a graph; it does not change one. SPARQL Update is the
separate language for that, with insert data, delete data and
delete/insert … where.
base <https://triplydb.com/>
insert data { <john> <knows> <mary>. }
Keep the distinction clear: construct derives, Update mutates. Where both are
available, construct is usually the safer of the two because it leaves the source
untouched.
Federation: querying someone else's data¶
A federated query runs partly on your endpoint and partly on another. The
service keyword marks the part that travels:
prefix owl: <http://www.w3.org/2002/07/owl#>
prefix pokemon: <https://triplydb.com/academy/pokemon/id/pokemon/>
construct {
pokemon:pikachu ?p ?o.
}
where {
pokemon:pikachu owl:sameAs ?dbpediaResource.
service <https://dbpedia.org/sparql> {
?dbpediaResource ?p ?o.
filter(isLiteral(?o))
}
}
The precondition is easy to miss: the two endpoints must share at least one
term. Without an IRI or literal in common there is nothing to join on. In
practice you create that overlap deliberately with owl:sameAs, which is exactly
the "link" in linked data.
For a sense of what is reachable this way, see the Linked Open Data Cloud — every dot is a queryable dataset.
Two cautions before you build on federation. A federated query is only as available as the slowest endpoint in it, and remote endpoints impose their own result limits. Treat it as a way to explore and to prototype integrations, not as a substitute for loading data you depend on.
Where this appears in Triply products¶
- Saved queries turn a
constructquery into an API that returns triples rather than a table. See Saved queries.
Where to next¶
- Querying real-world data — hierarchies, geodata and statistics
- SHACL — checking data quality properly
- Aggregation — back one chapter
- SPARQL: the basics — back to the first chapter
- Glossary — every term on this page, in one place