- From: Holger Knublauch <holger@topquadrant.com>
- Date: Thu, 16 Jun 2016 08:52:45 +1000
- To: public-data-shapes-wg@w3.org
The topic of macros only concerns the SPARQL extension mechanism, which is part of SHACL. Holger On 16/06/2016 1:29, Karen Coyle wrote: > I don't see how the topic of macros is relevant to what we are doing > here, which is, as I recall, developing a *language* for validation of > rdf graphs. SPARQL was to be used as the formalism for defining the > actual constraints described "to the extent possible" but at no time > did we decide that the group was producing the code for a SHACL > implementation nor that SHACL would be written in SPARQL. > > kc > > On 6/14/16 3:57 PM, Holger Knublauch wrote: >> I am not keen on macros - people should still be able to understand >> what's happening, and have valid SPARQL queries in front of them. >> However, since the introduction of path constraints we are already in >> the code injection business anyway, so we could just as well do it >> right. >> >> The boilerplate solution is basically a generalization of the templates >> used for ASK queries. We all share the goal of reusing code. At the same >> time, I am against limiting what people can express because the >> framework would only support certain patterns. For example there are >> cases where queries need to iterate over the same value set twice >> (comparing values), or to do existential checks instead of allValues >> iteration, see sh:hasValue. >> >> With all this said, here is what I believe may work well. I have not >> tried this out yet, so I may be missing something. >> >> As placeholder for the path, let's use a special variable $path that can >> only be used as predicate of triple patterns. Depending on the context, >> the engine would replace those triple patterns with suitable code. For >> node constraints, this path is interpreted as "the empty path starting >> at the focus node, i.e. the focus node itself". >> >> The standard placeholder line would be >> >> $this $path ?value . >> >> which for example would be replaced with >> >> $this ex:parent ?value . (property) >> ?value ex:parent $this . (inv property) >> $this ex:parent/ex:parent ?value . (path) >> BIND ($this AS ?value) . (node constraint) >> >> but these variations would also be allowed: >> >> $this $path ?value2 . >> >> which would allow binding more than one variable. And >> >> $this $path $hasValue . >> >> to do an existential match. (For node constraints this could become a >> FILTER sameTerm etc) >> >> Maybe even >> >> ?value $path $this . >> >> to walk the inverse direction. A nice thing about this syntax is that we >> only need to switch from $predicate to $path in our SPARQL snippets >> (section 4). >> >> We can take this further and generalize the special status of $path to >> apply to other path structures such as sh:equals, sh:lessThan. This >> would allow all kinds of paths to be used together with no extra costs >> to the implementation. Such constraint parameters could be marked, e.g. >> with sh:shape sh:Path . >> >> This pattern language may cover a sufficiently large set of use cases, >> and we could use that as a starting point until someone comes up with >> scenarios that cannot be expressed this way. As with everything related >> to the extension mechanism, it is very hard to anticipate all possible >> use cases, yet here I am quite optimistic that a good middle ground is >> found that supports code generalization without sacrificing >> expressivity. >> >> Holger >> >> >> On 10/06/2016 16:32, Dimitris Kontokostas wrote: >>> if we go this way, should we define something like a macro that >>> replaces [boilerplate] to the actual boilerplate? >>> It would make the queries easier to read and harder to make copy/paste >>> mistakes >>> something like %%BOILETPLATE%% or ##BOILETPLATE## >>> the first one makes the query invalid as a SPARQL query before we >>> replace the boilerplate code while the latter can be seen as a comment >>> but has the risk of being removed by mistake >>> >>> Dimitris >>> >>> On Fri, Jun 10, 2016 at 4:44 AM, Peter F. Patel-Schneider >>> <pfpschneider@gmail.com> wrote: >>> >>> The boilerplate is SPARQL code what with $this, $context, and >>> $predicate >>> pre-bound and ?value not in scope produces solutions for ?value as >>> the value >>> nodes. It can either bind ?subject and ?object as appropriate or >>> these can >>> be determined by the governing code, which is the solution used >>> here. A >>> suitable boilerplate that does not use anything beyond pre-binding >>> is given >>> here, but it would also be possible to do something more >>> sophisticated. >>> >>> [boilerplate] >>> >>> { $this $predicate ?value . >>> FILTER ( sameTerm($context,sh:PropertyConstraint) ) >>> } UNION { >>> ?value $predicate $this . >>> FILTER ( sameTerm($context,sh:InversePropertyConstraint) ) >>> } UNION { >>> BIND ( $this AS ?value ) >>> FILTER ( sameTerm($context,sh:NodeConstraint) ) >>> } >>> >>> The code for the core constraint components is then as follows: >>> >>> sh:class >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT EXISTS { ?value rdf:type/rdfs:subClassOf* $class } >>> } >>> >>> sh:classIn >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT EXISTS { >>> GRAPH $shapesGraph { $classIn (rdf:rest*)/rdf:first >>> ?class . } >>> ?value rdf:type/rdfs:subClassOf* ?class . >>> } >>> } >>> >>> sh:datatype >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT ( isLiteral(?value) && datatype(?value) = $datatype ) >>> } >>> >>> sh:datatypeIn >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT EXISTS { >>> GRAPH $shapesGraph { $classIn (rdf:rest*)/rdf:first >>> ?datatype . } >>> FILTER ( isLiteral(?value) && datatype(?value) = >>> ?datatype ) >>> } >>> } >>> >>> sh:maxExclusive >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER (?value < $maxExclusive) >>> } >>> >>> sh:maxInclusive is similar >>> sh:minExclusive is similar >>> sh:minInclusive is similar >>> >>> sh:in >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT EXISTS { >>> GRAPH $shapesGraph { $in (rdf:rest*)/rdf:first ?value . } >>> } >>> } >>> >>> sh:minLength >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT ( !isBlank(?value) && STRLEN(STR(?value)) >= >>> $minLength ) >>> } >>> >>> sh:maxLength is similar >>> >>> sh:nodeKind >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT >>> ((isIRI(?value) && $nodeKind IN (sh:IRI, sh:BlankNodeOrIRI, >>> sh:IRIOrLiteral) ) || >>> (isLiteral(?value) && $nodeKind IN ( sh:Literal, >>> sh:BlankNodeOrLiteral, >>> sh:IRIOrLiteral)) || >>> (isBlank(?value) && $nodeKind IN ( sh:BlankNode, >>> sh:BlankNodeOrLiteral, >>> sh:BlankNodeOrLiteral))) >>> } >>> >>> sh:pattern >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT (!isBlank(?value) && IF(bound($flags), >>> regex(str(?value), $pattern, >>> $flags), >>> regex(str(?value), $pattern))) >>> } >>> >>> sh:stem >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT (isIRI(?value) && STRSTARTS(str(?value), $stem)) >>> } >>> >>> sh:shape >>> >>> SELECT $this ?value ?failure WHERE { >>> [boilerplate] >>> BIND (sh:hasShape(?value, $shape, $shapesGraph) AS ?hasShape) . >>> BIND (!bound(?hasShape) AS ?failure) . >>> FILTER (?failure || !?hasShape) . >>> } >>> >>> sh:hasValue >>> >>> SELECT $this ?value WHERE { >>> FILTER NOT EXISTS { >>> [boilerplate] >>> FILTER (sameTerm(?value,$hasValue) ) >>> } >>> >>> sh:maxCount >>> >>> SELECT SAMPLE($this) WHERE { >>> [boilerplate] >>> } HAVING ( COUNT ( DISTINCT ?value ) > $maxCount ) >>> >>> sh:minCount is similar >>> >>> sh:equals >>> >>> SELECT $this ?value WHERE { >>> { >>> [boilerplate] >>> MINUS { $this $equals ?value . } >>> } UNION { >>> $this $equals ?value . >>> MINUS { [boilerplate] } >>> } >>> } >>> >>> sh:disjoint >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> $this $disjoint ?value . >>> } >>> >>> sh:lessThan >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> $this $lessThan ?value2 . >>> FILTER (!(?value < ?value2)) >>> } >>> >>> sh:lessThanOrEquals is similar >>> >>> sh:uniqueLang >>> >>> SELECT SAMPLE($this) ?lang WHERE { >>> { FILTER ($uniqueLang) } >>> [boilerplate] >>> BIND (lang(?value) AS ?lang) >>> FILTER (isLiteral(?value) && bound(?lang) && ?lang != "") >>> } GROUP BY ?lang HAVING ( COUNT(?this) > 1 ) >>> >>> sh:qualifiedMaxCount >>> >>> SELECT SAMPLE($this) ( SUM(?failed)>0 AS ?failure ) WHERE { >>> [boilerplate] >>> BIND (sh:hasShape(?value, $qualifiedValueShape, $shapesGraph) >>> AS ?hasShape) >>> BIND (!bound(?hasShape) AS ?failure) >>> BIND ( IF(?failure,1,0) AS ?failed ) >>> FILTER ( IF(?failure, true, ?hasShape) ) >>> } HAVING ( ( COUNT ( DISTINCT ?value ) > $qualifiedMaxCount ) || >>> ( SUM(?failed) > 0 ) ) >>> >>> sh:qualifiedMinCount is similar >>> >>> sh:closed >>> >>> SELECT $this ?value WHERE { >>> { FILTER ($closed) } >>> [boilerplate] >>> ?value ?predicate ?object . >>> FILTER (NOT EXISTS { >>> GRAPH $shapesGraph { $currentShape sh:property/sh:predicate >>> ?predicate . } >>> } && >>> ( !bound($ignoredProperties) || >>> NOT EXISTS { >>> GRAPH $shapesGraph { $ignoredProperties >>> rdf:rest*/rdf:first ?predicate . } >>> } >>> ) ) >>> } >>> >>> sh:not >>> >>> SELECT $this ?value ?failure WHERE { >>> [boilerplate] >>> BIND (sh:hasShape(?value, $not, $shapesGraph) AS ?hasShape) . >>> BIND (!bound(?hasShape) AS ?failure) . >>> FILTER (?failure || ?hasShape) . >>> } >>> >>> sh:and >>> >>> SELECT $this ?value ?failure WHERE { >>> [boilerplate] >>> GRAPH $shapesGraph { $and (rdf:rest*)/rdf:first ?conjunct . } >>> BIND (sh:hasShape(?value, ?conjunct, $shapesGraph) AS ?hasShape) >>> BIND (!bound(?hasShape) AS ?failure) >>> FILTER (?failure || !?hasShape) >>> } >>> >>> sh:or >>> >>> SELECT $this ?value WHERE { >>> [boilerplate] >>> FILTER NOT EXISTS { >>> GRAPH $shapesGraph { $or (rdf:rest*)/rdf:first ?disjunct . } >>> BIND (sh:hasShape(?value, ?disjunct, $shapesGraph) AS >>> ?hasShape) >>> BIND (!bound(?hasShape) AS ?failure) >>> FILTER ( !?failure || ?hasShape ) >>> } >>> } >>> >>> >>> peter >>> >>> >>> >>> >>> -- >>> Dimitris Kontokostas >>> Department of Computer Science, University of Leipzig & DBpedia >>> Association >>> Projects: http://dbpedia.org, http://rdfunit.aksw.org, >>> http://aligned-project.eu >>> Homepage: http://aksw.org/DimitrisKontokostas >>> Research Group: AKSW/KILT http://aksw.org/Groups/KILT >>> >> >
Received on Wednesday, 15 June 2016 22:53:19 UTC