C3 AI Documentation Home

Create an Entity Data Model

A C3 AI Application's data model is made up of Entity Types. All Entity Types mix Persistable, which provides the functionality required for manipulating and storing the Type's data in one of the C3 Agentic AI Platform's databases. Define an Entity Type in the ./src/ folder of your package using the following syntax:

Type
entity type MyEntityType {
    field1: string
    field2: int
    ...
}

Define relationships between entity types

Entity Types define relationships to other Entity Types using foreign key fields. When defining entity relationships, you create a corresponding foreign key in the database table backing the related Entity Type, allowing for performant queries when joining records for related Entity Types. The platform supports defining both one-to-many and one-to-one relationships. To define a one-to-many relationship between two Entity Types, use the following syntax:

Type
entity type MyParentType {
    ...
    myRelatedTypes: [MyRelatedType](myParentTypeId, id)
    ...
}

In the above example, the [MyRelatedType] component specifies a collection of the MyRelatedType Entity Type, and the (myParentTypeId, id)component specifies the field of the related MyRelatedType records that should be used as a foreign key reference to the parent MyParentType record.

You must also define the relationship to MyParentType in MyRelatedType.c3typ:

Type
entity type MyRelatedType {
    ...
    myParentTypeId: MyParentType
    ...
}

Store references to non-entity types

An Entity Type can also contain references to one or more Types that are not Entity Types:

Type
entity type MyEntityType {
    field1: string
    field2: int
    referenceType: ReferenceType
    ...
}

Persisting reference types allows more complex objects to be persisted in the database table that is created for MyEntityType, storing the field values of SomeType as columns in the MyEntityType database.

Extend entity types

Entity Types can be declared as extendable, which allows other entity types to inherit the fields and methods defined on the Entity Type. This is similar to "mix-ins", but provides additional information to the C3 Agentic AI Platform about how the Entity Type data should be stored most efficiently in the database.

You can define an extendable Entity Type with the following syntax:

Type
extendable entity type MyExtendableType {
    ...
    field1: string
    ...
}

To extend this type, define another Entity Type MyExtendingType.c3typ, which extends the previous Type:

Type
entity type MyExtendingType extends MyExtendableType type key "IDNTFR" {
    ...
    newField: string
    ...
}

The new Type contains all the fields and methods of MyExtendableType, along with an additional field newField. When an Entity Type is extended, instances of all Types that extend that Entity Types can be fetched from the parent Type, and the type key that was specified in the Type definition is used to discriminate between different Types that extend the extendable Type. For example, if you have defined two Types MyType1 and MyType2 that extend a Type called ExtendableType, then fetching these types has the following behavior:

  • Fetching ExtendableType returns instances of ExtendableType, MyType1, and MyType2
  • Fetching MyType1 returns instances of MyType1 only
    • This is similar to fetching on ExtendableType, filtering on the type key for MyType1
  • Fetching MyType2 returns instances of MyType2 only
    • This is similar to fetching on ExtendableType, filtering on the type key for MyType2

Extendable Types can be nested, providing multiple layers of inheritance, by defining an extendable type that extends another extendable type. For example, if you define MyExtendingType as extendable, you can define another type that extends MyExternalType:

Type
entity type DeeplyExtendingType extends `MyExtendableType` type key "DEEP" {
    ...
    deepField: string
    ...
}

When extendable Types are deeply nested, the Type keys are concatenated. This allows the following behavior to be upheld:

  • Fetching ExtendableType returns instances of ExtendableType, MyExtendingType, and DeeplyExtendingType
  • Fetching MyExtendingType returns return instances of MyExtendingType and DeeplyExtendingType
  • Fetching DeeplyExtendingType returns return instances of DeeplyExtendingType only

Model time series data

The C3 Agentic AI Platform offers an alternative key-value store that is highly optimized for managing and processing time series data. For more information on modeling time series data, see Modeling Time Series Data.

Advanced capabilities

Usually, the default behavior of the C3 Agentic AI Platform's database engine is sufficient to build a useful entity data model. However, sometimes it might be desirable to override the platform's default behavior to specify exactly how the data should be managed in the database. In these scenarios, the default behavior can be overridden by using specific syntax and annotations.

