Andy,

This is getting quite complicated compared to the QuerySolutionMap. I
get that we should rather be using alternatives, but you presented
several and neither is a drop-in replacement for the QuerySolutionMap.
I think Jena's documentation could use a section on this topic, with a
comparison of the approaches and some criteria which should be used
when, plus code examples.

I am trying to build a VALUES block now. My code:

    Map<String, RDFNode> subs = ...
    Query query = QueryFactory.create(wrapper.getQuery().toString());
// can't use wrapper.getQuery().cloneQuery() due to JENA-1935
    List<Var> variables = subs.entrySet().stream().map(e ->
Var.alloc(e.getKey())).collect(Collectors.toList());
    List<Binding> values = subs.entrySet().stream().map(e ->
BindingFactory.binding(Var.alloc(e.getKey()),
e.getValue().asNode())).collect(Collectors.toList());
    query.setValuesDataBlock(variables, values);

First of all I am wondering why a substitution map

    [( ?predicate = <http://spinrdf.org/spin#body> ), ( ?comment =
"the body of the Template" ), ( ?minCount = 0 ), ( ?maxCount = 1 )]

 produces the VALUES table of the form

VALUES ( ?predicate ?comment ?minCount ?maxCount ) {
  ( spin:body UNDEF UNDEF UNDEF )
  ( UNDEF "the body of the Template" UNDEF UNDEF )
  ( UNDEF UNDEF 0 UNDEF )
  ( UNDEF UNDEF UNDEF 1 )
}

and not simply

VALUES ( ?predicate ?comment ?minCount ?maxCount ) {
  ( spin:body "the body of the Template" 0 1 )
}


And then what do I do with expressions like FILTER (bound(?maxCount))
which are used in SPIN's internal queries to "switch off" graph
patterns (as I understand it) in the absence of the ?maxCount binding.
It does not seem to have the same effect with VALUES.




