I once started with query containment for a caching system, but in the
end I settled for the simpler and more tractable approach the led to
development of the Service Enhancer [1]:
Instead of the system trying to guess what might be worth caching, the
user declares it explicitly with a SERVICE <cache:> { } block.
So as an idea, the same concept could be adapted to your use case:
You could use the SERVICE block to request authorization of the given
pattern:
SELECT ?o {
{ SERVICE <authorize:> { # Request authorization to execute the
following pattern:
SELECT ?s ?o {
?s <foo> ?o
}
} }
FILTER(regex(?s), 'foo') # Post-filtering outside of the
authorization request.
}
From there one, all operations become quite straight forward:
If you want a standard SPARQL query back, you can easily get rid of
those SERVICE block using an ElementTransformBase subclass that returns
the body for SERVICE <authorize:> {} patterns.
You can then also visitors (Element or Op) to reject queries based on
the used features.
For example, you may want to reject any triple pattern / quad that
appears outside of an authorize block.
You could also reject queries with more than 1 authorize block (no joins
allowed).
You could easily extend authorize to allow for structural equivalence:
Iterate the mentions of variables in order, rename each variable to a
fresh one based on the ordinal of its first mention.
So the pattern above would be normalized to:
SERVICE <authorize:> {
SELECT ?v1 ?v2 {
?v1 <foo> ?v2
}
}
This way you can check authorization based on the canonical variable
naming, without having to resort to checking semantic equivalence of
algebra expressions.
Because trying to match two queries with query containment is quite hard
to make it work in a smart way.
Should a different order of UNION members still match? What about these
cases:
{ BIND(:o AS ?o) ?s ?p ?o }
{ ?s ?p ?o FILTER(?o = :o) }
{ ?s ?p :o }
{ ?s ?p ?o FILTER(sameTerm(?o, :o)) }
{ ?s ?p ?o FILTER(?o = :o || ?o = :x) }
{ ?s ?p ?o FILTER(?o IN (:o, :x)) }
Related: Stardog supports "stored queries" - which is essentially a map
from name to query. SERVICE <query://NAME> can be used to "transclude"
stored queries.
A small custom vocab allows to map variables. See [2].
SELECT (COUNT(*) AS ?c) {
SERVICE <query://salaries> { [] sqs:var:department ?d }
FILTER (?d NOT IN (:marketing))
}
Cheers,
Claus
[1] https://jena.apache.org/documentation/query/service_enhancer.html
[2] https://docs.stardog.com/query-stardog/stored-query-service
On 3/13/26 10:25, Martynas Jusevičius wrote:
I want to do this at the algebra level :) The use case is a DDoS protection
request filter that only allows "instantiations" of pre-defined query
templates.
On Fri, Mar 6, 2026 at 12:53 AM Justin Dowdy <[email protected]> wrote:
It seems like one query is "isomorphic" to another query if and only if
they produce the same bindings when evaluated against all possible data
sets.
If you want to do this in the general case you might need a way to simulate
all possible data sets then evaluate the queries against them, right?
On Thu, Mar 5, 2026, 2:37 PM Martynas Jusevičius <[email protected]>
wrote:
Hi,
What would be the way to check programatically using Jena that
{ <http://localhost/> rdf:type ?x }
is a substitution of a binding (?s = <http://localhost/>) or VALUES (?s)
{ <
http://localhost/> }
on
{ ?s rdf:type ?type }
but
{ <http://localhost/> another:property ?x }
is not?
Basically check if a query is "isomorphic" to a given query template?
Claude calls this "pattern containment" or "query subsumption".
Martynas