Skip to content

Commit a4b91f9

Browse files
committed
GH-5136 define that minCount and maxCount will only count the distinct values when applied to the union of multiple graphs
1 parent 6967301 commit a4b91f9

9 files changed

Lines changed: 267 additions & 6 deletions

File tree

core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/AbstractShaclTest.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,11 @@ void referenceImplementationTestCaseValidation(TestCase testCase) {
607607
return;
608608
}
609609

610+
// the TopBraid SHACL API doesn't support multiple data graphs
611+
if (testCase.testCasePath.startsWith("test-cases/maxCount/simple/invalid/case4/")) {
612+
return;
613+
}
614+
610615
printTestCase(testCase);
611616

612617
Dataset shaclDataset = DatasetFactory.create();

core/sail/shacl/src/test/java/org/eclipse/rdf4j/sail/shacl/ShapesGraphTest.java

Lines changed: 160 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -109,8 +109,8 @@ public void testInvalidUnionGraph() throws Throwable {
109109
connection.begin();
110110

111111
connection.add(laura, FOAF.PHONE, Values.literal(1));
112-
connection.add(laura, FOAF.PHONE, Values.literal(1), data1);
113-
connection.add(laura, FOAF.PHONE, Values.literal(1), data2);
112+
connection.add(laura, FOAF.PHONE, Values.literal(2), data1);
113+
connection.add(laura, FOAF.PHONE, Values.literal(3), data2);
114114

115115
connection.commit();
116116
}
@@ -120,6 +120,133 @@ public void testInvalidUnionGraph() throws Throwable {
120120

121121
}
122122

