Thanks, this seems to do the trick:
List<Var> variables = subs.entrySet().stream().map(e ->
Var.alloc(e.getKey())).collect(Collectors.toList());
BindingMap bindingMap = BindingFactory.create();
subs.entrySet().stream().forEach(e ->
bindingMap.add(Var.alloc(e.getKey()), e.getValue().asNode()));
List<Binding> values = Arrays.asList(bindingMap);
query.setValuesDataBlock(variables, values);
On Mon, Jul 6, 2020 at 3:45 PM Andy Seaborne <[email protected]> wrote:
>
>
>
> On 06/07/2020 11:56, Martynas Jusevičius wrote:
> > 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());
>
> That produces a new binding for key in the map, which is why you the stripe.
>
> Create one binding, add each key/value to the binding.
>
> > 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
> >>>>>>>>