cancel
Showing results for 
Search instead for 
Did you mean: 

Head's Up! Site migration is underway. Phase 2: migrate recent content

SHACL validation when a relationship leads to a node of one of two specified types

nataliazon
Node

I have the following graph in neo4j:

CREATE (banana:Fruit {name:'Banana'})
CREATE (apple:Fruit {name:'Apple'})

CREATE (fruit_salad:Dish {name:'Fruit Salad'})
CREATE (banana_gun:Weapon {name:'Banana Gun'})

CREATE
(apple)-[:can_be_used_in]->(fruit_salad),
(banana)-[:can_be_used_in]->(fruit_salad),
(banana)-[:can_be_used_in]->(banana_gun)

 

I am trying to write a SHACL schema for validating this graph.

The tricky part here is that the relationship "can_be_used_in" can lead from Fruit to either Dish or Weapon type nodes.

I don't want to allow for any other relationships from the Fruit type, so I am using "sh:closed true ;".

I suspect what I need can be somehow done via the sh:or or sh:xone blocks.

I attempted this (the below is a Cypher query for loading the schema):

call n10s.validation.shacl.import.inline('

@prefix neo4j: <neo4j://graph.schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

# ---- Fruit -------------

neo4j:FruitShape a sh:NodeShape ;
  sh:targetClass neo4j:Fruit ;
  sh:closed true ; 
  sh:property [  
    sh:path neo4j:name ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:datatype xsd:string ;
   ];

    sh:or(
      sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:class neo4j:Weapon ;
      ] 
      sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:class neo4j:Dish ;
      ] 
    );
.
','Turtle')

However this gives me the following output (failed validation):

[{'focusNode': 552,
  'nodeType': 'Fruit',
  'shapeId': 'neo4j://graph.schema#FruitShape',
  'propertyShape': 'http://www.w3.org/ns/shacl#ClosedConstraintComponent',
  'offendingValue': '555, 554',
  'resultPath': 'can_be_used_in',
  'severity': 'http://www.w3.org/ns/shacl#Violation',
  'resultMessage': 'Closed type definition does not include this property/relationship'},
 {'focusNode': 553,
  'nodeType': 'Fruit',
  'shapeId': 'neo4j://graph.schema#FruitShape',
  'propertyShape': 'http://www.w3.org/ns/shacl#ClosedConstraintComponent',
  'offendingValue': '554',
  'resultPath': 'can_be_used_in',
  'severity': 'http://www.w3.org/ns/shacl#Violation',
  'resultMessage': 'Closed type definition does not include this property/relationship'}]

I also attempted the following:

call n10s.validation.shacl.import.inline('

@prefix neo4j: <neo4j://graph.schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

# ---- Fruit -------------

neo4j:FruitShape a sh:NodeShape ;
  sh:targetClass neo4j:Fruit ;
  sh:closed true ; 
  sh:property [  
    sh:path neo4j:name ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:datatype xsd:string ;
   ];


    sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:or(
             [
                 sh:class neo4j:Whatever ;
             ]
             [
                 sh:class neo4j:ThisDoesntExist ;
             ]
        );
      ] ;     
.
','Turtle')

But this just returns an empty result (suggesting a successful validation), even though the types used in the 'or' statement don't exist. I also attempted these with sh:xone instead, and that didn't work either.

I am at a loss here, is this a SHACL/neo4j integration issue, or am I using it wrong?

Below is my full example in Python, in case it helps:

from py2neo import Graph
graph = Graph(password="test_database")

graph.delete_all()

example_graph_query = """
CREATE (banana:Fruit {name:'Banana'})
CREATE (apple:Fruit {name:'Apple'})

CREATE (fruit_salad:Dish {name:'Fruit Salad'})
CREATE (banana_gun:Weapon {name:'Banana Gun'})

CREATE
(apple)-[:can_be_used_in]->(fruit_salad),
(banana)-[:can_be_used_in]->(fruit_salad),
(banana)-[:can_be_used_in]->(banana_gun)

"""

graph.run(example_graph_query)

shacl_schema="""
call n10s.validation.shacl.import.inline('

@prefix neo4j: <neo4j://graph.schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

# ---- Fruit -------------

neo4j:FruitShape a sh:NodeShape ;
  sh:targetClass neo4j:Fruit ;
  sh:closed true ; 
  sh:property [  
    sh:path neo4j:name ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:datatype xsd:string ;
   ];

    sh:or(
      sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:class neo4j:Weapon ;
      ] 
      sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:class neo4j:Dish ;
      ] 
    );
.
','Turtle')
"""

shacl_schema_ver_2="""
call n10s.validation.shacl.import.inline('

@prefix neo4j: <neo4j://graph.schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .

# ---- Fruit -------------

neo4j:FruitShape a sh:NodeShape ;
  sh:targetClass neo4j:Fruit ;
  sh:closed true ; 
  sh:property [  
    sh:path neo4j:name ;
    sh:maxCount 1 ;
    sh:minCount 1 ;
    sh:datatype xsd:string ;
   ];


    sh:property [
        sh:path neo4j:can_be_used_in ;
        sh:or(
             [
                 sh:class neo4j:Whatever ;
             ]
             [
                 sh:class neo4j:ThisDoesntExist ;
             ]
        );
      ] ;     
.
','Turtle')
"""


data = graph.run(shacl_schema).data()

validation_query="""
call n10s.validation.shacl.validate() 
"""

result = graph.run(validation_query)
1 REPLY 1

nataliazon
Node

This seems to be a duplicate of https://community.neo4j.com/t5/neo4j-graph-platform/shacl-validate-nodes-have-1-class-a-and-1-class-... , which is also unsolved.

Is the answer that neosemantics doesn't support this functionality?