如何确保键值映射的键在 SHACL 中是唯一的?

How do I ensure keys of a key-value mapping are unique in SHACL?

假设我的数据模型是一个键值映射,例如,我 运行 一家高档酒店,想跟踪我的客人每顿饭的订单。我如何确保对于每顿饭,我得到每位客人的订单(即所有钥匙都存在)并且每位客人我只有一个订单(即所有键都是唯一的)?

让我们开始的示例代码:

形状:

ex:MealShape
    a sh:NodeShape ;
    sh:targetClass ex:Meal ;
    sh:property [
        sh:path ex:orders ;
        sh:minCount 2 ;
        sh:maxCount 2 ;
        sh:node ex:OrderShape ;
    ] .

ex:OrderShape
    a sh:NodeShape ;
    sh:targetClass ex:Order ;
    sh:property [
        sh:path ex:guest ;
        # The guest list! Code maintenance should happen here.
        sh:in ( "James" "Margaret" ) ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] ;
    sh:property [
        sh:path ex:order ;
        sh:datatype xsd:string ;
        sh:minCount 1 ;
        sh:maxCount 1 ;
    ] .

数据:

## Guests
ex:james ex:name "James" .
ex:margaret ex:name "Margaret" .

## Meals
### Valid meal
ex:breakfast
    a ex:Meal ;
    ex:orders [ ex:guest "James" ; ex:order "Eggs" ] ;
    ex:orders [ ex:guest "Margaret" ; ex:order "Cereal" ] .

### DESIRED TO BE invalid meal
### currently does not cause a validation result
ex:lunch
    a ex:Meal ;
    ex:orders [ ex:guest "James" ; ex:order "Salad" ] ;
    # Problem: James placed two orders, Maggie placed zero
    ex:orders [ ex:guest "James" ; ex:order "Burger" ] .

我知道的一种解决方案是对列表中的每个键分别使用 qualifiedShape 及其 minCount 和 maxCount 约束。然而,对于更大的 "guest lists" 这变得难以维护。在我的工作中,我有大约 40 个键的列表。使用 40 个 qualifiedShape 语句块,检查列表变得不切实际(我已经首先编写了这些语句的生成脚本)。

我搜索了文档,但找不到我想要的一种 "sh:disjointInScope" 或 "sh:uniqueFromList" 语句(重要的是,约束不应该强制值在整个数据图中是唯一的,因为例如詹姆斯可能会出现在几个订单中)。如何在人类可读的 SHACL 代码中获得所需的行为?

如果我正确理解了您的特定情况,那么这应该会导致违规:

ex:MealShape
  sh:property [
      sh:path ( ex:orders ex:guest ) ;
      sh:maxCount 2 ;
      sh:minCount 2 ;
  ] ...

这里的机制是使用路径表达式(例如:orders/ex:SPARQL 表示法中的客人)来说明每餐需要恰好有两个不同的客人,这也意味着他们每个人只能是一个订单的一部分。与 sh:in 结合使用,这可确保仅存在允许的键,并且所有键都存在。但是,您需要将 sh:in 列表的长度与 sh:min/max 计数对齐,所以我不确定这将如何管理。

您可以在 SHACL-SPARQL 的帮助下进一步概括此模式,例如介绍您正在谈论的 higher-level 约束组件。