Missed the BIND(?minCount AS ?mc) FILTER bound(?mc) suggestion, will try it now.
On Mon, Jul 6, 2020 at 12:56 PM Martynas Jusevičius <[email protected]> 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()); > 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 > > >>>>>>
