Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

Everything within the C3 AI Suite is managed through or exists as a C3.ai Type. C3.ai Types allow users to describe, process and interact with data and analytics. Broadly speaking, a C3.ai Type is Types are like a Java classclasses, containing and include 'fields', 'methods', and an inheritance structure. Once you understand the structure of C3.ai Type, you will be able to write your own C3.ai Types to load data, run analytics, build and deploy machine learning models, and configure application UIslogic, for your research project. 

All C3.ai Types are defined in a .c3typ file, stored in 'src' directory of a C3.ai Package. (We'll get to C3.ai Packages a little later). All A .c3typ files file can only define a single C3.ai Type.

First, we will introduce describe the C3.ai Package, to download for this exampletutorial. Then, we will discuss the syntax, special keywords, fields, methods, and Inheritance inheritance structure of C3.ai Types. Finally, we will review some examples.

lightbulbAD C3 Package

For In this Guidetutorial, we will use the lightbulbAD C3.ai package. If you want to follow along with source code, please To download the source code for this package following , please follow the instructions available here: C3 lightbulbAD Package

Syntax

To start this discussion of syntax, Let To help familiarize yourself with the syntax for a C3.ai Type, let's look at how the definition of the 'SmartBulb' Type is defined in the lightbulbAD C3.ai Package:

Code Block
/*
 * Copyright 2009-2020 C3 (www.c3.ai). All Rights Reserved.
 * This material, including without limitation any software, is the confidential trade secret and proprietary
 * information of C3 and its licensors. Reproduction, use and/or distribution of this material in any form is
 * strictly prohibited except as set forth in a written license agreement with C3 and/or its authorized distributors.
 * This material may be covered by one or more patents or pending patent applications.
 */

/**
 * A single light bulb capable of measuring various properties, including power consumption, light output, etc.
 */
entity type SmartBulb extends LightBulb mixes MetricEvaluatable, NLEvaluatable type key "SMRT_BLB" {

  /**
   * This bulb's historical measurements.
   */
  bulbMeasurements: [SmartBulbMeasurementSeries](smartBulb)

  /**
   * This bulb's historical predictions.
   */
  @db(order='descending(timestamp)')
  bulbPredictions: [SmartBulbPrediction](smartBulb)

  /**
   * This bulb's latest prediction.
   */
  currentPrediction: SmartBulbPrediction stored calc "bulbPredictions[0]"

  /**
   * This bulb's historical events.
   */
  bulbEvents: [SmartBulbEvent](smartBulb)

  /**
   * The latitude of this bulb.
   */
  latitude: double

  /**
   * The longitude of this bulb.
   */
  longitude: double

  /**
   * The unit of measurement used for this bulb's light output measurements.
   */
  lumensUOM: Unit

  /**
   * The unit of measurement used for this bulb's power consumption measurements.
   */
  powerUOM: Unit

  /**
   * The unit of measurement used for this bulb's temperature measurements.
   */
  temperatureUOM: Unit

  /**
   * The unit of measurement used for this bulb's voltage measurements.
   */
  voltageUOM: Unit

  /**
   * A SmartBulb is associated to a {@link Fixture} through a SmartBulbToFixtureRelation.
   */
  @db(order='descending(start), descending(end)')
  fixtureHistory: [SmartBulbToFixtureRelation](from)

  /**
   * The current Fixture to which this bulb is attached.
   */
  currentFixture: Fixture stored calc 'fixtureHistory[0].(end == null).to'

  /**
   * Method to determine the expected lumens of a light bulb
   */
  expectedLumens: function(wattage: !decimal, bulbType: !string): double js server

  /**
   * Returns the life span of this smartBulb
   */
  lifeSpanInYears: function(bulbId: string): double js server

  /**
   * Returns the average life span of all smartBulbs.
   */
  averageLifeSpan: function(): double js server

  /**
   * Returns the id of the smart bulb with the shortest recorded life span to date.
   */
  shortestLifeSpanBulb: function(): string js server

  /**
   * Returns the id of the smart bulb with the longest recorded life span to date.
   */
  longestLifeSpanBulb: function(): string js server

}

We can see the general structure of the At a glance, a .c3typ file at a glancehas the following components: 

  • Keywords introduce , which define the name, inheritance, and specify properties of the a C3.ai type.
  • fields are named, and their Types defined
  • methods are named, their types are similar to function signatures.
  • Additional annotations like '@db' can sometimes precede fields or methods.
  • Types can inherit other types.

...

Keywords

A Type definition is introduced using a series of keywords, and names. These tell the C3 AI Suite how to construct this type, how to store it internally, and whether it inherits fields and methods from other already defined Types. We describe each:

  • type: All C3 Types use the keyword 'type'. This indicates to the C3 AI Suite that this is a Type definition.
  • entity: The 'entity' keyword indicates that the type mixes in the 'Persistable' type and is stored internally to C3 in a table somewhere. We can mix in Persistable instead but this keyword makes the Type definition shorter and easier to read. Since a large majority of the Types on the C3 AI Suite are persistable, this is a good keyword to have.
  • mixes: A type which specifies `mixes AnotherType` inherits the fields and methods of the other type. Multiple Types can be mixed in, and mixes do not change the way the type is stored internally.
  • remixes: A type which specifies `remix` defines a modification of an existing type.
  • extends: A type which specifies `extends AnotherType` is a special type of of mixin. This mixin subclasses a persistable type in a special way, The original Types along with the new Types are stored together in the same table. required as well, is the `type key` modifier which describes the key to use when storing the new type internally.
  • extendable: A keyword indicating that a Type can be extended. The C3 AI Suite will create the internal table for this type with an additional field called the 'key' which will be used to distinguish the different varieties of this base type.
  • type key: A set of keywords indicating the Key value to use when storing a type which extends another type.
  • schema name: A set of keywords indicating the name of the table to use to store the Type.

Primitive Types

As a basis for many fields, the C3 AI Suite defines many 'primitive' types. These are basic types like 'int' and 'double. Primitive Types are given to the developer by the platform and new ones require backend C3 support to define. Most DTI researchers will not be defining these, but using those that already exist.

...

Persistable Types

The most common type a C3 developer will define is a 'Persistable' type. A Persistable type is any type which inherits the 'Persistable' Type. This can be specified either by directly mixing in 'Persistable', or using the keyword 'entity'. All persistable types live within the C3 AI Suite stored in a table backed by some sort of database technology. Usually this is postgres, but depending on the data (Or annotations as will be discussed below), It can also be stored in a file blob or key-value store such as Cassandra.

...

Generic Types

Types can also be made generic similar to Java generic Types and C++ generic classes. In fact, it uses the same syntax with angle brakets '<>' defining the generic parameters of the type. When defining a generic type, the Name of the type you're creating will be followed by angle brackets and a comma-separated list of placeholder names to refer to the generic type parameters you want to use. For example:

...

Fields

Each field of a C3 Type is defined by first giving the field name followed by a colon ':' followed by a Type.

...

Depending on the Type of the field, the field is grouped into some broad categories.

Primitive Fields

Primitive Fields use types which are PrimitiveTypes. These are simple data types usually directly supported by database technologies such as SQL. These fields are stored directly within the main type's table.

Reference Fields

Reference Fields use another defined C3 Type. This reference is implemented in the main Type's table as a foreign key into the referenced Type's table, (The 'id' field!).

Collection Fields

Collection Fields are groups of Types. There are four broad categories of collections which can be used.

...

When we define a field which is a collection of MyType, we can also specify which MyTypes get added by using Foreign Key Collection Notation. If for example, we're trying to define the new type TheType, and we want a collection to have type [MyType], but we don't want all MyTypes to be in this collection, we can use the notation `(thetype, id)` which specifies that C3 Should only use MyTypes whose 'thetype' property matches TheType's id property. Please see the section 'Foreign Key Collection Notation and Schema Names' in the following C3.ai documentation where this concept is explained better: https://developer.c3.ai/docs/7.12.17/topic/mda-fields

Calculated Fields

These fields are derived from other fields. Calculated fields are specified as follows

...

You may also specify the keyword 'stored'. With the stored keyword, the value of the field is stored in the Type's Table, while without it is calculated at runtime.

C3.ai Developer Resources on Fields:

Methods

Each method of a C3 Type is defined by first giving the method name followed by a colon ':' followed by a function signature, (e.g., function() or function(wattage: !decimal, bulbType: !string)), followed by a colon ':', followed by a return Type, along with a language and environment specification.

Function Signatures

Instead of a Type name following the name as with fields, methods have a function signature. the syntax looks like:

...

we see 'function' followed by a comma separated list of argument definitions. An argument definition consists of a parameter name followed by a colon ':', followed by a Type name. Optionally, you can also include an '!' before the parameter type to indicate that that argument is required.

Return Types

After the function signature we have a return type. This must be a C3 type. The function must return an object of the appropriate type in the .

Language/Environment Specification

Finally, we end with a language and environment specification.

...

For Python, we need to specify a valid 'ActionRuntime' for the python function to be executed in. (ActionRuntimes are defined within the C3 AI Suite, and new ones can be defined as part of a C3 Package. They are essentially conda environments.) For example, 'server' will use the 'py-server' ActionRuntime.

Function Definitions

Finally, the function definition goes in a different file from the Type definition .c3typ file. It should have the same base name as the .c3typ definition file, but have an extension appropriate for the language of implementation (e.g., 'py' for Python, 'js' for JavaScript). Within, the file must contain functions whose name matches the method name from the Type definition exactly. For example, if we have a Type defined like so:

...

Code Block
function funcB(num, name='default') {
    ...
}


C3.ai Developer Resources on Methods:

Inheritance

Like many modern languages, Types can also inherit fields and methods from other types in an object oriented fashion. The keywords which signal inheritance are `mixes` and `extends`. These follow the name of the new type.

...

Annotations

Annotations can appear before Type definitions, field declarations, and method declarations. They do a variety of things such as configure the database technology used to store that Type, mark what type of ActionRuntime should be used to run a method, Mark a field or method as deprecated or beta, Specify a timeseries treatment, and specify parameters of C3 Analytics (discussion to come soon). These are only a selection of what annotations can do.

...

Final Keyword

Types, methods, and fields can be made 'final' using the 'final' keyword. This prevents fields and methods from being overridden by any type which mixes them in. If a type is made final, all fields and methods of that type are made final.

...

Abstract Types

The C3 AI Suite also supports abstract types. If you use the keyword 'abstract' before the type definition, this will indicate an abstract type. You cannot instantiate an abstract type, and must mix it into another concrete type. This provides a great way to standardize interfaces. Here's an example:

...

Examples

Look through the lightbulbAD package and find .c3typ files. Look at these to see the range of possibilities