SHACL Rule Execution on New Instances?

334 Views Asked by At

I have an application wherein I used SPIN constructors as a means to implement Event/Condition/Action (ECA) policies. ECA is one classic construct for policies. The Event, in this case, was always the assertion of a class on an individual which caused that class's SPIN constructors to run on that new individual. The Condition was the pattern expressed in my SPARQL WHERE clause, and the Action was provided by assertions in a SPARQL CONSTRUCT clause. My understanding is that the SPIN constructor would only run on the new individual of the class, not on all individuals of the class.

I'm now looking into switching to SHACL as the successor to SPIN. I'm developing in TopBraid Composer Maestro Edition, and I could implement using the Jena SHACL API.

Suppose that I express an ECA policy as a SHACL shape and target a class via the sh:targetClass SHACL predicate. Let's say my target class is family:Person. Every time I assert a new family:Person individual, I'd like to run my ECA policy (expressed as a SHACL shape) on only that new individual. I'm aware that a listener could be used to sense new class membership. I'm also aware that methods such as RuleUtil.executeRules() could be used once a change is sensed to execute all rules on all targeted nodes (individuals of the family:Person class, in my example). But, is there a way to apply SHACL shapes to only the new individuals of a targeted class? In my application, individuals would be accumulated over time and could become quite numerous. I'm concerned that the computational load of running shapes repeatedly against the same, old, unaltered individuals would become significant.

2

There are 2 best solutions below

2
On

The concept of "new individuals" sounds application-specific and depends on the execution logic. Nobody forces anyone to use sh:targetClass neither is it required to run all rules all the time. You can control this on API level. Instead of sh:targetClass, you could use some other property of your choice such as ex:constructedClass and implement a Java-based function that takes the new instances as input and follows the property to find all applicable shapes. If you think there is a generic pattern here, we could add them to a de-facto extension namespace such as dash:

0
On

A possible way to solve your problem is to use a "third-party" relation to mark the individuals that have been processed by the rule, and use such marking in a sh:condition referenced by the rule.

You would obtain something like that:

:MyCondition
    a sh:NodeShape ;
    rdfs:comment "The condition preventing re-application of the rule";
    sh:targetClass :MyClass ;
    sh:sparql [
        sh:select """
        PREFIX : <http://example.com/ns#>
        PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
        SELECT $this
        WHERE {
            # Here, the $this will be the focus node of the target class
            $this :hasMarking "true"^^xsd:boolean.
        }
        """;
    ].

:MyRule
    a sh:NodeShape ;
    rdfs:comment "The rule should be applied only once";
    sh:targetClass :MyClass ;
    sh:rule [
        a sh:SPARQLRule ;
        sh:condition :MyCondition ;
        sh:construct """
            PREFIX : <http://example.com/ns#>

            CONSTRUCT {
                $this :hasMarking "true"^^xsd:boolean.
            } WHERE {
                $this :property :object.
                # Here, the body of the rule
                # ...
            }
        """;
    ].

In this case, the marking is based on a simple boolean property, but it may also be based on a more useful information captured by the rule body, and representative of the rule result, i.e. $this :property :Object, where property is only inferred by your rule.