On Mon, Jul 6, 2020 at 11:28 AM Andy Seaborne <[email protected]> wrote:
>
> "bound()" is not a function in the strict sense.
>
> Another case where variables are necessary is "AS ?var"
>
> Long version:
> https://afs.github.io/substitute.html
>
> The advantage of using VALUES or BIND to inject the substitution value
> is that it preserves the original variable.
>
> Contrast the difference between these two:
>
>    QueryFactory.create("SELECT * { VALUES ?s {"+bn+"} ?s ?p ?o }");
>    QueryFactory.create("SELECT * { "+bn+" ?p ?o }");
>
> BIND(?minCount AS ?mc) FILTER bound(?mc)
>
>      Andy
>
> On 06/07/2020 09:56, Martynas Jusevičius wrote:
> > The same problem (invalid query string) using
> > QueryTransformOps.transformQuery().
> >
> > On Sun, Jul 5, 2020 at 11:34 PM Martynas Jusevičius
> > <[email protected]> wrote:
> >>
> >> ParameterizedSparqlString does not work either (not related to bnodes).
> >>
> >> With a mapping ?minCount => 0, it produces an invalid query string:
> >>
> >> SELECT  (count(*) AS ?cardinality)
> >>          WHERE
> >>            { <http://spinrdf.org/spin#Templates>
> >> <http://spinrdf.org/spin#body>  ?value
> >>              FILTER bound(0)
> >>            }
> >>          HAVING ( ?cardinality < 0 )
> >>
> >> On Sun, Jul 5, 2020 at 10:31 PM Andy Seaborne <[email protected]> wrote:
> >>>
> >>>
> >>>
> >>> On 05/07/2020 20:36, Martynas Jusevičius wrote:
> >>>> Thanks for the explanation.
> >>>>
> >>>> What do you mean with "inject" in 1/? ParameterizedSparqlString?
> >>>
> >>> initialBinding is done by making the starting condition for execution a
> >>> binding of the variables instead of the empty root. (This works nearly
> >>> always but is affected by variable name scopes - scopes, like subquery,
> >>> didn't exist when the mechanism was introduced.
> >>>
> >>> ParameterizedSparqlString builds which is parsed so you should be able
> >>> to put in <_:....>.
> >>>
> >>>       Andy
> >>>
> >>>>
> >>>> On Sun, Jul 5, 2020 at 8:53 PM Andy Seaborne <[email protected]> wrote:
> >>>>>
> >>>>> <_:label> is a syntax feature, not built into the storage or query
> >>>>> execution.
> >>>>>
> >>>>> model.createResource("_:" + id));
> >>>>>
> >>>>> creates a resource with a strange URI (which is actually illegal by RFC
> >>>>> 3986/7).
> >>>>>
> >>>>> There are various ways:
> >>>>>
> >>>>> 1/ The app can put the bnode into the QSM and injected at execution - it
> >>>>> won't become a bnode-variable because that happens in parsing.
> >>>>>
> >>>>> 2/ To put a concrete node into a query:
> >>>>>
> >>>>> String bn = "<_:" + id+">";
> >>>>>
> >>>>> then put string into SPARQL syntax:
> >>>>>
> >>>>> QueryFactory.create("SELECT * { VALUES ?s {"+bn+"} ?s ?p ?o }");
> >>>>> QueryFactory.create("SELECT * { "+bn+" ?p ?o }");
> >>>>>
> >>>>> 3/ rewrite the abstract syntax after parsing:
> >>>>>
> >>>>> Map<String, Resource> substitutions =
> >>>>>      Collections.singletonMap("s", bnode);
> >>>>> query = QueryFactory.create("SELECT * { ?s ?p ?o }");
> >>>>> query = QueryTransformOps.transformQuery(query, substitutions);
> >>>>>
> >>>>> Forms 2 and 3 have the advantage of not relying on how execution works -
> >>>>> QSMs are ingested inthe start of execution and e.g. aren't visible in
> >>>>> subqueries and also interact and bypass with query optimization.
> >>>>>
> >>>>>        Andy
> >>>>>
> >>>>> On 05/07/2020 15:22, Martynas Jusevičius wrote:
> >>>>>> Hi,
> >>>>>>
> >>>>>> I came across a situation where I want to carry over a blank node ID
> >>>>>> in a QuerySolutionMap to QueryExecution, to match exact blank node
> >>>>>> resources rather than have them as variables.
> >>>>>>
> >>>>>> I found an old thread by Holger on this topic:
> >>>>>> https://mail-archives.apache.org/mod_mbox/jena-users/201308.mbox/browser
> >>>>>>
> >>>>>> The suggestion was to use <_:LABEL> URI scheme for blank nodes.
> >>>>>> https://jena.apache.org/documentation/query/extension.html#blank-node-labels
> >>>>>>
> >>>>>> Based on that, I tried this logic:
> >>>>>>
> >>>>>>        if (instance.isURIResource()) qsm.add(SPIN.THIS_VAR_NAME, 
> >>>>>> instance);
> >>>>>>        if (instance.isAnon()) qsm.add(SPIN.THIS_VAR_NAME,
> >>>>>> model.createResource("_:" + instance.getId()));
> >>>>>>
> >>>>>> However I'm not getting the results I expect. So I decided to make an
> >>>>>> isolated test:
> >>>>>>
> >>>>>>        @Test
> >>>>>>        public void bnodeQueryTest()
> >>>>>>        {
> >>>>>>            Model model = ModelFactory.createDefaultModel();
> >>>>>>            Resource bnode = 
> >>>>>> model.createResource().addProperty(FOAF.name,
> >>>>>> "whateverest");
> >>>>>>            AnonId id = bnode.getId();
> >>>>>>
> >>>>>>            Query query = QueryFactory.create("SELECT * { ?s ?p ?o }");
> >>>>>>            QuerySolutionMap qsm = new QuerySolutionMap();
> >>>>>>            qsm.add("s", model.createResource("_:" + id));
> >>>>>>
> >>>>>>            try (QueryExecution qex = 
> >>>>>> QueryExecutionFactory.create(query,
> >>>>>> model, qsm))
> >>>>>>            {
> >>>>>>                ResultSet resultSet = qex.execSelect();
> >>>>>>
> >>>>>>                assertTrue(resultSet.hasNext());
> >>>>>>                assertEquals(id, 
> >>>>>> resultSet.next().get("s").asResource().getId());
> >>>>>>            }
> >>>>>>        }
> >>>>>>
> >>>>>> The test fails on assertTrue() because SELECT returns no results.
> >>>>>>
> >>>>>> Is the test flawed? Am I misunderstanding the use of this URI scheme?
> >>>>>> If not, what's the purpose if it cannot match blank nodes in data?
> >>>>>>
> >>>>>> Martynas
> >>>>>>

Reply via email to