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:
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:
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:
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:
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:
extendable entity type MyExtendableType {
...
field1: string
...
}To extend this type, define another Entity Type MyExtendingType.c3typ, which extends the previous 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
ExtendableTypereturns instances ofExtendableType,MyType1, andMyType2 - Fetching
MyType1returns instances ofMyType1only- This is similar to fetching on
ExtendableType, filtering on the type key forMyType1
- This is similar to fetching on
- Fetching
MyType2returns instances ofMyType2only- This is similar to fetching on
ExtendableType, filtering on the type key forMyType2
- This is similar to fetching on
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:
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
ExtendableTypereturns instances ofExtendableType,MyExtendingType, andDeeplyExtendingType - Fetching
MyExtendingTypereturns return instances ofMyExtendingTypeandDeeplyExtendingType - Fetching
DeeplyExtendingTypereturns return instances ofDeeplyExtendingTypeonly
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:
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 namedoes 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 orderingunique- 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 justid
For example, consider the following Entity Type definitions:
@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
comp1andcomp2fields forMyType - When fetching
MyTypedata, the results are sorted alphabetically byname - When fetching
MyTypedata, the nestedchildrenincludes thetimestampandvaluefields, sorted bytimestampfrom most recent to oldest - The
ChildTypetable has an index set up ontimestampto ensure the performance of fetchingMyTypedata
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.
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.
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.
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.
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.
var objs = [
MyEntityType.make(...),
MyEntityType.make(...),
...
];
MyEntityType.createBatch(objs, {...})Data retrieval
Use fetch() to retrieve instances of a Type.
MyEntityType.fetch({...});Use evaluate() for performing more complicated database queries, such as grouping and aggregation:
MyEntityType.evaluate({...});For a full list of supported data manipulation APIs, see the Persistable Type documentation.