Dan Kennedy
Dan Kennedy

Reputation: 101

Should I subclass rdf:Statement for reification?

I want to make a reified 'statement about a statement', say I have userX memberOf groupY and want to make a statement about that (say, that they joined on May 11).

So, I have something like:

statementX a rdf:statement
statementX subject userX
statementX predicate memberOf
statementX object groupY
statementX since "2022-05-11T11:32:52"^^xsd:dateTime

My question is, is it worthwhile subclassing rdf:statement? Say UserGroupStatement rdf:subClassOf rdf:statement, then statementX a UserGroupStatement.

Does that make sense, is it something people do? Or do people just use rdf:statement, or create their own joining classes? What would be the pros and cons?

In my thinking, it would at least allow me to model that a certain type of statement has certain properties, e.g. that a UserGroupStatement has a 'since' property (domain UserGroupStatement, range xsd:datetime). But then I can see that it doesn't help me specify anything else, because the subject/predicate/object of the UserGroupStatement could still be any Resource. Or for modelling purposes should I just make a new statement-like linking object, and forget about rdf:statement altogether?

Upvotes: 2

Views: 109

Answers (1)

Konrad Höffner
Konrad Höffner

Reputation: 12207

If it suits your use case, you can subclass rdf:Statement but be careful about the capital S, as URIs are case sensitive.

You could then use OWL to restrict the properties on that subclass further but that would help you only with inference. I assume you want to validate your data, for that use case SHACL or ShEx are better suited.

SHACL Example

@prefix : <http://example.org/>.
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>.
@prefix sh: <http://www.w3.org/ns/shacl#>.
    
# Ontology
:UserStatement rdfs:subClassOf rdf:Statement.

## Knowledge Base
:GroupY a :Group.
:UserX a :User; :memberOf :GroupY.
:AdminX a :Admin; :memberOf :GroupY.    

:CorrectStatement a :UserStatement;
    rdf:subject :UserX;
    rdf:predicate :memberOf;
    rdf:object :GroupY;
    :since "2022-05-11T11:32:52"^^xsd:dateTime.

:IncorrectStatement1 a :UserStatement;
    rdf:subject :AdminX;
    rdf:predicate :memberOf;
    rdf:object :GroupY;
    :since "2022-05-11T11:32:52"^^xsd:dateTime.

:IncorrectStatement2 a :UserStatement;
    rdf:subject :UserX;
    rdf:predicate :schmemberOf;
    rdf:object :GroupY;
    :since "2022-05-11T11:32:52"^^xsd:dateTime.

:InorrectStatement3 a :UserStatement;
    rdf:subject :UserX;
    rdf:predicate :memberOf;
    rdf:object :GroupY.

# SHACL Shape
:UserStatementShape a sh:NodeShape;
    sh:targetClass :UserStatement;  
    sh:property                                       
        [sh:path rdf:type; sh:hasValue :UserStatement; sh:minCount 1; sh:maxCount 1],
        [sh:path rdf:subject; sh:class :User; sh:minCount 1; sh:maxCount 1],
        [sh:path rdf:predicate; sh:hasValue :memberOf; sh:minCount 1; sh:maxCount 1],
        [sh:path rdf:object; sh:class :Group; sh:minCount 1; sh:maxCount 1],
        [sh:path :since; sh:nodeKind sh:Literal; sh:datatype xsd:dateTime; sh:minCount 1; sh:maxCount 1];
    sh:closed true.

Output

Save the example as test.ttl, install pySHACL and run pyshacl test.ttl and you will get the following:

$ pyshacl test.ttl
Validation Report
Conforms: False
Results (3):
Constraint Violation in MinCountConstraintComponent (http://www.w3.org/ns/shacl#MinCountConstraintComponent):
    Severity: sh:Violation
    Source Shape: [ sh:datatype xsd:dateTime ; sh:maxCount Literal("1", datatype=xsd:integer) ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:nodeKind sh:Literal ; sh:path :since ]
    Focus Node: :InorrectStatement3
    Result Path: :since
    Message: Less than 1 values on :InorrectStatement3->:since
Constraint Violation in HasValueConstraintComponent (http://www.w3.org/ns/shacl#HasValueConstraintComponent):
    Severity: sh:Violation
    Source Shape: [ sh:hasValue :memberOf ; sh:maxCount Literal("1", datatype=xsd:integer) ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:path rdf:predicate ]
    Focus Node: :IncorrectStatement2
    Result Path: rdf:predicate
    Message: Node :IncorrectStatement2->rdf:predicate does not contain a value in the set: [':memberOf']
Constraint Violation in ClassConstraintComponent (http://www.w3.org/ns/shacl#ClassConstraintComponent):
    Severity: sh:Violation
    Source Shape: [ sh:class :User ; sh:maxCount Literal("1", datatype=xsd:integer) ; sh:minCount Literal("1", datatype=xsd:integer) ; sh:path rdf:subject ]
    Focus Node: :IncorrectStatement1
    Value Node: :AdminX
    Result Path: rdf:subject
    Message: Value does not have class :User

Upvotes: 1

Related Questions