- From: Scott Henninger <scotthenninger@gmail.com>
- Date: Wed, 15 Sep 2021 16:33:57 -0500
- To: "public-shacl@w3.org" <public-shacl@w3.org>
- Message-ID: <CAJ-KvVqLHbrGu00y0Z8w_8WR9VsQ1eW6+_nwZxBbfr=8Ehh9ZQ@mail.gmail.com>
Thank you for your considered ideas, Irene. Unfortunately, while I've asked for an enhancement for SHACL AF, in the meantime I must rely on SHACL alone. Also, the system is used by downstream systems and others. Therefore changing the class structure is not an option (that's a real-world constraint). I'll be more careful with my terminology. Thanks for the explanation. -- Scott On Wed, Sep 15, 2021 at 12:56 PM Irene Polikoff <irene@topquadrant.com> wrote: > Hi Scott, > > If I understand this correctly (and I am not sure I do), there are the > following options: > > 1. Use sh:or to say that either values of ex:parent come from members of > below classes and the additional constraints apply OR values of ex:parent > do not come from members of below classes. > > To make the expressions easier to understand and more maintainable, you > may consider creating a parent class ex:Cls and make ex:Cls1, ex:Cls2 and ex:Cls3 > its subclasses. Then, you would simply add or remove subclasses if your > model evolves. > > You could also create two untargeted node shapes - just to make it simpler > to understand e.g., > > my_sh:ClassShape > a sh:NodeShape ; > sh:targetClass ex:Cls1 ; . > sh:or ( ex:ParentsCls > > ex:ParentsNotCls) ; > > > ex:ParentsCls and ex:ParentsNotCls would be the two untargeted NodeShapes > > 2. Use SPARQL-based constraint components option to create a new > constraint component that would encapsulate your conditions and take as > parameters ex:Cls1, ex:Cls2 and ex:Cls3. > > 3. Use SPARQL-based targets to target only some members of ex:Cls1 - those > with only certain values in the ex:parent property > > This option is available only in the Advanced SHACL Features specification. > > As an aside, I wanted to comment on the terminology used because using the > right terminology is helpful in articulating requirements and solutions: > > 1. Everything in SHACL is either a target, shape or constraint > > 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. > > > Not quite. The fact that a value of ex:property is a member of a certain > class is a constraint > > 2. > > 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 is not a shape. It is data. > > I understand that you mean “this data is of valid shape” or "this data > would conform to the SHACL shapes I am needing to create”. However, in > SHACL, when you say “the following is a shape”, you are expected to be > referring to either a SHACL Node Shape or a SHACL Property Shape. > > The same applies to the word “target”. Data does not target anything. A > target can only be defined in a shape. A shape targets some data. > > > Hope this helps, > > Irene > > On Sep 14, 2021, at 11:44 PM, Scott Henninger <scotthenninger@gmail.com> > wrote: > > 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 21:34:24 UTC