- From: Scott Henninger <scotthenninger@gmail.com>
- Date: Tue, 14 Sep 2021 22:44:58 -0500
- To: "public-shacl@w3.org" <public-shacl@w3.org>
- Message-ID: <CAJ-KvVpSRdzCUCKt1hwbtXt0SbEUkmbDhVGNKZM9yzSAkHnB+A@mail.gmail.com>
Thanks for the detailed analysis, Ashley. It gave me some insights.
However, I'm guilty of not expressing the problem correctly. The main
requirements are that 1) the target is ex:Cls1 and 2) the ex:parent is 3)
one of n classes, which I have arbitrarily named ex:P_Cls{1, 2, 3}. But
these are intended not as constraints but as a prerequisite. I.e. if the
three are met, then there is a set of constraints that can be evaluated -
such as ex:Cls1 must have one ex:name property. Otherwise the constraints
are ignored.
So let me start over with some examples.
Case 1. Adding the constraint that the target class must have an ex:name,
the following is valid for both the class and property constraints.
ex:Child1
a ex:Cls1 ;
ex:parent ex:Parent1 ;
ex:name "John" .
ex:Parent1
a ex:P_cls1 .
Case 2. This is invalid because the ex:name is missing.
ex:Child1
a ex:Cls1 ;
ex:parent ex:Parent1 .
ex:Parent1
a ex:P_cls2 .
Case 3. This is valid because the parent is not in ex:P_Cls{1, 2, 3} and
therefore no shapes are applied.
ex:Child1
a ex:Cls1 ;
ex:parent ex:Parent1 .
ex:Parent1
a ex:P_cls_x .
That to me is the challenging part. I need to express that the three main
requirements are present before the other constraints can be validated.
I.e. the three main requirements (above 1), 2), 3) ) are prerequisites for
applying other shapes.
Here's an attempt that does some of this (and taking your advice not to
overthink). The only problem is that I do not want a violation on the
class defs.
my_sh:ClassShape
a sh:NodeShape ;
sh:targetClass ex:Cls1 ; .
sh:xone ( [sh:path ex:parent ; sh:class ex:ex:P_cls1 ]
[sh:path ex:parent ; sh:class ex:ex:P_cls2 ]
[sh:path ex:parent ; sh:class ex:ex:P_cls3 ]
) ;
sh:message "Should not be a violation" ;
sh:property my_sh:MustHaveAName .
my_sh:MustHaveAName
a sh:PropertyShape ;
sh:path ex:name ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:message "Instance of targeted class must have one and only one
ex:name" .
What I would want this to expressis that only Case 2 should cause a
violation.
Sorry for bungling the initial question (and thanks again for a thorough
response). I'm not entirely sure whether or how SHACL could satisfy this
kind of "prerequisite" constraint. The above fails, as it will throw a
violation if the parent is not one of ex:P_cls{1, 2, 3}. If anyone has
ideas on how to avoid this, I'm all ears.
Let me know if anything isn't clear or is incorrect (thanks for catching my
sh:not inside of sh:or mistake, BTW).
Thanks again for taking a look and helping out.
-- Scott
On Tue, Sep 14, 2021 at 6:44 PM Sommer, Ashley (L&W, Dutton Park) <
Ashley.Sommer@csiro.au> wrote:
> Hi Scott,
>
> I think you're over thinking this. From what I can tell, you have a simple
> requirement.
>
> Firstly, an obvious error: You have your sh:not constraint in your list of
> sh:or options.
> So you're effectively saying "class is P_cls1 OR P_cls2 OR P_cls3 OR NOT
> ex:P_clsInvalid_a" which as you can see is not logically consistent,
> because it would pass for *any* parent class that is
> NOT ex:P_clsInvalid_a.
> You instead need an AND in there, like "(class is P_cls1 OR P_cls2
> OR P_cls3) AND (NOT ex:P_clsInvalid_a)" like this:
> sh:and (
> [
> sh:or (
> [ sh:class ex:P_cls1 ]
> [ sh:class ex:P_cls2 ]
> [ sh:class ex:P_cls3 ]
> )
> ]
> [ sh:not [ sh:class ex:P_clsInvalid_a ] ]
> )
>
> Secondly, to match one and only one of those in the OR list, use
> exclusive-or logic (sh:xone in SHACL
> <https://www.w3.org/TR/shacl/#XoneConstraintComponent>). Like this:
> sh:xone (
> [ sh:class ex:P_cls1 ]
> [ sh:class ex:P_cls2 ]
> [ sh:class ex:P_cls3 ]
> ) ;
>
> Next, if you want to maintain a list of invalid options, you can use sh:in
> <https://www.w3.org/TR/shacl/#InConstraintComponent> wrapped in a sh:not,
> to make a "not in" constraint. Like this:
> sh:not [ sh:in ( ex:P_clsInvalid_a ex:P_clsInvalid_b ) ] ;
>
> But when you break it down that far, why do you need the NOT? Wouldn't
> just "(class is P_cls1 XOR P_cls2 XOR P_cls3)" do the trick? Anything
> with a parent not in that list will be invalid, why does there need to be
> invalid classes specified, unless those invalid class are subclasses of
> P_cls1, P_cls2, P_cls3 ?
> Or are there other unspecified parent classes which are not in the list of
> three possible required, but also not invalid? If that is the case, your
> requirements need to be restated like:
>
> - path ex:parent will have only one value
> - the value for ex:parent can have one or more types (classes)
> - one of those types needs to be {P_cls1 OR P_cls2 OR P_cls3}
> - maximum of one of those types can be {P_cls1, P_cls2, P_cls3}
> - parent's types cannot be one of {ex:P_clsInvalid_a,
> ex:P_clsInvalid_b}
>
> You can use qualifiedvalueshape to ensure that at least one of the parents
> match the OR constraint, not need to use sh:xone in this case.
> Note, to emulate sh:class semantics on value-taking shapes like
> qualifiedValueShape and sh:in, I am using the same transitive SHACL
> Properly Path as demonstrated in the sh:class docs.
> <https://www.w3.org/TR/shacl/#ClassConstraintComponent>
>
> So, if I were writing a shape to match the requirements above, it would
> look like this:
>
> my_sh:ParentTypeRestriction
> a sh:NodeShape ;
> sh:targetClass ex:Cls1 ;
> sh:property [
> sh:path ex:parent ;
> sh:minCount 1 ;
> sh:maxCount 1 ;
> sh:property [
> sh:path ( rdf:type [ sh:zeroOrMorePath rdfs:subClassOf ] ) ;
> sh:minCount 1 ;
> sh:qualifiedMinCount 1 ;
> sh:qualifiedMaxCount 1 ;
> sh:qualifiedValueShape [
> sh:or (
> [ sh:hasValue ex:P_cls1 ]
> [ sh:hasValue ex:P_cls2 ]
> [ sh:hasValue ex:P_cls3 ]
> )
> ] ;
> sh:not [ sh:in ( ex:P_clsInvalid_a ex:P_clsInvalid_b ) ] ;
> ] ;
> sh:message "parent type for ex:Cls1 is not in x:P_cls{1, 2, 3}" ;
> ] ;
> .
> (Tested in PySHACL, using the valid and invalid examples you gave in your
> correspondence).
>
> This may be overkill for your requirements, and I may have misinterpreted
> your use-case. But it should at least give you something to work with.
> It could be taken even further by doing regex on type names to match
> P_cls{1,2,3}, if there is a known pattern they match, but that might be
> going too far.
>
> - Ashley
>
> ------------------------------
> *From:* Scott Henninger <scotthenninger@gmail.com>
> *Sent:* Tuesday, 14 September 2021 3:24 PM
> *To:* public-shacl@w3.org <public-shacl@w3.org>
> *Subject:* SHACL Shape definition for including and excluding parent types
>
> I have a couple of scenarios that involve restrictions on classes. Most
> of them I can get working, but a couple I'm finding a bit difficult to
> figure out. To make it simple, I'll start with the parent type restriction
> I'm trying to express.
>
> The following would be a valid shape as it targets ex:Cls1, specifies a
> parent with ex:parent, and the parent has one of the types ex:P_cls{1, 2,
> 3}. (In all of these cases the targetClass is ex:Cls1).
> ex:Child1
> a ex:Cls1 ;
> ex:parent ex:Parent1 ;
> .
> ex:Parent1
> a ex:Pcls1 ;
> .
>
> This would be an invalid shape because ex:Pcls{1,2,3} types must be
> defined for the parent:
> ex:Child1
> a ex:Cls1 ;
> ex:parent ex:Parent1 ;
> .
> ex:Parent1
> a ex:P_clsInvalid_a ;
> .
>
> This one is valid because it includes one of ex:Pcls{1,2,3}:
> ex:Child1
> a ex:Cls1 ;
> ex:parent ex:Parent1 ;
> .
> ex:Parent1
> a ex:P_cls2, ex:P_clsInvalid_a ;
> .
>
> The fourth is invalid because only one of ex:Pcls{1, 2, 3} is allowed:
> ex:Child1
> a ex:Cls1 ;
> ex:parent ex:Parent1 ;
> .
> ex:Parent1
> a ex:P_cls1, ex:P_cls2;
> .
>
> If I could live with a list of disallowed types (for the sake of
> maintenance I'd rather say allow only ex:P_cls{1, 2, 3}) then the following
> seems to work:
> my_sh:ParentTypeRestriction
> a sh:NodeShape ;
> sh:targetClass ex:Cls1 ;
> sh:property[
> s h:path ex:parent ;
> sh:or (
> [ sh:class ex:P_cls1 ]
> [ sh:class ex:P_cls2 ]
> [ sh:class ex:P_cls3 ]
> [ sh:not [ sh:class ex:P_clsInvalid_a ] ]
> ) ;
> sh:message "parent type for ex:Cls1 is not in x:P_cls{1, 2, 3}" ;
> ] ;
> .
>
> ..where I include an exhaustive list for sh:not. As stated before, this
> isn't the best when the model changes and I need to find all the places
> where it needs to be excluded.
>
> So any ideas on how to 1) improve this shape, or 2) future-proof it for
> future additions to the rsf:type list?
>
> Thanks a bunch for taking a look.
> -- Scott
>
> Scott Henninger
> scotthenninger@gmail.com
>
Received on Wednesday, 15 September 2021 03:45:26 UTC