123+
@Test
124+
public void testValidUnionGraphMinCount() throws Throwable {
125+
126+
test(repository -> {
127+
128+
try (RepositoryConnection connection = repository.getConnection()) {
129+
connection.begin();
130+
connection.add(laura, RDF.TYPE, FOAF.PERSON, data2);
131+
connection.add(laura, FOAF.NAME, Values.literal("Laura"), data2);
132+
connection.commit();
133+
}
134+
135+
try (RepositoryConnection connection = repository.getConnection()) {
136+
connection.begin();
137+
138+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"));
139+
connection.add(laura, FOAF.INTEREST, Values.literal("tennis"), data1);
140+
connection.add(laura, FOAF.INTEREST, Values.literal("chess"), data2);
141+
142+
connection.commit();
143+
}
144+
145+
});
146+
147+
}
148+
149+
@Test
150+
public void testInvalidUnionGraphMinCount() throws Throwable {
151+
152+
test(repository -> {
153+
154+
try (RepositoryConnection connection = repository.getConnection()) {
155+
connection.begin();
156+
connection.add(laura, RDF.TYPE, FOAF.PERSON, data2);
157+
connection.add(laura, FOAF.NAME, Values.literal("Laura"), data2);
158+
connection.commit();
159+
}
160+
161+
assertThrows(RepositoryException.class, () -> {
162+
try (RepositoryConnection connection = repository.getConnection()) {
163+
connection.begin();
164+
165+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"));
166+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"), data1);
167+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"), data2);
168+
169+
connection.commit();
170+
}
171+
});
172+
173+
});
174+
175+
}
176+
177+
@Test
178+
public void testInvalidUnionGraphMinCountSparql() throws Throwable {
179+
180+
test(repository -> {
181+
182+
assertThrows(RepositoryException.class, () -> {
183+
try (RepositoryConnection connection = repository.getConnection()) {
184+
connection.begin(ShaclSail.TransactionSettings.ValidationApproach.Bulk);
185+
connection.add(laura, RDF.TYPE, FOAF.PERSON, data2);
186+
connection.add(laura, FOAF.NAME, Values.literal("Laura"), data2);
187+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"));
188+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"), data1);
189+
connection.add(laura, FOAF.INTEREST, Values.literal("golf"), data2);
190+
connection.commit();
191+
}
192+
});
193+
194+
});
195+
196+
}
197+
198+
@Test
199+
public void testValidUnionGraph() throws Throwable {
200+
201+
test(repository -> {
202+
203+
try (RepositoryConnection connection = repository.getConnection()) {
204+
connection.begin();
205+
connection.add(laura, RDF.TYPE, FOAF.PERSON, data2);
206+
connection.add(laura, FOAF.NAME, Values.literal("Laura"), data2);
207+
connection.commit();
208+
}
209+
210+
try (RepositoryConnection connection = repository.getConnection()) {
211+
connection.begin();
212+
213+
connection.add(laura, FOAF.PHONE, Values.literal(1));
214+
connection.add(laura, FOAF.PHONE, Values.literal(1), data1);
215+
connection.add(laura, FOAF.PHONE, Values.literal(1), data2);
216+
217+
connection.commit();
218+
}
219+
220+
});
221+
222+
}
223+
224+
@Test
225+
public void testValidUnionGraphSparql() throws Throwable {
226+
227+
test(repository -> {
228+
229+
try (RepositoryConnection connection = repository.getConnection()) {
230+
connection.begin();
231+
connection.add(laura, RDF.TYPE, FOAF.PERSON, data2);
232+
connection.add(laura, FOAF.NAME, Values.literal("Laura"), data2);
233+
connection.commit();
234+
}
235+
236+
try (RepositoryConnection connection = repository.getConnection()) {
237+
connection.begin(ShaclSail.TransactionSettings.ValidationApproach.Bulk);
238+
239+
connection.add(laura, FOAF.PHONE, Values.literal(1));
240+
connection.add(laura, FOAF.PHONE, Values.literal(1), data1);
241+
connection.add(laura, FOAF.PHONE, Values.literal(1), data2);
242+
243+
connection.commit();
244+
}
245+
246+
});
247+
248+
}
249+
123250
@Test
124251
public void testInvalidSplitAcrossGraphs() throws Throwable {
125252

@@ -210,15 +337,15 @@ public void testDefaultShapesGraph() throws IOException {
210337
try (RepositoryConnection connection = repository.getConnection()) {
211338
connection.begin();
212339
connection.add(laura, RDF.TYPE, FOAF.PERSON, Values.iri("http://example.org/differentGraph"));
213-
connection.add(laura, FOAF.PHONE, Values.literal(12345678));
214-
connection.add(laura, FOAF.PHONE, Values.literal(12345678), data2);
340+
connection.add(laura, FOAF.PHONE, Values.literal(1));
341+
connection.add(laura, FOAF.PHONE, Values.literal(2), data2);
215342
connection.commit();
216343
}
217344

218345
assertThrows(RepositoryException.class, () -> {
219346
try (RepositoryConnection connection = repository.getConnection()) {
220347
connection.begin();
221-
connection.add(laura, FOAF.PHONE, Values.literal(12345678), data1);
348+
connection.add(laura, FOAF.PHONE, Values.literal(3), data1);
222349
connection.commit();
223350
}
224351
});
@@ -227,6 +354,32 @@ public void testDefaultShapesGraph() throws IOException {
227354

228355
}
229356

357+
@Test
358+
public void testDefaultShapesGraph2() throws IOException {
359+
360+
ShaclSail shaclSail = new ShaclSail(new MemoryStore());
361+
SailRepository repository = new SailRepository(shaclSail);
362+
363+
loadShapes(repository);
364+
365+
try (RepositoryConnection connection = repository.getConnection()) {
366+
connection.begin();
367+
connection.add(laura, RDF.TYPE, FOAF.PERSON, Values.iri("http://example.org/differentGraph"));
368+
connection.add(laura, FOAF.PHONE, Values.literal(1));
369+
connection.add(laura, FOAF.PHONE, Values.literal(1), data2);
370+
connection.commit();
371+
}
372+
373+
try (RepositoryConnection connection = repository.getConnection()) {
374+
connection.begin();
375+
connection.add(laura, FOAF.PHONE, Values.literal(1), data1);
376+
connection.commit();
377+
}
378+
379+
repository.shutDown();
380+
381+
}
382+
230383
private void loadShapes(SailRepository repository) throws IOException {
231384
try (SailRepositoryConnection connection = repository.getConnection()) {
232385
connection.begin(ShaclSail.TransactionSettings.ValidationApproach.Disabled);
@@ -246,7 +399,8 @@ private void test(Consumer<Repository> testCase) throws Throwable {
246399
Values.iri(EX, "peopleKnowHumansShapes"),
247400
Values.iri(EX, "mustHaveNameShapes"),
248401
Values.iri(EX, "maxFiveAcquaintances"),
249-
Values.iri(EX, "nestedKnowsShouldHaveAge")
402+
Values.iri(EX, "nestedKnowsShouldHaveAge"),
403+
Values.iri(EX, "mustHaveMinThreeInterestsOrNoneAtAll")
250404
));
251405

252406
loadShapes(repository);

core/sail/shacl/src/test/resources/multipleShapesGraphs.trig

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
@prefix sh: <http://www.w3.org/ns/shacl#> .
66
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
77
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
8+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
89

910
ex:peopleKnowPeopleShapes {
1011
ex:PersonShapeKnowsPerson a sh:NodeShape;
@@ -43,6 +44,23 @@ ex:mustHaveNameShapes {
4344
ex:data2 sh:shapesGraph ex:mustHaveNameShapes.
4445
}
4546

47+
ex:mustHaveMinThreeInterestsOrNoneAtAll {
48+
ex:mustHaveMinThreeInterestsOrNoneAtAll a sh:NodeShape;
49+
sh:targetSubjectsOf foaf:interest;
50+
sh:property [
51+
sh:path foaf:interest;
52+
sh:minCount 3;
53+
] .
54+
55+
[
56+
a rsx:DataAndShapesGraphLink;
57+
rsx:shapesGraph ex:mustHaveMinThreeInterestsOrNoneAtAll;
58+
rsx:dataGraph rdf4j:nil, ex:data1, ex:data2;
59+
]
60+
}
61+
62+
63+
4664
rdf4j:SHACLShapeGraph {
4765
ex:PersonShapeMustHaveName a sh:NodeShape;
4866
sh:targetClass foaf:Person;
@@ -65,6 +83,7 @@ ex:maxFiveAcquaintances {
6583
ex:data2 sh:shapesGraph ex:maxFiveAcquaintances.
6684
}
6785

86+
6887
ex:nestedKnowsShouldHaveAge {
6988
ex:PersonShapeNestedKnowsShouldHaveAge a sh:NodeShape;
7089
sh:targetClass foaf:Person;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1 a ex:Person ;
10+
ex:ssn "123", "456".
11+
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
GRAPH ex:g1 {
10+
ex:validPerson1 ex:ssn "789", "012".
11+
}
12+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
@prefix ex: <http://example.com/ns#> .
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
3+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4+
@prefix sh: <http://www.w3.org/ns/shacl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
8+
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
9+
10+
[] a sh:ValidationReport;
11+
rdf4j:truncated false;
12+
sh:conforms false;
13+
sh:result [ a sh:ValidationResult;
14+
rsx:shapesGraph rdf4j:SHACLShapeGraph;
15+
sh:focusNode ex:validPerson1;
16+
sh:resultPath ex:ssn;
17+
sh:resultSeverity sh:Violation;
18+
sh:sourceConstraintComponent sh:MaxCountConstraintComponent;
19+
sh:sourceShape [ a sh:PropertyShape;
20+
sh:maxCount 3;
21+
sh:path ex:ssn
22+
]
23+
] .
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
ex:validPerson1 a ex:Person ;
10+
ex:ssn "123", "456".
11+
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
PREFIX ex: <http://example.com/ns#>
2+
PREFIX owl: <http://www.w3.org/2002/07/owl#>
3+
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
4+
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
5+
PREFIX sh: <http://www.w3.org/ns/shacl#>
6+
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
7+
8+
INSERT DATA {
9+
GRAPH ex:g1 {
10+
ex:validPerson1 ex:ssn "789", "123".
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@prefix ex: <http://example.com/ns#> .
2+
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
3+
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
4+
@prefix sh: <http://www.w3.org/ns/shacl#> .
5+
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
6+
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
7+
@prefix rsx: <http://rdf4j.org/shacl-extensions#> .
8+
@prefix rdf4j: <http://rdf4j.org/schema/rdf4j#> .
9+
10+
[] a sh:ValidationReport;
11+
rdf4j:truncated false;
12+
sh:conforms true .

0 commit comments

Comments
 (0)