为何GraphDB会拒绝OGC GeoSparql SHACL约束?
背景
GraphDB支持对传入及现有数据执行SHACL验证,OGC为GeoSPARQL标准提供了配套的SHACL shapes文件,其中包含验证通过和未通过的测试数据。
已为仓库启用SHACL和GeoSPARQL插件,使用默认命名图存储shapes,但在将shapes插入默认SHACL形状图http://rdf4j.org/schema/rdf4j#SHACLShapeGraph时,抛出以下异常:
org.eclipse.rdf4j.repository.RepositoryException: org.eclipse.rdf4j.sail.SailException: Invalid shacl constraint pattern: "^\s*$|^\s*({)(.*)(})\s*$"^^<http://www.w3.org/2001/XMLSchema#string>
未在shapes文件中找到该正则表达式,且用pyshacl处理这些shapes时无异常,怀疑是GraphDB的兼容性问题。
完整堆栈跟踪:
21:01:53.284 [repository-manager-6] INFO c.o.rio.parallel.ParallelLoader - Using 1 threads for inference 21:01:53.350 [repository-manager-6] INFO o.eclipse.rdf4j.sail.shacl.ShaclSail - Shapes will be persisted in: /Users/thomas/Library/Application Support/GraphDB/data/repositories/GeoSPARQL-SHACL-Test/shapes-graph/ 21:01:58.359 [repositories/GeoSPARQL-SHACL-Test] INFO o.e.r.h.s.r.n.NamespacesController - GET namespace list 21:01:58.359 [repositories/GeoSPARQL-SHACL-Test] INFO o.e.r.h.s.r.n.NamespacesController - GET namespace list 21:01:58.363 [repositories/GeoSPARQL-SHACL-Test] INFO o.e.r.h.s.r.TupleQueryResultView - Request for query 3392903 is finished 21:01:58.364 [repositories/GeoSPARQL-SHACL-Test] INFO o.e.r.h.s.r.TupleQueryResultView - Request for query 3392903 is finished 21:02:15.760 [import-task-GeoSPARQL-SHACL-Test-1] ERROR c.o.f.impex.StringImportRunnableTask - Could not import file org.eclipse.rdf4j.repository.RepositoryException: org.eclipse.rdf4j.sail.SailException: Invalid shacl constraint pattern: "^\s*$|^\s*({)(.*)(})\s*$"^^<http://www.w3.org/2001/XMLSchema#string> at org.eclipse.rdf4j.repository.sail.SailRepositoryConnection.addWithoutCommit(SailRepositoryConnection.java:398) at com.ontotext.trree.monitorRepository.MonitorRepositoryConnection.addWithoutCommit(MonitorRepositoryConnection.java:367) at org.eclipse.rdf4j.repository.base.AbstractRepositoryConnection.add(AbstractRepositoryConnection.java:436) at org.eclipse.rdf4j.repository.util.RDFInserter.addStatement(RDFInserter.java:64) at org.eclipse.rdf4j.repository.util.AbstractRDFInserter.handleStatement(AbstractRDFInserter.java:184) at org.eclipse.rdf4j.rio.turtle.TurtleParser.reportStatement(TurtleParser.java:1123) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parseObject(TurtleParser.java:454) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parseObjectList(TurtleParser.java:375) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parsePredicateObjectList(TurtleParser.java:370) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parseTriples(TurtleParser.java:335) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parseStatement(TurtleParser.java:203) at org.eclipse.rdf4j.rio.turtle.TurtleParser.parse(TurtleParser.java:165) at org.eclipse.rdf4j.repository.util.RDFLoader.loadInputStreamOrReader(RDFLoader.java:304) at org.eclipse.rdf4j.repository.util.RDFLoader.load(RDFLoader.java:249) at org.eclipse.rdf4j.repository.base.AbstractRepositoryConnection.add(AbstractRepositoryConnection.java:351) at com.ontotext.trree.monitorRepository.MonitorRepositoryConnection.add(MonitorRepositoryConnection.java:175) at com.ontotext.trree.parallel.InterruptableConnectionWrapper.add(InterruptableConnectionWrapper.java:60) at com.ontotext.forest.impex.ParallelAwareImporter.lambda$add$3(ParallelAwareImporter.java:96) at com.ontotext.forest.impex.ParallelAwareImporter.wrapInBeginCommit(ParallelAwareImporter.java:143) at com.ontotext.forest.impex.ParallelAwareImporter.add(ParallelAwareImporter.java:96) at com.ontotext.forest.impex.StringImportRunnableTask.load(StringImportRunnableTask.java:34) at com.ontotext.forest.impex.ImportRunnableTask.run(ImportRunnableTask.java:85) at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) at java.base/java.util.concurrent.FutureTask.run(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) at java.base/java.lang.Thread.run(Unknown Source) Caused by: org.eclipse.rdf4j.sail.SailException: Invalid shacl constraint pattern: "^\s*$|^\s*({)(.*)(})\s*$"^^<http://www.w3.org/2001/XMLSchema#string> at org.eclipse.rdf4j.sail.shacl.GraphDBShaclSailConnection.validateShaclPattern(GraphDBShaclSailConnection.java:119) at org.eclipse.rdf4j.sail.shacl.GraphDBShaclSailConnection.addStatement(GraphDBShaclSailConnection.java:74) at org.eclipse.rdf4j.repository.sail.SailRepositoryConnection.addWithoutCommit(SailRepositoryConnection.java:394) ... 26 common frames omitted Caused by: java.util.regex.PatternSyntaxException: Illegal repetition near index 12 ^\s*$|^\s*({)(.*)(})\s*$ ^ at java.base/java.util.regex.Pattern.error(Unknown Source) at java.base/java.util.regex.Pattern.closure(Unknown Source) at java.base/java.util.regex.Pattern.sequence(Unknown Source) at java.base/java.util.regex.Pattern.expr(Unknown Source) at java.base/java.util.regex.Pattern.group0(Unknown Source) at java.base/java.util.regex.Pattern.sequence(Unknown Source) at java.base/java.util.regex.Pattern.expr(Unknown Source) at java.base/java.util.regex.Pattern.compile(Unknown Source) at java.base/java.util.regex.Pattern.<init>(Unknown Source) at java.base/java.util.regex.Pattern.compile(Unknown Source) at org.eclipse.rdf4j.sail.shacl.GraphDBShaclSailConnection.validateShaclPattern(GraphDBShaclSailConnection.java:116) ... 28 common frames omitted
问题原因
核心问题出在Java正则表达式引擎与Python正则引擎的行为差异:
- 报错的正则
^\s*$|^\s*({)(.*)(})\s*$中,{是Java正则的特殊字符,用于指定重复次数(如{2,5}),未转义的{会被视为非法重复标记,触发PatternSyntaxException。 - pyshacl基于Python的
re模块,该模块在遇到未转义的{时,若后续没有合法的重复数字/格式,会自动将其视为字面字符,因此不会报错。
你在shapes文件中找不到该正则,大概率是OGC的SHACL shapes在处理GeoSPARQL约束时,由GraphDB内部生成了这个未转义的正则表达式,或者是shapes中某个隐式的正则约束被解析后出现了这个问题。
解决方案
手动修正正则约束:
定位OGC SHACL shapes中所有带有sh:pattern的约束,检查是否存在未转义的{或},将其替换为\{和\}(注意在Turtle等RDF格式中,反斜杠需要转义为\\,即最终写成\\{和\\})。升级GraphDB版本:
该问题可能是GraphDB的GeoSPARQL与SHACL插件交互的兼容性bug,尝试升级到最新版本,看官方是否已修复该问题。临时绕过方案:
先禁用仓库的SHACL验证,导入shapes文件后,手动修改GraphDB中对应形状的正则约束,再重新启用SHACL验证。
内容的提问来源于stack exchange,提问作者user9465104

