From e08f9aad1abffb2368dd14d90b438051486bc1d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ha=CC=8Avard=20Ottestad?= Date: Tue, 24 Sep 2024 08:57:35 +0200 Subject: [PATCH] zeroOrMorePath is mostly implemented --- .../AbstractConstraintComponent.java | 3 + .../AbstractPairwiseConstraintComponent.java | 2 +- .../AbstractSimpleConstraintComponent.java | 5 +- .../ClassConstraintComponent.java | 3 +- .../ClosedConstraintComponent.java | 8 +- .../DashHasValueInConstraintComponent.java | 3 +- .../HasValueConstraintComponent.java | 7 +- .../MaxCountConstraintComponent.java | 5 +- .../MinCountConstraintComponent.java | 3 +- .../QualifiedMaxCountConstraintComponent.java | 5 +- .../QualifiedMinCountConstraintComponent.java | 5 +- .../UniqueLangConstraintComponent.java | 6 +- .../sail/shacl/ast/paths/OneOrMorePath.java | 13 +-- .../rdf4j/sail/shacl/ast/paths/Path.java | 23 +++-- .../sail/shacl/ast/paths/ZeroOrMorePath.java | 90 +++++++++++++++---- .../planNodes/AbstractBulkJoinPlanNode.java | 19 +++- .../planNodes/BulkedExternalInnerJoin.java | 7 +- .../BulkedExternalLeftOuterJoin.java | 7 +- .../ast/targets/TargetChainRetriever.java | 7 -- .../rdf4j/sail/shacl/AbstractShaclTest.java | 3 +- .../rdf4j/sail/shacl/UnknownShapesTest.java | 7 +- .../rdf4j/sail/shacl/W3cComplianceTest.java | 6 +- .../shacl/src/test/resources/complexPath.trig | 6 ++ .../shacl/src/test/resources/logback-test.xml | 2 +- 24 files changed, 157 insertions(+), 88 deletions(-) diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractConstraintComponent.java index f3a6413a5cb..0b0f0266f67 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractConstraintComponent.java @@ -171,6 +171,9 @@ static PlanNode getAllTargetsIncludingThoseAddedByPath(ConnectionsGroup connecti null); allTargets = UnionNode.getInstance(connectionsGroup, addedTargets.getPlanNode(), addedByPath); + + allTargets = Unique.getInstance(new TrimToTarget(allTargets, connectionsGroup), false, connectionsGroup); + return allTargets; } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractPairwiseConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractPairwiseConstraintComponent.java index 8b7dcde0f50..0e98dafb020 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractPairwiseConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractPairwiseConstraintComponent.java @@ -252,7 +252,7 @@ public PlanNode getAllTargetsPlan(ConnectionsGroup connectionsGroup, Resource[] } - return Unique.getInstance(allTargetsPlan, false, connectionsGroup); + return Unique.getInstance(new TrimToTarget(allTargetsPlan, connectionsGroup), false, connectionsGroup); } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractSimpleConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractSimpleConstraintComponent.java index c9f0c119ba7..27794706e63 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractSimpleConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/AbstractSimpleConstraintComponent.java @@ -28,6 +28,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AllTargetsPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BufferedPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalInnerJoin; @@ -135,7 +136,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections connectionsGroup.getPreviousStateConnection(), b -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); top = UnionNode.getInstance(connectionsGroup, top, bulkedExternalInnerJoin); @@ -218,7 +219,7 @@ private PlanNode getPlanNodeForOverrideTargetNode(ConnectionsGroup connectionsGr Set.of()), false, null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); planNode = connectionsGroup.getCachedNodeFor(planNode); } } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClassConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClassConstraintComponent.java index 8dde6295aff..4b1173d5251 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClassConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClassConstraintComponent.java @@ -29,6 +29,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AllTargetsPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BufferedSplitter; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalInnerJoin; @@ -171,7 +172,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections false, null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); if (connectionsGroup.getAddedStatements() != null) { // filter by type against the added statements diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClosedConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClosedConstraintComponent.java index a24a92f99a1..c5deca68eed 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClosedConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/ClosedConstraintComponent.java @@ -39,6 +39,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; import org.eclipse.rdf4j.sail.shacl.ast.paths.SimplePath; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BufferedSplitter; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalInnerJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.ExternalFilterByQuery; @@ -192,7 +193,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections false, null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); StatementMatcher.Variable subjectVariable = stableRandomVariableProvider.next(); StatementMatcher.Variable predicateVariable = stableRandomVariableProvider.next(); @@ -317,7 +318,10 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections } return validationTuple; }, - connectionsGroup); + connectionsGroup, + List.of(AbstractBulkJoinPlanNode.DEFAULT_VARS.get(0), AbstractBulkJoinPlanNode.DEFAULT_VARS.get(1), + predicateVariable) + ); return bulkedExternalInnerJoin; } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/DashHasValueInConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/DashHasValueInConstraintComponent.java index 450b0ce4ecd..c0d0f2840c0 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/DashHasValueInConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/DashHasValueInConstraintComponent.java @@ -31,6 +31,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher.Variable; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.GroupByFilter; @@ -113,7 +114,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider, Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); PlanNode invalidTargets = new GroupByFilter(joined, group -> { return group.stream().map(ValidationTuple::getValue).noneMatch(hasValueIn::contains); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/HasValueConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/HasValueConstraintComponent.java index 7dbfea97a80..471cafc2bb3 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/HasValueConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/HasValueConstraintComponent.java @@ -30,6 +30,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.GroupByFilter; @@ -98,16 +99,18 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections EffectiveTarget.Extend.left, false, null); addedTargets = UnionNode.getInstance(connectionsGroup, addedByPath, addedTargets); - addedTargets = Unique.getInstance(addedTargets, false, connectionsGroup); } + addedTargets = Unique.getInstance(new TrimToTarget(addedTargets, connectionsGroup), false, + connectionsGroup); + PlanNode joined = new BulkedExternalLeftOuterJoin(addedTargets, connectionsGroup.getBaseConnection(), validationSettings.getDataGraph(), path.getTargetQueryFragment(new Variable<>("a"), new Variable<>("c"), connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider, Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); PlanNode invalidTargets = new GroupByFilter(joined, group -> { return group diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MaxCountConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MaxCountConstraintComponent.java index a204fe88de0..5f6553c8857 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MaxCountConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MaxCountConstraintComponent.java @@ -32,6 +32,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalInnerJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode; @@ -113,7 +114,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections false, null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); } else { relevantTargetsWithPath = new BulkedExternalLeftOuterJoin( mergeNode, @@ -126,7 +127,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); } relevantTargetsWithPath = connectionsGroup.getCachedNodeFor(relevantTargetsWithPath); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MinCountConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MinCountConstraintComponent.java index 00e01090abb..1f16d608d96 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MinCountConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/MinCountConstraintComponent.java @@ -29,6 +29,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.GroupByCountFilter; @@ -118,7 +119,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider, Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); relevantTargetsWithPath = connectionsGroup.getCachedNodeFor(relevantTargetsWithPath); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMaxCountConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMaxCountConstraintComponent.java index 6394c2f9114..870fcedde84 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMaxCountConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMaxCountConstraintComponent.java @@ -33,6 +33,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.Shape; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.GroupByCountFilter; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.LeftOuterJoin; @@ -182,7 +183,7 @@ public PlanNode negated(ConnectionsGroup connectionsGroup, ValidationSettings va Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); return new TupleMapper(relevantTargetsWithPath, t -> { List targetChain = t.getTargetChain(true); @@ -226,7 +227,7 @@ public PlanNode negated(ConnectionsGroup connectionsGroup, ValidationSettings va connectionsGroup.getRdfsSubClassOfReasoner(), stableRandomVariableProvider, Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); invalid = new NotValuesIn(allTargetsPlan, invalid, connectionsGroup); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMinCountConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMinCountConstraintComponent.java index 69c499a0fbc..8f241009774 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMinCountConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/QualifiedMinCountConstraintComponent.java @@ -34,6 +34,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.Shape; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AllTargetsPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalLeftOuterJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.GroupByCountFilter; @@ -189,7 +190,7 @@ public PlanNode negated(ConnectionsGroup connectionsGroup, ValidationSettings va Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); return new TupleMapper(relevantTargetsWithPath, t -> { List targetChain = t.getTargetChain(true); @@ -240,7 +241,7 @@ public PlanNode negated(ConnectionsGroup connectionsGroup, ValidationSettings va Set.of()), (b) -> new ValidationTuple(b.getValue("a"), b.getValue("c"), scope, true, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); } invalid = new NotValuesIn(allTargetsPlan, invalid, connectionsGroup); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/UniqueLangConstraintComponent.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/UniqueLangConstraintComponent.java index 453ff412372..8b11807c8f2 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/UniqueLangConstraintComponent.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/constraintcomponents/UniqueLangConstraintComponent.java @@ -27,6 +27,7 @@ import org.eclipse.rdf4j.sail.shacl.ast.ValidationApproach; import org.eclipse.rdf4j.sail.shacl.ast.ValidationQuery; import org.eclipse.rdf4j.sail.shacl.ast.paths.Path; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.AbstractBulkJoinPlanNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.BulkedExternalInnerJoin; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.EmptyNode; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.InnerJoin; @@ -142,7 +143,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); PlanNode nonUniqueTargetLang = new NonUniqueTargetLang(relevantTargetsWithPath, connectionsGroup); return Unique.getInstance(new TrimToTarget(nonUniqueTargetLang, connectionsGroup), false, connectionsGroup); @@ -189,8 +190,7 @@ public PlanNode generateTransactionalValidationPlan(ConnectionsGroup connections false, null, BulkedExternalInnerJoin.getMapper("a", "c", scope, validationSettings.getDataGraph()), - - connectionsGroup); + connectionsGroup, AbstractBulkJoinPlanNode.DEFAULT_VARS); PlanNode nonUniqueTargetLang = new NonUniqueTargetLang(relevantTargetsWithPath, connectionsGroup); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/OneOrMorePath.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/OneOrMorePath.java index 7bd4c40536a..8092286d500 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/OneOrMorePath.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/OneOrMorePath.java @@ -57,7 +57,7 @@ public OneOrMorePath(Resource id, Path path) { @Override public String toString() { - return "OneOrOnePath{ " + path + " }"; + return "OneOrMorePath{ " + path + " }"; } @Override @@ -132,14 +132,6 @@ public SparqlFragment getTargetQueryFragment(StatementMatcher.Variable subject, rdfsSubClassOfReasoner, stableRandomVariableProvider, inheritedVarNames); -// SparqlFragment targetQueryFragmentStart = path.getTargetQueryFragment(subject, pathStart, -// rdfsSubClassOfReasoner, stableRandomVariableProvider, -// inheritedVarNames); -// -// SparqlFragment targetQueryFragmentEnd = path.getTargetQueryFragment(pathEnd, object, rdfsSubClassOfReasoner, -// stableRandomVariableProvider, -// inheritedVarNames); - SparqlFragment targetQueryFragmentExactlyOne = path.getTargetQueryFragment(subject, object, rdfsSubClassOfReasoner, stableRandomVariableProvider, inheritedVarNames); @@ -171,7 +163,7 @@ public SparqlFragment getTargetQueryFragment(StatementMatcher.Variable subject, assert subjectName.equals(pathStart.getName()); String query = "select distinct " + subject.asSparqlVariable() + " where {\n" - + subject.asSparqlVariable() + " (" + sparqlPathString + ")* " + + subject.asSparqlVariable() + " (" + sparqlPathString + ")+ " + pathStart.asSparqlVariable() + "\n}"; TupleExpr tupleExpr = SparqlQueryParserCache.get(query); @@ -185,7 +177,6 @@ public SparqlFragment getTargetQueryFragment(StatementMatcher.Variable subject, new SingletonBindingSet(subjectName, statement.getSubject()), true)) { while (evaluate.hasNext()) { BindingSet next = evaluate.next(); - System.out.println(next); statements.add(new EffectiveTarget.SubjectObjectAndMatcher.SubjectObject( ((Resource) next.getValue(subject.getName())), null)); } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/Path.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/Path.java index 45cdfc5da24..7ea5363f276 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/Path.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/Path.java @@ -21,6 +21,7 @@ import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.Statement; import org.eclipse.rdf4j.model.vocabulary.RDF; +import org.eclipse.rdf4j.sail.shacl.ShaclSailConnection; import org.eclipse.rdf4j.sail.shacl.ast.Exportable; import org.eclipse.rdf4j.sail.shacl.ast.Identifiable; import org.eclipse.rdf4j.sail.shacl.ast.ShaclUnsupportedException; @@ -30,9 +31,13 @@ import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeWrapper; import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup; import org.eclipse.rdf4j.sail.shacl.wrapper.shape.ShapeSource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public abstract class Path implements Identifiable, Exportable, Targetable { + private static final Logger logger = LoggerFactory.getLogger(ShaclSailConnection.class); + Resource id; public Path(Resource id) { @@ -62,43 +67,37 @@ static public Path buildPath(ShapeSource shapeSource, Resource id) { switch (pathType.toString()) { case "http://www.w3.org/ns/shacl#inversePath": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new InversePath(id, (Resource) statement.getObject(), shapeSource); break; case "http://www.w3.org/ns/shacl#alternativePath": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new AlternativePath(id, (Resource) statement.getObject(), shapeSource); break; case "http://www.w3.org/ns/shacl#zeroOrMorePath": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new ZeroOrMorePath(id, (Resource) statement.getObject(), shapeSource); break; case "http://www.w3.org/ns/shacl#oneOrMorePath": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new OneOrMorePath(id, (Resource) statement.getObject(), shapeSource); break; case "http://www.w3.org/ns/shacl#zeroOrOnePath": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new ZeroOrOnePath(id, (Resource) statement.getObject(), shapeSource); break; case "http://www.w3.org/1999/02/22-rdf-syntax-ns#first": if (ret != null) { - throw new ShaclUnsupportedException( - "Multiple path types not supported! " + Arrays.toString(collect.toArray())); + logger.error("Multiple path types not supported! {}", Arrays.toString(collect.toArray())); } ret = new SequencePath(id, shapeSource); break; diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/ZeroOrMorePath.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/ZeroOrMorePath.java index e7f17bb3067..ae662dce828 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/ZeroOrMorePath.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/paths/ZeroOrMorePath.java @@ -14,19 +14,26 @@ import java.util.ArrayList; import java.util.List; import java.util.Set; -import java.util.stream.Collectors; import java.util.stream.Stream; +import org.apache.commons.lang3.NotImplementedException; +import org.eclipse.rdf4j.common.iteration.CloseableIteration; import org.eclipse.rdf4j.model.IRI; import org.eclipse.rdf4j.model.Model; import org.eclipse.rdf4j.model.Resource; import org.eclipse.rdf4j.model.vocabulary.SHACL; -import org.eclipse.rdf4j.sail.shacl.ast.ShaclUnsupportedException; +import org.eclipse.rdf4j.query.BindingSet; +import org.eclipse.rdf4j.query.algebra.TupleExpr; +import org.eclipse.rdf4j.sail.SailConnection; import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment; +import org.eclipse.rdf4j.sail.shacl.ast.SparqlQueryParserCache; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.ConstraintComponent; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNode; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeHelper; import org.eclipse.rdf4j.sail.shacl.ast.planNodes.PlanNodeWrapper; +import org.eclipse.rdf4j.sail.shacl.ast.planNodes.SingletonBindingSet; +import org.eclipse.rdf4j.sail.shacl.ast.targets.EffectiveTarget; import org.eclipse.rdf4j.sail.shacl.ast.targets.TargetChainRetriever; import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup; import org.eclipse.rdf4j.sail.shacl.wrapper.data.RdfsSubClassOfReasoner; @@ -125,33 +132,80 @@ public SparqlFragment getTargetQueryFragment(StatementMatcher.Variable subject, rdfsSubClassOfReasoner, stableRandomVariableProvider, inheritedVarNames); - SparqlFragment targetQueryFragmentStart = path.getTargetQueryFragment(subject, pathStart, - rdfsSubClassOfReasoner, stableRandomVariableProvider, - inheritedVarNames); + SparqlFragment targetQueryFragmentZeroOrOne = SparqlFragment.bgp(List.of(), + subject.asSparqlVariable() + " (" + sparqlPathString + ")? " + object.asSparqlVariable(), + List.of(new StatementMatcher(subject, null, object, this, inheritedVarNames)), + (connectionsGroup, dataGraph, path, currentStatementMatcher, currentStatements) -> { + // TODO: We don't have any tests that cover this! Maybe take a look at minCount/oneOrMorePath? + if (currentStatementMatcher.hasSubject(subject)) { + System.out.println(); + throw new NotImplementedException("This should not happen!"); + } - SparqlFragment targetQueryFragmentEnd = path.getTargetQueryFragment(pathEnd, object, rdfsSubClassOfReasoner, - stableRandomVariableProvider, - inheritedVarNames); + return Stream.empty(); + } + ); String oneOrMore = subject.asSparqlVariable() + " (" + sparqlPathString + ")* " + pathStart.asSparqlVariable() + " .\n" + targetQueryFragmentMiddle.getFragment() + "\n" + pathEnd.asSparqlVariable() + " (" + sparqlPathString + ")* " + object.asSparqlVariable() + " .\n"; - String zeroOrOne = subject.asSparqlVariable() + " (" + sparqlPathString + ")? " + object.asSparqlVariable() - + " .\n"; + SparqlFragment oneOrMoreBgp = SparqlFragment.bgp(List.of(), oneOrMore, + targetQueryFragmentMiddle.getStatementMatchers()); + + var temp = inheritedVarNames; + + return SparqlFragment.union(List.of(targetQueryFragmentZeroOrOne, oneOrMoreBgp), + (connectionsGroup, dataGraph, path, currentStatementMatcher, currentStatements) -> { + + Stream statementsAndMatcherStream1 = targetQueryFragmentZeroOrOne + .getRoot(connectionsGroup, dataGraph, path, currentStatementMatcher, currentStatements) + .filter(EffectiveTarget.SubjectObjectAndMatcher::hasStatements); + + Stream peek = targetQueryFragmentMiddle + .getRoot(connectionsGroup, dataGraph, path, currentStatementMatcher, currentStatements) + .filter(EffectiveTarget.SubjectObjectAndMatcher::hasStatements) + .map(a -> { + SailConnection baseConnection = connectionsGroup.getBaseConnection(); + + String subjectName = a.getStatementMatcher().getSubjectName(); + assert subjectName.equals(pathStart.getName()); + + String query = "select distinct " + subject.asSparqlVariable() + " where {\n" + + subject.asSparqlVariable() + " (" + sparqlPathString + ")* " + + pathStart.asSparqlVariable() + "\n}"; + + TupleExpr tupleExpr = SparqlQueryParserCache.get(query); + + List statements = new ArrayList<>(); + + for (EffectiveTarget.SubjectObjectAndMatcher.SubjectObject statement : a + .getStatements()) { + try (CloseableIteration evaluate = baseConnection.evaluate( + tupleExpr, PlanNodeHelper.asDefaultGraphDataset(dataGraph), + new SingletonBindingSet(subjectName, statement.getSubject()), true)) { + while (evaluate.hasNext()) { + BindingSet next = evaluate.next(); + System.out.println(next); + statements.add(new EffectiveTarget.SubjectObjectAndMatcher.SubjectObject( + ((Resource) next.getValue(subject.getName())), null)); + } + } + } + + StatementMatcher statementMatcher = new StatementMatcher(subject, null, null, path, + temp); - ArrayList statementMatchers = Stream - .of(targetQueryFragmentStart.getStatementMatchers(), targetQueryFragmentMiddle.getStatementMatchers(), - targetQueryFragmentEnd.getStatementMatchers()) - .flatMap(List::stream) - .collect(Collectors.toCollection(ArrayList::new)); + EffectiveTarget.SubjectObjectAndMatcher effectiveTarget = new EffectiveTarget.SubjectObjectAndMatcher( + statements, statementMatcher); - SparqlFragment bgp1 = SparqlFragment.bgp(List.of(), oneOrMore, statementMatchers); + return effectiveTarget; + }); - SparqlFragment bgp2 = SparqlFragment.bgp(List.of(), zeroOrOne, statementMatchers); + return Stream.concat(statementsAndMatcherStream1, peek); - return SparqlFragment.union(List.of(bgp1, bgp2)); + }); } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractBulkJoinPlanNode.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractBulkJoinPlanNode.java index dab0aac9d7e..5ca6b5ca029 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractBulkJoinPlanNode.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/AbstractBulkJoinPlanNode.java @@ -28,18 +28,28 @@ import org.eclipse.rdf4j.query.impl.EmptyBindingSet; import org.eclipse.rdf4j.sail.SailConnection; import org.eclipse.rdf4j.sail.shacl.ast.SparqlQueryParserCache; +import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.AbstractConstraintComponent; public abstract class AbstractBulkJoinPlanNode implements PlanNode { + public static final List DEFAULT_VARS = List.of(new StatementMatcher.Variable("a"), + new StatementMatcher.Variable("c")); public static final String BINDING_NAME = "a"; protected static final int BULK_SIZE = 1000; - private StackTraceElement[] stackTrace; + private final List vars; + private final String varsQueryString; + StackTraceElement[] stackTrace; protected Function mapper; ValidationExecutionLogger validationExecutionLogger; - public AbstractBulkJoinPlanNode() { -// this.stackTrace = Thread.currentThread().getStackTrace(); + public AbstractBulkJoinPlanNode(List vars) { + this.vars = vars; + this.varsQueryString = vars.stream() + .map(StatementMatcher.Variable::asSparqlVariable) + .reduce((a, b) -> a + " " + b) + .orElseThrow(); + this.stackTrace = Thread.currentThread().getStackTrace(); } TupleExpr parseQuery(String query) { @@ -47,7 +57,8 @@ TupleExpr parseQuery(String query) { // #VALUES_INJECTION_POINT# is an annotation in the query where there is a "new scope" due to the bottom up // semantics of SPARQL but where we don't actually want a new scope. query = query.replace(AbstractConstraintComponent.VALUES_INJECTION_POINT, "\nVALUES (?a) {}\n"); - String completeQuery = "select * where {\nVALUES (?a) {}\n" + query + "\n}"; + + String completeQuery = "select distinct " + varsQueryString + " where {\nVALUES (?a) {}\n" + query + "\n}"; return SparqlQueryParserCache.get(completeQuery); } diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalInnerJoin.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalInnerJoin.java index c48e3ee02c6..d732f092e18 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalInnerJoin.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalInnerJoin.java @@ -13,6 +13,7 @@ import java.util.ArrayDeque; import java.util.LinkedHashMap; +import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -25,7 +26,6 @@ import org.eclipse.rdf4j.query.algebra.TupleExpr; import org.eclipse.rdf4j.query.algebra.evaluation.iterator.PeekMarkIterator; import org.eclipse.rdf4j.sail.SailConnection; -import org.eclipse.rdf4j.sail.memory.MemoryStoreConnection; import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.ast.constraintcomponents.ConstraintComponent; @@ -61,8 +61,9 @@ public class BulkedExternalInnerJoin extends AbstractBulkJoinPlanNode { public BulkedExternalInnerJoin(PlanNode leftNode, SailConnection connection, Resource[] dataGraph, SparqlFragment query, boolean skipBasedOnPreviousConnection, SailConnection previousStateConnection, - Function mapper, ConnectionsGroup connectionsGroup) { - super(); + Function mapper, ConnectionsGroup connectionsGroup, + List vars) { + super(vars); assert !skipBasedOnPreviousConnection || previousStateConnection != null; this.leftNode = PlanNodeHelper.handleSorting(this, leftNode, connectionsGroup); diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalLeftOuterJoin.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalLeftOuterJoin.java index 8ea6b629229..2b44471a9bc 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalLeftOuterJoin.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/planNodes/BulkedExternalLeftOuterJoin.java @@ -12,6 +12,7 @@ package org.eclipse.rdf4j.sail.shacl.ast.planNodes; import java.util.ArrayDeque; +import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -23,7 +24,6 @@ import org.eclipse.rdf4j.query.algebra.TupleExpr; import org.eclipse.rdf4j.query.algebra.evaluation.iterator.PeekMarkIterator; import org.eclipse.rdf4j.sail.SailConnection; -import org.eclipse.rdf4j.sail.memory.MemoryStoreConnection; import org.eclipse.rdf4j.sail.shacl.ast.SparqlFragment; import org.eclipse.rdf4j.sail.shacl.ast.StatementMatcher; import org.eclipse.rdf4j.sail.shacl.wrapper.data.ConnectionsGroup; @@ -46,8 +46,9 @@ public class BulkedExternalLeftOuterJoin extends AbstractBulkJoinPlanNode { public BulkedExternalLeftOuterJoin(PlanNode leftNode, SailConnection connection, Resource[] dataGraph, SparqlFragment query, - Function mapper, ConnectionsGroup connectionsGroup) { - super(); + Function mapper, ConnectionsGroup connectionsGroup, + List vars) { + super(vars); leftNode = PlanNodeHelper.handleSorting(this, leftNode, connectionsGroup); this.leftNode = leftNode; this.query = query.getNamespacesForSparql() diff --git a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/targets/TargetChainRetriever.java b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/targets/TargetChainRetriever.java index 37318457b98..bc062f2d003 100644 --- a/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/targets/TargetChainRetriever.java +++ b/core/sail/shacl/src/main/java/org/eclipse/rdf4j/sail/shacl/ast/targets/TargetChainRetriever.java @@ -302,19 +302,12 @@ private List readStatementsInBulk(Set varNames) { } } - List collect = rootStatements.collect(Collectors.toList()); - rootStatements = collect.stream(); - rootStatements .filter(EffectiveTarget.SubjectObjectAndMatcher::hasStatements) .flatMap(statementsAndMatcher -> { StatementMatcher newCurrentStatementMatcher = statementsAndMatcher .getStatementMatcher(); - for (EffectiveTarget.SubjectObjectAndMatcher andMatcher : collect) { - System.out.println(andMatcher); - } - return statementsAndMatcher.getStatements() .stream() .map(temp -> { diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java index 9392cbf043d..df30cebb714 100644 --- a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java @@ -83,6 +83,7 @@ import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.parallel.Execution; import org.junit.jupiter.api.parallel.Isolated; import org.junit.jupiter.params.provider.Arguments; import org.slf4j.Logger; @@ -115,7 +116,7 @@ abstract public class AbstractShaclTest { "test-cases/path/zeroOrOnePath" ); - public static final Set ISOLATION_LEVELS = Set.of( + public static final List ISOLATION_LEVELS = List.of( IsolationLevels.NONE, IsolationLevels.SNAPSHOT, IsolationLevels.SERIALIZABLE diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/UnknownShapesTest.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/UnknownShapesTest.java index 44175008568..679d1d6e8dd 100644 --- a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/UnknownShapesTest.java +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/UnknownShapesTest.java @@ -104,13 +104,10 @@ public void testComplexPath() throws IOException { } } - Set relevantLog = getRelevantLog(4); + Set relevantLog = getRelevantLog(1); Set expected = Set.of( - "Unsupported SHACL feature detected: InversePath{ ZeroOrMorePath{ SimplePath{ } } }. Shape ignored! a sh:PropertyShape; sh:path [ sh:inversePath [ sh:zeroOrMorePath ] ]; sh:datatype xsd:int .", - "Unsupported SHACL feature detected: AlternativePath{ [SimplePath{ }, ZeroOrOnePath{ SimplePath{ } }, SimplePath{ }] }. Shape ignored! a sh:PropertyShape; sh:path [ sh:alternativePath ( [ sh:oneOrMorePath ] ) ]; sh:nodeKind sh:BlankNodeOrIRI .", - "Unsupported SHACL feature detected: AlternativePath{ [SimplePath{ }, ZeroOrOnePath{ SimplePath{ } }, SimplePath{ }] }. Shape ignored! a sh:PropertyShape; sh:path [ sh:alternativePath ( [ sh:zeroOrOnePath ] ) ]; sh:nodeKind sh:BlankNodeOrIRI .", - "Unsupported SHACL feature detected: InversePath{ ZeroOrMorePath{ SimplePath{ } } }. Shape ignored! a sh:PropertyShape; sh:path [ sh:inversePath [ sh:zeroOrMorePath ] ]; sh:minCount 1 ." + "Unsupported SHACL feature detected: AlternativePath{ [SimplePath{ }, ZeroOrOnePath{ SimplePath{ } }, SimplePath{ }] }. Shape ignored! a sh:PropertyShape; sh:path [ sh:alternativePath ( [ sh:zeroOrOnePath ] ) ]; sh:nodeKind sh:BlankNodeOrIRI ." ); Assertions.assertEquals(expected, relevantLog); diff --git a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/W3cComplianceTest.java b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/W3cComplianceTest.java index cd428fceae4..965569a6ec6 100644 --- a/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/W3cComplianceTest.java +++ b/core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/W3cComplianceTest.java @@ -56,13 +56,11 @@ public class W3cComplianceTest { private final static Set TESTS_FAILING_DUE_TO_MISSING_FEATURES_FROM_THE_SPEC = Set.of( "/core/node/xone-001.ttl", "/core/node/xone-duplicate.ttl", - "/core/path/path-complex-001.ttl", - "/core/path/path-oneOrMore-001.ttl", - "/core/path/path-zeroOrMore-001.ttl", "/core/path/path-zeroOrOne-001.ttl", "/core/property/qualifiedMinCountDisjoint-001.ttl", "/core/property/qualifiedValueShapesDisjoint-001.ttl", - "/core/property/uniqueLang-002.ttl"); + "/core/property/uniqueLang-002.ttl" + ); public static Stream data() { return getTestFiles().stream() diff --git a/core/sail/shacl/src/test/resources/complexPath.trig b/core/sail/shacl/src/test/resources/complexPath.trig index 6156512fe4d..ab26e8df364 100644 --- a/core/sail/shacl/src/test/resources/complexPath.trig +++ b/core/sail/shacl/src/test/resources/complexPath.trig @@ -20,6 +20,7 @@ ex:PersonShape ex:alternativePath , ex:nestedSequencePath , ex:alternativePathZeroOrOne , + ex:alternativePathZeroOrMore , ex:alternativePathOneOrMore . ex:inverseOfWithComplex @@ -47,6 +48,11 @@ ex:alternativePathZeroOrOne sh:path [ sh:alternativePath (ex:father [sh:zeroOrOnePath ex:parent] ex:mother ) ] ; sh:nodeKind sh:BlankNodeOrIRI . +ex:alternativePathZeroOrMore + sh:path [ sh:alternativePath (ex:father [sh:zeroOrMorePath ex:parent] ex:mother ) ] ; + sh:nodeKind sh:BlankNodeOrIRI . + + ex:alternativePathOneOrMore sh:path [ sh:alternativePath (ex:father [sh:oneOrMorePath ex:parent] ex:mother ) ] ; sh:nodeKind sh:BlankNodeOrIRI . diff --git a/core/sail/shacl/src/test/resources/logback-test.xml b/core/sail/shacl/src/test/resources/logback-test.xml index 990f3bb2551..40ced8210b0 100644 --- a/core/sail/shacl/src/test/resources/logback-test.xml +++ b/core/sail/shacl/src/test/resources/logback-test.xml @@ -7,7 +7,7 @@ - +