On a odrl:refinement over an Action / Asset Collection / Party Collection
In the examples (14), in JSON we have the following
"action": [{
"rdf:value": { "@id": "odrl:print" },
"refinement": [{
"leftOperand": "resolution",
"operator": "lteq",
"rightOperand": { "@value": "1200", "@type": "xsd:integer" },
"unit": "http://dbpedia.org/resource/Dots_per_inch"
}]
}]
When transformed to TTL:
odrl:action ex:URI_23244A;
ex:URI_23244A a odrl:Action; // <== this node is not an Action
rdf:value odrl:print;
odrl:refinement ex:URI_23245A.
ex:URI_23245A a odrl:Constraint;
odrl:leftOperand odrl:resolution;
odrl:operator odrl:lteq;
odrl:unit "http://dbpedia.org/resource/Dots_per_inch"^^xsd:string;
odrl:rightOperandReference ex:URI_23246R. // not included
The class of md:URI_23244A surfaces (implicit in JSON, hidden under the opening "[{" ).
When the example is swapped from an odrl:action to either odrl:target or odrl:function, it becomes confusing:
// ----------------------------------------------------------------
odrl:target ex:URI_23244A;
ex:URI_23244A a odrl:AssetCollection; // <== this is not an AssetCollection
rdf:value ex:SomeAssetCollection; // <== this is the collection
odrl:refinement ex:URI_23245A.
// ----------------------------------------------------------------
odrl:function ex:URI_23244A;
ex:URI_23244A a odrl:PartyCollection; // <== this is not an PartyCollection
rdf:value ex:SomePartyCollection; // <== this is the collection
odrl:refinement ex:URI_23245A.
// ----------------------------------------------------------------
From the diagram, the issue is:
When you transform Example 14 to turtle, you would get:
<http://example.com/policy:6161> a odrl:Offer ;
odrl:profile <http://example.com/odrl:profile:10> ;
odrl:assigner <http://example.com/org:616> ;
odrl:target <http://example.com/document:1234> ;
odrl:permission [
odrl:action [
rdf:value odrl:print ;
odrl:refinement [
odrl:leftOperand odrl:resolution ;
odrl:operator odrl:lteq ;
odrl:rightOperand "1200"^^xsd:integer ;
odrl:unit "http://dbpedia.org/resource/Dots_per_inch"^^xsd:string
]
] ;
] .
That's the compressed syntax with anonymous nodes and leaves the node rdf:Class hidden (not clear if i had multiple nodes pointing at the same refinement for example), can we use the extended syntax (that's what comes out of the library, and that will tell me what is that I need to add when there is a refinement)
odrl:print is an instance of the class odrl:Action.
If you need the object of an RDF triple referring to "printing", using that URI ("odrl:print") is enough and simple. If you need to say more things about that fact of printing, we need another RDF resource, subject of other triples that refine it. Indeed, the first and foremost of such triples is the fact that we still referring to the printing action (odrl:print). We do this by means of the rdf:value, as illustrated in the example above. Indeed, in Turtle or in JSON-LD we can have such a resource fully anonymous, if you like it.
This is succintly explained in Section 2.5.4 of the ODRL model. Is the 'note' in that section enough (and the examples above in that section)? Or do you think it needs further clarification?
The actions are a bit of a red herring. I am not talking about the property that points at the action, I am concerned about the "wrapper" class for the refinements. I think it needs further clarification, ideally in "fully qualified domains" as that is what the libraries need to add triples (even if you can then output "succinct").
As I explained above (copy/paste again), I have a refinement for an AssetCollection or a PartyCollection, odrl:target or odrl:function are not "pointing at " an AssetCollection or PartyCollection, they are "pointing at something else" and that "something else" now has an "rdf:Value".
// ----------------------------------------------------------------
odrl:target ex:URI_23244A; // <== the diagram says this is "pointing at an AssetCollection"
ex:URI_23244A a odrl:AssetCollection; // <== this is not an AssetCollection
rdf:value ex:SomeAssetCollection; // <== this is the collection
odrl:refinement ex:URI_23245A.
// ----------------------------------------------------------------
odrl:function ex:URI_23249A; // <== the diagram says this is "pointing at a PartyCollection"
ex:URI_23249A a odrl:PartyCollection; // <== this is not an PartyCollection
rdf:value ex:SomePartyCollection; // <== this is the collection
odrl:refinement ex:URI_23245A.
// ----------------------------------------------------------------
In other words: If I was building the SHACL for the classes that are the target for odrl:refinement - they are "not currently defined".
The class you are looking for is odrl:Action (if the refinement is of an Action). Please note that the odrl:action property, has defined as rdfs:range the Action class. This means, according to the RDFS semantics, that "any object in a triple where odrl:action is the property, has class odrl:Action". We can explicitly declare that, if you like, but knowing that it can be inferred, it can be omitted.
In the examples you post, from the odrl:target property, we can infer that ex:URI_23244A is an odrl:Asset (or any of its subclasses, as odrl:AssetCollection). Therefore, in your examples, you do well by saying that ex:URI_23244A is a odrl:AssetCollection: in this case there was an ambiguity.
I think the rdf:value properties should be odrl:source properties.
Forget the action example ...
@riannella @vroddon I believe we might be talking about different things, I will elaborate the example further with a couple of diagrams.
Somewhere, someone defines an asset collection:
md:some_catalog terms:accessRights odrl:distribute,
odrl:use;
terms:accrualPeriodicity prov:freq-N;
terms:created "2024-08-22T14:08:23.0000000Z"^^xsd:dateTime;
terms:description "this is an example of a catalog"@en;
a odrl:AssetCollection,
fibo-fnd-acc-aeq:FinancialAsset,
dcat:Catalog;
owl:versionInfo "1.0"@en;
prov:generatedBy "[email protected]"^^xsd:anyURI;
prov:version "1.0"@en;
dprod:informationSensitivityClassification md:public.
Now we move to the policy creation under ODRL:
- According to the diagram
odrl:targetshould point at anodrl:AssetCollection(or action or partycollection). - My policy user has two rules: a permission and a prohibition, the permission refines
md:some_catalog, the prohibition doesn't.
The permission's target:
odrl:target ex:URI_23244A; // <== this is pointing at a **REFINEMENT** to an AssetCollection
The prohibition's target:
odrl:target md:some_catalog; // <== this is "pointing" at an AssetCollection
So ex:URI_23244A is not an AssetCollection, and the refinement should not be part of the asset's triples, because the ex:URI_23245A is a property of the refinement in the permission and not a property of the asset itself.
ex:URI_23244A a odrl:AssetCollection; // <== this is not an AssetCollection
rdf:value md:some_catalog; // <== this is the collection
odrl:refinement ex:URI_23245A.
I would argue that odrl:target md:some_catalog; is an abstraction that is a shortcut and "hiding" an intermediate node because it is not required, but it could also be represented:
When you say:
odrl:target ex:URI_23244A; // <== this is pointing at a **REFINEMENT** to an AssetCollection
How can this be?
The range of target is Asset ?
The target might be defined in writing as an odrl:AssetCollection, but when you have 2 rules pointing at the same asset, with different requirements (one refinement, one not) and you lay it in a graph (as above) that the range comes into question.
I aim to show in the diagrams above: that it isn't in the range of an asset in the graph. There is a node that in a "single rule JSON" can look like it is implicit, but not in FQN RDF with multiple rules and different refinements on an element (in this example an AssetCollection.
To start, I hope we agree that ex:constraints is not a property of the odrl:AssetCollection, that is a requirement specified by a rule ex:some_permission within a policy, other rules within the same policy (ex:some_prohibition) could have different requirements (or not) and address the same asset.
When you output the RDF that includes an asset and a policy, in the example above ex:some_catalog doesn't have ex:constraints as properties (otherwise ex:some_prohibition should also consider them?)
As per the example above, this is a DCAT catalog:
ex:some_catalog terms:accessRights odrl:distribute,
odrl:use;
terms:accrualPeriodicity prov:freq-N;
terms:created "2024-08-22T14:08:23.0000000Z"^^xsd:dateTime;
terms:description "this is an example of a catalog"@en;
a odrl:AssetCollection,
dcat:Catalog;
owl:versionInfo "1.0"@en;
prov:generatedBy "[email protected]"^^xsd:anyURI;
prov:version "1.0"@en;
dprod:informationSensitivityClassification md:public.
And we can create a policy:
ex:policy terms:created "2024-10-05T06:41:47.0000000Z"^^xsd:dateTime;
terms:creator "some UUID"^^xsd:anyURI;
terms:description "Example of an Offer"@en;
terms:valid md:uri:225b96258645:23241I;
a odrl:Offer;
owl:versionInfo "0.1"@en;
odrl:conflict odrl:prohibit;
odrl:permission ex:demo_permission;
odrl:prohibition ex:demo_prohibition;
prov:version "0.1"@en.
The prohibition does look like the target is just the ex:some_catalog which is an odrl:AssetCollection:
ex:demo_prohibition a odrl:Prohibition;
odrl:action odrl:distribute;
odrl:target ex:some_catalog.
But you can't "attach" the refinement to ex:some_catalog for the permission:
ex:demo_permission a odrl:Permission;
odrl:action odrl:play;
odrl:target ex:some_catalog. // the refinement shouldn't be a 'property' of the asset
// otherwise it will be triggered by other rules
It needs a class that binds the semantics "for this rule, the target has this refinement":
ex:demo_permission a odrl:Permission;
odrl:action odrl:play;
odrl:target ex:URI_23244A;
But ex:URI_23244A is not an asset ... is the binder for the permission that says "this asset has this refinement"
ex:URI_23244A a odrl:AssetCollection; // <== this is not an AssetCollection, nor is the target
rdf:value ex:some_catalog; // <== this is the target AssetCollection
odrl:refinement ex:some_constraints.
IMHO: odrl:target / odrl:function / odrl:action should have more detailed definitions for range.
A refinement is a type of odrl:Constraint (or odrl:LogicalConstraint)
So you can create an instance of the refinement, then only refer to it in the Permission.
Like:
ex:someCatalog rdf:type odrl:Asset, odrl:AssetCollection .
ex:policy01 rdf:type odrl:Offer ;
odrl:permission ex:perm01 ;
odrl:prohibition ex:prohib01 .
ex:perm01 rdf:type odrl:Permission ;
odrl:action odrl:play ;
odrl:target [ rdf:type odrl:AssetCollection ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine01 . ] .
ex:prohib01 rdf:type odrl:Prohibition ;
odrl:action odrl:distribute ;
odrl:target ex:someCatalog .
ex:refine01 rdf:type odrl:Constraint ;
odrl:leftOperand odrl:resolution ;
odrl:operator odrl:lteq ;
odrl:rightOperand [ rdf:type xsd:integer ; rdf:value "1200" ] .
When you say "you can" - I can't, as I explained yesterday, the library is asking for a node "to attach" the refinement.
A text file like the one you produced above looks like it works, but in a graph it doesn't make sense.
ex:perm01 rdf:type odrl:Permission ;
odrl:action odrl:play ;
odrl:target [ rdf:type odrl:AssetCollection ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine01 . ] .
As per the graph below odrl:target is implying that the node "????" is an odrl:AssetCollection?
When I add another rule with a the same topology (target-refinement) over the same asset, but different refinement:
ex:prohib01 rdf:type odrl:Prohibition ;
odrl:action odrl:distribute ;
odrl:target ex:someCatalog .
ex:perm02 rdf:type odrl:Permission ;
odrl:action odrl:aggregate ;
odrl:target [ rdf:type odrl:AssetCollection ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine02. ] .
I would be interested in seeing what result a SPARQL query returns on how many odrl:AssetCollection elements now exist.
IMHO - these look very confusing as it appear to attach properties ("odrl:refinement") to the AssetCollection.
Seems to work fine when I load into GraphDB
And the SPARQL
ex:someCatalog rdf:type odrl:Asset, odrl:AssetCollection .
ex:policy01 rdf:type odrl:Offer ;
rdfs:label "Policy 01" ;
odrl:permission ex:perm01, ex:perm02 ;
odrl:prohibition ex:prohib01 .
ex:perm01 rdf:type odrl:Permission ;
rdfs:label "Permission 01" ;
odrl:action odrl:play ;
odrl:target ex:perm01-target .
ex:perm01-target rdf:type odrl:AssetCollection ;
rdfs:label "Perm01 Target" ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine01 .
ex:refine01 rdf:type odrl:Constraint ;
rdfs:label "Refinement 01" ;
odrl:leftOperand odrl:resolution ;
odrl:operator odrl:lteq ;
odrl:rightOperand ex:refine01-rightOp .
ex:refine01-rightOp rdf:type xsd:integer ;
rdfs:label "Refine01 RightOp" ;
rdf:value "1200" .
ex:perm02 rdf:type odrl:Permission ;
rdfs:label "Permission 02" ;
odrl:action odrl:play ;
odrl:target ex:perm02-target .
ex:perm02-target rdf:type odrl:AssetCollection ;
rdfs:label "Perm02 Target" ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine02 .
ex:refine02 rdf:type odrl:Constraint ;
rdfs:label "Refinement 02" ;
odrl:leftOperand odrl:resolution ;
odrl:operator odrl:gteq ;
odrl:rightOperand ex:refine02-rightOp .
ex:refine02-rightOp rdf:type xsd:integer ;
rdfs:label "Refine02 RightOp" ;
rdf:value "32000" .
ex:prohib01 rdf:type odrl:Prohibition ;
odrl:action odrl:distribute ;
odrl:target ex:someCatalog .
This just showed exactly the problem where I started in March. Are those ex:perm-target asset collections (are they managed in the asset section?), Do people need to know about the existence of policies to create a clean query? it doesn't seem to flow as the ODRL diagram (none of those -target point at a policy).
The query should return 1 asset/assetCollection - ex:someCatalog, but it has returned 3:
ex:someCatalog rdf:type odrl:Asset, odrl:AssetCollection .
ex:perm01-target rdf:type odrl:AssetCollection ;
rdfs:label "Perm01 Target" ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine01 .
ex:perm02-target rdf:type odrl:AssetCollection ;
rdfs:label "Perm02 Target" ;
odrl:source ex:someCatalog ;
odrl:refinement ex:refine02 .
It might appear as a good binding from an ODRL perspective.
But from the asset manager's perspective how is it justified semantically?, the -target nodes are now unmanaged, redundant and somewhat unlinked to the ex:someCatalog (responsibility principles would dictate asset creations are managed by the "asset manager role", not the "policy manager role") - does the 'ex:someCatalog need to have a property that says ex:surrogate-target to keep track to those for completeness? I don't think so.
Further, when you generate the JSON-LD (because this is what is used by most API), parsing is cluttered - there are 3 entries for odrl:AssetCollection, need to figure out which is the actual asset. Now it also is waste of code, and waste of execution time.
If the purpose of making it a -Collection is to represent that the "output" of the refinement is still -Collection, that is unnecessary (just like SPARQL can output anything, a refinement should output anything) - the outcome of the query is when applying the refinement, the result shouldn't be an empty set (practically: true/false), not an actual list.
This is about end-to-end lifecycle and IMHO, still stands: it is not elegant and will confuse developers just as it has done to me so far.
I fully understand @joshcornejo's point, there is something "logically" wrong about this SPARQL query returning three instances of AssetCollection, when only one has real existence. Either the factuality is marked, or a different model is seeked. I do not see a solution compatible with the current ODRL spec. Therefore, solving this problem would be a major change to the spec --discussion should be elevated to ODRL plennary meetings. More info in the wiki
In lieu that nobody is giving a path forward, this is my proposal:
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix ex: <http://example.org/> .
@prefix odrl: <http://www.w3.org/ns/odrl/2/> .
# Main new shape
ex:ShapeThatFixesRefinementIssue
a sh:NodeShape ;
sh:or (
[ sh:property [
sh:path odrl:relation ;
sh:or (
[ sh:class odrl:Asset ]
[ sh:class ex:Refinement ]
)
] ]
[ sh:property [
sh:path odrl:function ;
sh:or (
[ sh:class odrl:Party ]
[ sh:class ex:Refinement ]
)
] ]
[ sh:property [
sh:path odrl:action ;
sh:or (
[ sh:class odrl:Action ]
[ sh:class ex:Refinement ]
)
] ]
) .
# Shape for nodes of class ex:Refinement
ex:RefinementShape
a sh:NodeShape ;
sh:targetClass ex:Refinement ;
# One of these three must exist and match the correct class
sh:or (
[ sh:property [
sh:path odrl:relation ;
sh:class odrl:Asset
] ]
[ sh:property [
sh:path odrl:function ;
sh:class odrl:Party
] ]
[ sh:property [
sh:path odrl:action ;
sh:class odrl:Action
] ]
) ;
# refinement property must be a Constraint or LogicalConstraint
sh:property [
sh:path ex:refinement ;
sh:or (
[ sh:class odrl:Constraint ]
[ sh:class odrl:LogicalConstraint ]
)
] .
I am also aware that for declarative implementations (SPARQL) you will need to check that there's a match between the class inside a refinement and the property in the ex:ShapeThatFixesRefinementIssue.
... the issue is trivial on imperative implementations.