Specify database table and column names

When you create an Entity Type, the Platform generates a database table in the relational database using a default naming convention. However, in some scenarios, the entity type must specify exactly the expected schema of the corresponding database table.

For example, when connecting to an external database, use the schema name keyword to specify the name of the table and any fields:

Type
entity type MyEntityType schema name "MY_TYP" {
    field1: string schema name "DB_F1"
    field2: string
    ...
}

In the previous example, the Platform creates a "MY_TYP" table in the relational database, with one column "DB_F1" to store the field1 data, and another column whose name is decided by the Platform's naming scheme to store the field2 data. When using schema name, make sure that:

  • You only use the 26 alphabet letters with optional underscores as the values
  • The length of the schema name does not exceed 30 characters

Override default database engine behaviors

The Ann.Db Type provides several useful annotations for overriding the default behaviors of the database engine at the database, table, and column level. For example, each of the following parameters of the @db annotation allows for some default behavior to be overridden:

  • order - When data is fetched, specifies that the following order spec should be used for ordering
  • unique - Specifies that a uniqueness constraint should be created for the field(s)
  • index - Specifies that a database index should be created and maintained for the field(s)
  • include - Specifies which fields should be fetched for a Type reference, overriding the default behavior of fetching just id

For example, consider the following Entity Type definitions:

Type
@db(unique="comp1, comp2", order="name")
entity type MyType {

    comp1: string

    comp2: string

    @db(include="timestamp,value", order="descending(timestamp)")
    children: [ChildType] (parentId, id)
}

@db(index="timestamp")
entity type ChildType {

    parentId: MyType

    timestamp: datetime

    value: int
}

This creates two related Entity Types with the following special characteristics:

  • There is a composite uniqueness constraint set up for the comp1 and comp2 fields for MyType
  • When fetching MyType data, the results are sorted alphabetically by name
  • When fetching MyType data, the nested children includes the timestamp and value fields, sorted by timestamp from most recent to oldest
  • The ChildType table has an index set up on timestamp to ensure the performance of fetching MyType data

See the Ann.Db Type documentation for a complete list of supported database annotations.

Manipulate Entity Type data

The Persistable Type defines several methods available on all Entity Types for managing and manipulating data.

Create an instance of a Type

Use create() to create an instance of a Type. This method fails if the instance already exists.

JavaScript
MyEntityType.make(...).create();

The relational database in the C3 Agentic AI Platform does not enforce the existence of foreign key, so you do not need to consider the order in which related data is created in the database.

Update an instance of a Type

Use update() to update an instance of a Type (including null values). This method fails if the instance does not exist.

JavaScript
MyEntityType.make({...}).update();

Merge instances of a Type

Use merge() to create an instance of a Type if it does not exist, or merge if it does. Merging an instance updates the non-null field values in the input object. Null field values are ignored. Use include to explicitly control which field values are merged.

JavaScript
MyEntityType.make({...}).merge({include:"field1, field2..."});

Upsert an instance of a Type

Use upsert() to create an instance of a Type if it does not exist, or to update an instance of a Type if it exists.

upsert() sets values to null if the values do not exist in the Type instance that is being upserted if a database record previously existed. Use merge() instead if you want to avoid this behavior.

JavaScript
MyEntityType.make({...}).upsert();

Batch data manipulation for instances

You can use createBatch(), updateBatch(), upsertBatch(), mergeBatch(), and more to perform operations on several objects at the same time. Using batch operations allows the C3 Agentic AI Platform to optimize database read/writes.

JavaScript
var objs = [
    MyEntityType.make(...),
    MyEntityType.make(...),
    ...
];
MyEntityType.createBatch(objs, {...})

Data retrieval

Use fetch() to retrieve instances of a Type.

JavaScript
MyEntityType.fetch({...});

Use evaluate() for performing more complicated database queries, such as grouping and aggregation:

JavaScript
MyEntityType.evaluate({...});

For a full list of supported data manipulation APIs, see the Persistable Type documentation.

See also

Was this page helpful?