Extending the core data model via GraphQL
Cognite's core data model offers standardized building blocks for industrial data, forming the basis for more specialized models. This article outlines how to use the Data modeling language extension for GraphQL (DML) to create a custom data model that extends the core data model.
Import types from the core data model
By default, new custom data models don't have any type definitions. Before extending the core data model, you must import the type definitions you need from the core data model into the custom data model.
For example, to include the CogniteSchedulable feature from the core data model in your custom data model:
-
Copy the type definition from the core data model to your custom data model:
interface CogniteSchedulable
@container(
indexes: [
{ identifier: "startTime", fields: ["startTime"], cursorable: true }
{ identifier: "endTime", fields: ["endTime"], cursorable: true }
{
identifier: "scheduledStartTime"
fields: ["scheduledStartTime"]
cursorable: true
}
{
identifier: "scheduledEndTime"
fields: ["scheduledEndTime"]
cursorable: true
}
]
) {
startTime: Timestamp
endTime: Timestamp
scheduledStartTime: Timestamp
scheduledEndTime: Timestamp
} -
Specify which data model you want to import the view definition from. Use the
@import
directive to import the type definition from another type and to decide which data model and version to import from.For example, to import the view from the core data model instead of creating a new view with the same schema in the custom data model:
interface CogniteSchedulable
@container(
indexes: [
{ identifier: "startTime", fields: ["startTime"], cursorable: true }
{ identifier: "endTime", fields: ["endTime"], cursorable: true }
{
identifier: "scheduledStartTime"
fields: ["scheduledStartTime"]
cursorable: true
}
{
identifier: "scheduledEndTime"
fields: ["scheduledEndTime"]
cursorable: true
}
]
)
@import(
dataModel: { externalId: "CogniteCore", version: "v1", space: "cdf_cdm" }
) {
startTime: Timestamp
endTime: Timestamp
scheduledStartTime: Timestamp
scheduledEndTime: Timestamp
}
If you copy the type definition without the @import
directive and publish the data model, you create a new view and container in your custom data model space independent from the core data model. You won't be able to access the data of the views stored in the core data model from your custom data model. Also, functionality such as path materialization of CogniteAsset won't work in your custom data model.
Resolve transitive view dependencies
When you import a type from another data model, you must also import all the views it depends on. For example, to import a "Pump" view that has a direct relation to a "Location" view:
-
Add the type definition for "Location" to your data model:
type Pump
@import(
dataModel: {
externalId: "my-custom-datamodel"
version: "v1"
space: "my-custom-space"
}
) {
name: String
location: Location
} -
The data model won't validate since the "Location" type isn't defined yet. To define it, you need to import the "Location" view in the same way you imported "Pump":
type Pump
@import(
dataModel: {
externalId: "imaginary-datamodel"
version: "v1"
space: "imaginary-space"
}
) {
name: String
location: Location
}
type Location
@import(
dataModel: {
externalId: "my-custom-datamodel"
version: "v1"
space: "my-custom-datamodel"
}
) {
name: String
}
Extend imported views
When you've imported the necessary types from the core data model, you can extend them to match your needs.
This example extends the CogniteAsset concept from the core data model with a new department
field:
-
Import CogniteAsset into your data model:
type CogniteAsset implements CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1")
@import {
# ... omitted CogniteAsset fields
} -
Define a
CustomCogniteAsset
type that implements the CogniteAsset type:type CustomCogniteAsset implements CogniteAsset & CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1") {
# ... omitted CogniteAsset fields
department: String
}
interface CogniteAsset implements CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1")
@import {
# ... omitted CogniteAsset fields
}
In the example above:
- We removed the
@import
fromCustomCogniteAsset
because the type is owned by the custom data model and not imported from the core data model like theCogniteAsset
type. - We changed
CogniteAsset
to be an interface instead of a type. The GraphQL specification requires this, and it has no impact on the functionality of the importedCogniteAsset
type. - We included all the fields from the
CogniteAsset
on theCustomCogniteAsset
. The GraphQL specification requires that all fields of the base interface are included when implementing it in another type.
Override types for edges and direct relations
When you have defined an extension to an existing type, you typically want any direct relation fields that reference the base type to reference the extension type instead.
This example defines an extension of CogniteAsset
called CustomCogniteAsset
:
type CustomCogniteAsset implements CogniteAsset & CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1")
@import {
# ... omitted CogniteAsset fields
parent: CogniteAsset @mapping(property: "assetHierarchy_parent")
}
The parent direct relation still references the base type (CogniteAsset
). If you query for data on the parent direct relation via CustomCogniteAsset
, you won't be able to access the data of the department
field since it only exists in CustomCogniteAsset
.
To solve this issue, you need to manually override the type of the parent direct relation on the CustomCogniteAsset
to reference the CustomCogniteAsset
type instead of the base type:
type CustomCogniteAsset implements CogniteAsset & CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1")
@import {
# ... omitted CogniteAsset fields
parent: CustomCogniteAsset @mapping(property: "assetHierarchy_parent")
}
You can only change the type of a field from the base type like this if you're overriding the existing type with a type that implements it. For example, you can't change the parent field type to CogniteEquipment because CogniteEquipment doesn't extend CogniteAsset.