Relations
Relations describe connections between two entities as a directed triple:
subject -> predicate -> object
The predicate defines the semantics and how to interpret the relation from the
subject's perspective. For example, Asset A -> found_by_gateway -> Gateway B
means that Asset A has been found by Gateway B and not the other way around.
The subject is the entity the relation starts from, and the object is the
entity the relation points to. Swapping them changes the meaning of the relation.
Relation Entities
An asset is managed by Inventory. A node belongs to an external information
domain, such as the Context Hierarchy Service. From Inventory's perspective, a node
is an external pointer.
When the Inventory API receives a POST /relations, it validates the
UUID syntax of both entity IDs. It then resolves and checks permissions for an entity
only when its type is asset, regardless of whether it is the subject or the object.
It does not verify that an external node exists, because a node references an
external scope that might not have a physical or digital representation.
Consequently:
- A missing asset subject or object returns an error.
- The caller must have read and write permissions for every referenced asset in that asset's access group.
- A node ID is not checked for existence by Inventory; the external service owns that validation.
Origins and Creation
Relations have two origins: IAH can create them automatically from system events, or users can create them explicitly through the Inventory API.
System-Managed Relations
IAH creates or updates these relations automatically. The source identifies the process that created the relation. The source value identifies the specific source instance where applicable.
| Predicate | Meaning | Subject | Object | Source type | Source value |
|---|---|---|---|---|---|
found_by_gateway |
An asset was found by a gateway. | Discovered asset | Gateway asset | discovery |
Discovery source, for example siemens.cdm.dcd.sat |
found_by_asset_link |
An asset was found by an Asset Link. | Discovered asset | Asset Link asset | discovery |
Discovery source, for example siemens.cdm.dcd.sat |
is_installed_on |
An Asset Link is installed on a gateway. Installation does not imply that the Asset Link is currently running. | Asset Link asset | Gateway asset | assetlinkdiscovery |
siemens.cdm.gateway.assetlinkdiscovery |
is_running_on |
An Asset Link is currently running on a gateway. This represents the current runtime state. | Asset Link asset | Gateway asset | assetlinkdiscovery |
siemens.cdm.gateway.assetlinkdiscovery |
The direction is significant. For example: PLC -> found_by_gateway -> Gateway
means that the gateway found the PLC, while: Asset Link -> is_running_on -> Gateway
means that the Asset Link is currently running on the gateway.
System relations may be deleted manually, but the system will recreate or update them when the related event occurs again.
User-Managed Relations
Create a relation with the Inventory API's POST /relations endpoint. The request must contain subject,
predicate, and object. The optional source.value identifies the source; the
source type is set to manual by Inventory to distinguish between manual and system-managed relations.
isBidirectional: true also creates the reverse relation atomically.
Creating a relation requires the global permission
glb_inventory_write_relation_data. Every referenced asset also requires
inventory_read_asset_data and inventory_write_asset_data in its access group.
Nodes have no Inventory access group check.
Recommended predicates such as is_module_of and is_periphery_of can be
used for user-managed relations between an asset and related child items. These
predicates retain their meaning across contexts. Examples are:
| Predicate | Meaning | Example origin |
|---|---|---|
is_module_of |
An asset is a submodule of another asset. | SAT Asset Link or Proneta Service |
is_periphery_of |
An asset is a periphery of another asset. | SAT Asset Link or Proneta Service |
Example asset-to-asset relation:
{
"data": {
"subject": { "type": "asset", "id": "<asset-id>" },
"predicate": "is_connected_to",
"object": { "type": "asset", "id": "<other-asset-id>" },
"isBidirectional": true
}
}
Example asset assignment to a context node:
{
"data": {
"subject": { "type": "asset", "id": "<asset-id>" },
"predicate": "is_assigned_to_node",
"object": { "type": "node", "id": "<node-id>" }
}
}
Creating the same relation again updates its timestamp instead of creating a duplicate. Relations cannot be edited; delete and recreate an incorrect relation.
Create Relations with an Asset
The Inventory API can also create or update user-managed asset-to-asset relations
when creating or updating an asset with POST /assets. Add an asset_relations
array to data.asset and identify the related asset by its asset identifiers. The
asset relation also supports is_bidirectional:
The following is a minimal v2 example asset payload. Both the current asset and the
related asset use the CustomIdentifier name ExternalSystemID; the related asset
must already exist in the inventory.
{
"data": {
"asset": {
"functional_object_type": "Device",
"functional_object_schema_url": "https://industrial-assets.io/schemas/iah/base-schema/released/v1/iah-base.json",
"asset_identifiers": [
{
"asset_identifier_type": "CustomIdentifier",
"name": "ExternalSystemID",
"value": "asset-a"
}
],
"asset_relations": [
{
"predicate": "is_part_of",
"related_asset": {
"asset_identifiers": [
{
"asset_identifier_type": "CustomIdentifier",
"name": "ExternalSystemID",
"value": "asset-b"
}
]
},
"relational_role_of_related_asset": "object",
"is_bidirectional": false
}
]
}
}
}
The asset in data.asset takes the opposite role. With
relational_role_of_related_asset: "object", the relation is:
asset-a -> is_part_of -> asset-b
Set the value to subject to reverse the direction. The relation source is taken
from data.meta.source; unlike POST /relations, it is not always set to manual.
This feature creates asset-to-asset relations only; use the Inventory API's
POST /relations endpoint for relations involving nodes. The relation is skipped if
the related asset cannot be found or the caller lacks the required permissions. The
asset creation or update can still succeed.
Consumer Side: Read and Filter Relations
Use GET /relations with filter, limit, and offset. It requires the global
permission glb_inventory_read_relation_data. Filters use OData-inspired syntax and
must be URL encoded in the request. The following readable filters are shown before
encoding:
| Filter | Purpose |
|---|---|
subject.type eq 'asset' and subject.id eq '<asset-id>' |
Relations starting at an asset |
object.type eq 'asset' and object.id eq '<asset-id>' |
Relations ending at an asset |
object.type eq 'node' and predicate eq 'is_assigned_to_node' |
Asset assignments to nodes |
predicate eq 'found_by_gateway' |
Assets found by gateways |
object.id eq '<gateway-id>' and predicate eq 'is_installed_on' |
Asset Links installed on a gateway |
object.id eq '<gateway-id>' and predicate eq 'is_running_on' |
Asset Links currently running on a gateway |
source.type eq 'manual' and predicate eq 'is_assigned_to_node' |
Manual node assignments |
For example, find the gateway that found an asset. The human-readable query is:
GET /relations?filter=subject.type eq 'asset' and subject.id eq '<asset-id>' and predicate eq 'found_by_gateway'
Use its URL-encoded form in the request:
GET /relations?filter=subject.type%20eq%20%27asset%27%20and%20subject.id%20eq%20%27<asset-id>%27%20and%20predicate%20eq%20%27found_by_gateway%27
Use GET /assets?relationsFilter=... to return assets matching their relations. An
asset ID used as subject.id returns matching relation objects; an asset ID used as
object.id returns matching relation subjects. Without a concrete anchor, matching
asset sides are returned, while node sides are not returned as assets.
For example, retrieve readable assets assigned to nodes. The human-readable query is:
GET /assets?relationsFilter=object.type eq 'node' and predicate eq 'is_assigned_to_node'
Use its URL-encoded form in the request:
GET /assets?relationsFilter=object.type%20eq%20%27node%27%20and%20predicate%20eq%20%27is_assigned_to_node%27
Deletion
Deleting an asset automatically deletes every relation in which that asset is the subject or object. This also removes relations that point to the asset from an external entity.
Deleting an external node does not delete its relations in Inventory, because Inventory does not know about the actual entity behind a node's UUID.
Additionally, delete relations explicitly with two endpoints:
DELETE /relationsdeletes up to 50 relations by ID.DELETE /relations/query-resultsdeletes matching relations, up to 100 per request.
Both endpoints require the global permissions
glb_inventory_read_relation_data and glb_inventory_write_relation_data.
The query-result endpoint requires a narrowing filter, such as the following human-readable query:
relationsFilter=object.type eq 'node' and object.id eq '<node-id>' and predicate eq 'is_assigned_to_node'
Use its URL-encoded form in the request:
relationsFilter=object.type%20eq%20%27node%27%20and%20object.id%20eq%20%27<node-id>%27%20and%20predicate%20eq%20%27is_assigned_to_node%27
Repeat the request while the response reports more deletable relations than deleted relations.