# TML modification

> For the complete documentation index, see [llms.txt](https://developers.thoughtspot.com/docs/llms.txt)

Source: https://developers.thoughtspot.com/docs/modify-tml

# TML modification

[ThoughtSpot Modeling Language (TML)](https://docs.thoughtspot.com/cloud/latest/tml) is slightly different for every object type, but all follow a general pattern that allows for programmatic editing. This article describes how various objects and their connections to each other are represented in TML.

When doing development and deployment with TML, [variables]({{navprefix}}/{{variables}}) and [obj\_id]({{navprefix}}/{{deploy-with-tml-apis}}#obj_id) make TML portable across different Orgs and ThoughtSpot instances.

Modifying TML programmatically is useful for more complex levels of transformations, such as the following:

-   Re-mapping of table objects between different data warehouse types
    
-   Deploying dynamically generated data models from the data warehouse
    
-   Translations or other variations from templates
    

You can use the [thoughtspot\_tml Python library](https://github.com/thoughtspot/thoughtspot_tml) to access and transform any of the properties discussed within this document, most of which are covered in the README and the [common\_attribute\_changes.py example script](https://github.com/thoughtspot/thoughtspot_tml/blob/v2_main/examples/common_attribute_changes.py).

## Best practices

Because of the flexibility of the various data object types, there is no particular hierarchy to TML files, but rather just the following rules:

-   Data objects can only be joined to other objects from a single connection
    
-   Any single search (an Answer or a visualization on a Liveboard) can only connect to data objects from a single connection The **Connection** is thus the natural packaging level for importing TML files, except for Liveboards which may contain visualizations connected to different **Connections**.
    

This is to say, for a given **Connection** you can always upload all TML files connected to that connection safely except Liveboards.

One best practice recommendation is to only connect **Answers** and **Liveboards** to **Models**. Although they can connect directly to **Tables** and **Views**, it is far simpler to adjust a single reference to one Model within the Answer and Liveboard TML than many individual **tables**. The only exception is if you use a single **table** or **view** for the underlying Answer.

![ThoughtSpot object model hierarchy and relationships](/docs/doc-images/images/object_model_hierarchy.png)

### Name properties are identifiers in connected objects

When editing entire packages of exported TML files to be deployed in a different environment, you **must consider changes to identifiers in other "downstream" TML files**. ThoughtSpot will do the necessary updates on an existing data model.

One of the most important examples is that the `name:` property of a column in a Table is used as part of the column identifier in connected Model objects.

A Table object might have a column with a display name property (`name:`) that has been modified to be different than the `db_table:` property (the actual name of the column in the database itself):

```YAML
table:
  name: FACT_RETAPP_SALES
  db: RETAILAPPAREL
  schema: PUBLIC
  db_table: FACT_RETAPP_SALES
  connection:
    name: Demo Connection
    fqn: 2aa36dbd-dda6-4497-a6db-bc47e128862e
  columns:
  - name: Sales Id
    db_column_name: SALESID
    properties:
      column_type: MEASURE
      aggregation: SUM
    db_column_properties:
      data_type: INT64
```

The connected Model TML referencing the same column would be as shown in the following example. Note that it is the formatted `name` property from the Table TML, not `db_table_name`.

```YAML
  columns:
  - name: Product Name
    column_id: FACT_RETAPP_SALES_1::Sales Id
    properties:
      column_type: ATTRIBUTE
```

In the Worksheet TML, the column properties are presented as shown in the following example:

```YAML
...
  worksheet_columns:
  - name: Product Name
    column_id: FACT_RETAPP_SALES_1::Sales Id
    properties:
      column_type: ATTRIBUTE
...
```

The pattern for `column_id` is a combination of the `id` from the `table_paths` section along with the `name` property of the column in the table object: `{Model.table_paths[n].id}::{Table.columns[n].name}`

This is the section in Model where the first part of the identifier is defined as the `id` property (`table` property references the `tables:` section above in the TML file):

```YAML
table_paths:
  - id: DIM_RETAPP_PRODUCTS_1
    table: DIM_RETAPP_PRODUCTS
    join_path:
    - join:
      - C_DIM_RETAPP_PRODUCTS
  - id: FACT_RETAPP_SALES_1
    table: FACT_RETAPP_SALES
    join_path:
    - join:
      - C_FACT_RETAPP_SALES
```

## Connections

Connections contain the details and credentials necessary to connect to a database. A TML representation of a Connection includes all the details of connection object.

```YAML
guid: 46030ea3-ecba-4cbf-a02c-c2ef5d5f29f1
connection:
  name: SnowflakeConnection
  type: RDBMS_SNOWFLAKE
  authentication_type: SERVICE_ACCOUNT
  properties:
  - key: accountName
    value: <account-name>
  - key: user
    value: <user_name>
  - key: password
    value: "<password>"
  - key: role
    value: <role>
  - key: warehouse
    value: <warehouse>
  - key: database
    value: <database-name>
  description: ""
```

ThoughtSpot does not enforce uniqueness on Connection names. As a best practice, assign unique names to connections.

For more information about Connection TML and its limitations, see [TML for Connections](https://docs.thoughtspot.com/cloud/latest/tml-connections).

## Tables

[Table objects](https://docs.thoughtspot.com/cloud/latest/tml-tables) represent an actual table (or view) within a database.

When importing a table through the ThoughtSpot UI, a table’s display name will default to the table’s name in the data warehouse, but you can change the display name to whatever you’d like. You can specify the display name within the TML document when creating a table via TML import by changing the `name` property, while leaving the `db_table` property as it was originally imported.

```YAML
guid: <table_guid>
table:
  name: <table_name>
  db: <database_name>
  schema: <schema_name>
  db_table: <database_table_name>
  connection:
    name: <connection_name>
    fqn: <connection_guid>
 ...
```

### Change table properties

You can replace the `connection: name:` or `fqn:` property to 'repoint' the **table** to a different data environment.

The `db`, `schema`, and `db_table` properties may also be changed easily along with `connection`, assuming the schema/structure (field names and data types) are identical to the original object.

### Change or add columns

You can adjust the properties of existing columns or even add new column objects with TML.

When updating an existing object, do not change both `name` and `db_column_name` at the same time. The TML parser will consider this as a deletion of the original column and an addition of a new column.

If you are modifying a package of TML for deployment to a new environment, changes to `name` property have downstream effects in connected Model files.

```YAML
  columns:
  - name: Sales Id
    db_column_name: SALESID
    properties:
      column_type: MEASURE
      aggregation: SUM
    db_column_properties:
      data_type: INT64
```

### Join relationships and row-level security rules

**To import joins, you must import all the related Table TML files together at one time**

Joins do not have separate TML representations, but are defined in the table TML in the `joins_with` section.

Row-level security (RLS) rules are defined in the `rls_rules` section of the table TML. Similar to the `joins_with` section, RLS rules must reference existing table objects for the TML to validate.

If you cannot upload your Table TMLs all at once and you are encountering errors with Import, you can try the following process to minimize any unresolved references:

1.  Remove the `rls_rules` and `joins_with` sections from the TML document on the initial import of each table TML. Save the GUID of each newly created object where it can be referenced to the original GUID.
    
2.  Check that all table objects have been created successfully on the ThoughtSpot server.
    
3.  Do a second import of the TML documents with the `rls_rules` and `joins_with` sections. To update the new objects rather than the original objects, specify the GUIDs of the newly created table objects on the server in the TML documents' `guid` property.
    

## Models

Models combine several Tables to create a data model optimized for Search. With Models, you can build a data model easily by dragging and dropping tables and columns and creating joins, and switch between Table and Column views.

TML for Models has a specific syntax and includes several parameters. It includes parameters that are explicitly defined. For example, if you do not have any filters on your Model, the `filters` parameter does not appear. You can add that variable to the TML file to specify filters for your Model.

The `model_tables` section is a list of table objects that exist on the ThoughtSpot Server. The `name` property is all that is included in an exported TML file, and this matches the `name` property of the table object. If there is more than one table object on the server with identical name properties, you must use the `fqn` property to specify the GUID of the particular table you want. However, the string value of `name` is used in the `joins` section, so the correct process for adding an `fqn` property is as follows:

```YAML
  model_tables:
  - name: <table_name_1>
    id : <optional_table_id>
    fqn : <optional_GUID_of_table_name>
```

If you edit the joins in a Model TML file, the modifications will apply only to that specific Model. The joins at the table level will not be updated. To modify table-level joins, you must edit the source table’s TML file.

The following example shows the TML syntax for Models:

```YAML
guid: <model_guid>
model:
  name: <model_name>
  description:
    This is a multi-line description of the model
    Description line 2
  model_tables:
  - name: <table_name_1>
    id : <optional_table_id>
    fqn : <optional_GUID_of_table_name>
    joins:
    - with: Alias
      on: <join_expression_string>
      type: [RIGHT_OUTER | LEFT_OUTER | INNER | OUTER]
      cardinality: [MANY_TO_ONE | ONE_TO_ONE | ONE_TO_MANY]
  - name: <table_name_2>
    alias: Alias
  - name: <table_name_3>
    joins:
    - with: <table_name_1>
      referencing_join: <join_expression_string>
  formulas:
  - name: <formula_name_1>
    expr: <formula_definition_1>
    id: <optional_unique_identifier>
  - name: <formula_name_2>
    expr: <formula_definition_2>
  filters:
  - column: <filtered_column_name_1>
    oper: <filter_operator>
    values: <filtered_values>
    - value 1
    - value 2
    - value n
  - column: <filtered_column_name_2>
  columns:
  - name: <column_name_1>
    description: <optional_column_description>
    column_id: <table_path>::<column_id_1>
    properties:
      column_type: [ MEASURE | ATTRIBUTE ]
      aggregation: [ SUM | COUNT | AVERAGE | MAX | MIN |
                     COUNT_DISTINCT | NONE | STD_DEVIATION | VARIANCE]
      index_type: [ DONT_INDEX | DEFAULT | PREFIX_ONLY |
                    PREFIX_AND_SUBSTRING | PREFIX_AND_WORD_SUBSTRING ]
      index_priority: <index_priority>
      synonyms :
      - value 1
      - value 2
      - value n
      is_attribution_dimension : [true | false]
      is_additive : [ true | false ]
      calendar : [ default | calendar_name ]
      format_pattern : <format_pattern_string>
      currency_type :
        is_browser : true
          OR
        column : <column_name>
          OR
        iso_code : <valid_ISO_code>
      is_hidden: [ true | false ]
      geo_config:
        latitude : true
          OR
        longitude : true
          OR
        country : true
          OR
        region_name:
        - country : <name_supported_country>
        - region_name : <region_name_in_UI>
          OR
        custom_file_guid: <custom_map_guid>
        geometryType: <custom_map_geometry_type>
      spotiq_preference: <spotiq_preference_string>
    name: <column_name_2>
    description: <column_description>
    column_id: <table_path>::<column_id_2>
    ...
  properties:
    is_bypass_rls: [ true | false ]
    join_progressive: [ true | false ]
  lesson_plans:
  - lesson_id: <lesson_id_number_1>
    lesson_plan_string: <lesson_plan_string_1>
  - lesson_id: <lesson_id_number_2>
    lesson_plan_string: <lesson_plan_string_2>
  parameters:
  - id: <parameter_1_id>
    name: <parameter_1_name>
    data_type: <parameter_1_data_type>
    default_value: <parameter_1_default_value>
    description: “parameter description”
    range_config:
      range_min: <parameter_1_range_minimum>
      range_max: <parameter_1_range_maximum>
      include_min: [true | false]
      include_max: [true | false]
  - id: <parameter_2_id>
    name: <parameter_2_name>
    data_type: <parameter_2_data_type>
    default_value: <parameter_2_default_value>
    list_config:
      list_choice:
      - value: <list_value_1>
        display_name: <value_1_display_name>
      - value: <list_value_2>
        display_name: <value_2_display_name>
      - value: <list_value_n>
        display_name: <value_n_display_name>
        linked_parameters: <table_name>::<parameter_name_in_ThoughtSpot>
```

For more information, see [TML for Models](https://docs.thoughtspot.com/cloud/latest/tml-models).

## Worksheets

DEPRECATED

> **IMPORTANT:** Worksheets are deprecated and replaced with Models in ThoughtSpot Cloud 10.12.0.cl and later versions. You’ll no longer be able to import a Worksheet TML object into ThoughtSpot without converting it into a Model. For information about the TML properties of a Model object, see TML for Models.

Worksheets combine several **tables**, including **Views**, into a coherent data model optimized for searches. The TML syntax for Worksheets defines all aspects, including the tables it joins together, the columns and their properties, filters, and so on.

If you want to change the values for an existing object, the `tables`, `joins` and `table_paths` sections are the most important.

For example, you go from

```YAML
  tables:
  - name: <table_name_1>
```

to

```YAML
  tables:
  - id : <table_name_1>
    fqn : <GUID_of_table_name>
```

The `name` property, which is now transformed into the `id` property, is used in the `joins` and `table_paths` sections that follow. Under `joins`, the `source` and `destination` properties take the string `id` property of a table in the tables list. In a TML file exported from ThoughtSpot, you won’t have to make any changes, because this value will already be set to what was defined in the `name` property, and we’ve maintained that value by switching it to the `id` property.

Under `table_paths`, the `table` property also uses the values we moved to `id`. The list of join names under `join_path` will need to match the text value of the `name` element of an item in the `joins`. This should be valid as exported and not require any changes, but if you do change the `name` value of a join, you will need to update the value in the `join_path` list in `table_paths`.

```YAML
  joins:
  - name: <join_name_1>
    source: <source_table_name>
    destination: <destination_table_name>
    type: [RIGHT_OUTER | LEFT_OUTER | INNER | OUTER]
    on: <join_expression_string>
    is_one_to_one: [ false | true ]
  - ...
  table_paths:
  - id: <table_path_name_1>
    table: <table_name_1>
    join_path:
    - join:
      - <join_name_1>
      - <join_name_n>
```

## Answers

The [answer TML syntax](https://docs.thoughtspot.com/cloud/latest/tml-answers) defines all aspects of a saved search and how it is visualized. The `tables` property is used to point to ThoughtSpot **table**, **view**, or **model** objects, whichever the answer is connected to.

As mentioned above, it is simpler to connect an answer to a single **model**, so that you only have to update one reference in the `tables` section.

```YAML
guid: <answer_guid>
answer:
  name: <answer_name>
  description:
    This is a multi-line description of the answer
    Description line 2
  tables:
  - id: <table_id>
    name: <table_name_1>
    fqn : <GUID_of_table_name>
 ...
```

## Liveboards

[Liveboards](https://docs.thoughtspot.com/cloud/latest/tml-liveboards) include many different visualizations and define a layout of the visualizations elements.

It is **best practice** to only use **one model** for all visualizations on a Liveboard. However, each visualization on a **Liveboard** can connect to different data objects.

### Visualizations on a Liveboard

The individual elements on a **Liveboard** are referred to as visualizations and are defined in the `visualizations` section.

A visualization on a Liveboard **is not linked** to any answer object outside that Liveboard.

When you pin an answer to a Liveboard, the full definition of the answer is copied into the Liveboard definition at that time. However, the capabilities and definitions for answers stored separately or on a Liveboard are identical.

Within the TML, the `visualizations` section uses the same syntax as a separate **answer** stored on its own, with the `guid` property of an individual answer replaced by an `id` property. The value for the `id` property is typically the word `Viz` and a number, `Viz 1`, `Viz 10`.

> **NOTE:** If your instance is running 8.9.0.cl, do not create visualizations on a single Liveboard that connect to different models with the same name (this is possible, as model names are not unique) if you wish to do programmatic transformations, because it is impossible to add in the FQN properties based on just name if names are not unique.

### Modifying Liveboard TML

```YAML
liveboard:
  name: <Liveboard_name>
  description:
    This is a multi-line description of the Liveboard
    Description line 2
  visualizations:
  - id: <viz_id_1>
    answer:
 ...
```

The `layout` section is an ordered list with a `size` property for each visualization tile. If a visualization is added programmatically, it needs a matching entry in the layout section to determine how it appears within the Liveboard. Adjustments to the order within the Liveboard can be made by reordering the layout list. The `visualization_id` property must match to an existing `id` value in the `visualizations` list.

```YAML
  layout:
    tiles:
    - visualization_id: <visualization_id_1>
      size: <viz_id_1_size>
    - visualization_id: <visualization_id_2>
```

## Views

[Views](https://docs.thoughtspot.com/cloud/latest/views) transform a saved search into a data source, allowing for analysis that would require complex sub-queries in SQL. **Views** can be joined with other data objects in a **model**. The best practice is to make **views** available to **answers** and **Liveboards** through a **model**.

The [TML syntax for views](https://docs.thoughtspot.com/cloud/latest/tml-sql-views) is similar to that of models, in that it defines links to table objects on the ThoughtSpot server and join overrides using the `joins` and `table_paths` sections. What truly distinguishes a view is the `search_query` element, which contains a string using the [ThoughtSpot search syntax]({{navprefix}}/{{search-data-api}}).

```YAML
guid: <view_guid>
view:
  name: <view_name>
  description:
    This is a multi-line description of the View.
    Description line 2
  tables:
  - name: <table_name_1>
    id : <optional_table_id>
    fqn : <GUID_of_table_name>
  - name: <table_name_2>
  - name: <table_name_n>
  joins:
  - name: <join_name_1>
    source: <source_table_name>
    destination: <destination_table_name>
    type: [RIGHT_OUTER | LEFT_OUTER | INNER | OUTER]
    on: <join_expression_string>
    is_one_to_one: [ false | true ]
  table_paths:
  - id: <table_path_name_1>
    table: <table_name_1>
    join_path:
    - join:
      - <join_name_1>
      - <join_name_n>
 ...
  search_query: <query_string>
 ...
```

## SQL Views

A [SQL View](https://docs.thoughtspot.com/cloud/latest/tml#syntax-sql-views) defines an underlying query directly to a **connection**.

The `connection` property can be changed similar to a **table** object’s TML either using the `fqn` property, while the `sql_query` property can be changed to a different valid text string.

```YAML
guid: <sql_view_guid>
sql_view:
  name: <view_name>
  description:
    This is a multi-line description of the View.
    Description line 2
  connection:
    name: <connection_name>
    fqn: <connection_GUID>
  sql_query: <sql_query_string>
```