Salt la conținutul principal

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:

  1. 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
}
  1. Specify which data model you want to import the view definition from. Use the @import directive to import a view definition from another data model. The dataModel argument of the @import directive is used to specify which data model you are importing the view definition from. When you publish a type with the @import directive your current data model will be updated to include the definition of the view as it is defined in the source data model, as opposed to defining a similar but separate view definition in your current data model.
notă

A view definition cannot contain both an @import directive with a dataModel argument and a @view directive at the same time as this can create referencial conflicts when importing the view definition. You need to remove any @view directives from the view definition when you add @import as described above or you will not be able to publish the updated model.

For example, to import the CogniteSchedulable 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
}
notă

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:

  1. 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
}
  1. 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:

  1. Import CogniteAsset into your data model:
type CogniteAsset implements CogniteVisualizable & CogniteDescribable & CogniteSourceable
@view(space: "cdf_cdm", version: "v1")
@import {
# ... omitted CogniteAsset fields
}
  1. Define a CustomCogniteAsset type that implements the CogniteAsset type:
type CustomCogniteAsset implements CogniteAsset & CogniteVisualizable & CogniteDescribable & CogniteSourceable {
# ... 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 and the @view from CustomCogniteAsset because the type is owned by the custom data model and not imported from the core data model like the CogniteAsset 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 imported CogniteAsset type.
  • We included all the fields from the CogniteAsset on the CustomCogniteAsset. 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 {
# ... 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 {
# ... omitted CogniteAsset fields
parent: CustomCogniteAsset @mapping(property: "assetHierarchy_parent")
}
notă

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.