- 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