# 데이터 원본 바인딩

## Content

데이터 차트에서 데이터를 효과적으로 표시하려면 차트를 데이터 소스에 연결하는 것이 필수입니다. 이 항목에서는 데이터 차트에서 데이터 소스로부터 데이터를 바인딩하는 방법을 설명합니다.
SpreadJS에서 데이터 소스는 일반적으로 데이터 매니저가 관리하는 테이블로 표현되며, 시각화 과정은 데이터 매니저의 테이블 정보를 바인딩함으로써 완성됩니다.
데이터 차트를 생성하고 관리하려면 `GC.Spread.Sheets.DataCharts` 네임스페이스의 `IDataChartConfig` 인터페이스를 사용합니다. 이 인터페이스에는 다음과 같은 속성이 포함됩니다:

| 속성 이름 | 설명 |
| ----- | --- |
| `plots` | **[필수]** 차트에 데이터를 어떻게 표시할지 정의하는 plot 옵션입니다. |
| `config` | **[선택]** 데이터 차트에 대한 추가 구성 옵션입니다. |
| `tableName` | **[선택]** 데이터 바인딩에 사용할 테이블의 이름입니다.<br>차트 내에서 바인딩된 필드가 어느 테이블에 속하는지를 명확히 지정할 수 있습니다. |

***

`config.tableName`을 설정하면, 바인딩할 필드가 속한 테이블을 지정할 수 있습니다.
또한, `IPlotEncodingsOption` 인터페이스를 통해 각 plot에 대한 다양한 인코딩(encoding) 옵션을 정의할 수 있으며, `config.plots[0].encodings` 내에 바인딩 정보를 설정하여 데이터를 차트에 표시합니다. 아래는 주요 필드입니다:

| 필드 | 설명 |
| --- | --- |
| **values** | Y축에 표시할 필드를 지정하며, 여러 하위 속성을 지원합니다. 만약 데이터 차트에 `values`가 설정되지 않으면, SpreadJS에서 제공하는 기본 데이터가 자동으로 바인딩됩니다.<br>사용자는 특정 요구에 따라 각 필드에 서로 다른 요약 방식(aggregate)을 지정할 수 있습니다.<br>`values` 속성에 여러 필드를 설정하는 경우 `legend` 필드를 별도로 설정하더라도 적용되지 않으며, 대신 데이터 차트는 각 `values` 필드에 따라 자동으로 범례를 생성합니다.<br>**참고:** `pie`, `donut` 차트와 같은 일부 차트 유형에서는 `values`에 **단일 필드만 바인딩**할 수 있습니다. |
| **category** | 데이터를 축(axis) 또는 차원(dimension) 기준으로 정의하고 그룹화합니다.<br>이는 데이터를 서로 다른 범주로 나누어, 사용자에게 데이터의 분포나 추세를 보다 쉽게 이해할 수 있도록 도와줍니다.<br>막대(bar) 차트나 라인(line) 차트에서는 `category`가 보통 **시간(연도, 월 등)** 또는 **지역, 제품 유형** 등의 분류 기준을 나타냅니다.<br>파이(pie) 차트에서는 `category`가 차트의 각 섹터가 어떤 범주에 해당하는지를 나타냅니다.<br>또한 `category` 필드를 바인딩할 때 정렬(sorting) 정보를 함께 지정할 수 있습니다. 정렬 옵션에는 `Field`, `Aggregate`, `Order`가 포함됩니다. |
| **details** | 데이터를 여러 하위 섹션으로 나누며, 이를 통해 plot 내에서 클러스터(cluster) 또는 스택(stack) 방식으로 시각화할 수 있습니다.<br>데이터 차트에서는 **sunburst 차트 유형을 제외한 모든 차트**에서 `details` 필드를 **최대 한 개까지만 바인딩**할 수 있습니다. |
| **color** | 서로 다른 기호(symbol)를 가진 차트 유형에서 색상 범례(color legend)를 표시합니다.<br>`color` 필드는 문자열(string), 불리언(boolean), 날짜(date), 숫자(number) 등 다양한 타입의 값을 허용합니다. |
| **size** | plot과 범례에 대해 **연속적인 크기 기반의 스케일(scale)** 을 적용할 필드를 지정합니다.<br>`color` 범례와 유사하게, `size` 범례는 보통 **3차원 데이터를 숫자 스케일로 시각화**할 때 사용됩니다.<br>해당 속성은 **bubble plot**에만 적용 가능합니다. |
| **tooltip** | plot에 표시될 툴팁 내에 어떤 필드들이 포함될지를 결정합니다.<br>데이터 차트는 `tooltip` 인코딩을 통해, 각 데이터 포인트 위에 마우스를 올렸을 때 표시될 툴팁 내용을 결정합니다.<br>툴팁이 표시되는 동안, **축 레이블(axis labels)** 이나 **기준선 레이블(reference line labels)** 등은 무시됩니다. |
| **filter** | 필터는 **원본 데이터에 적용**되며, 데이터가 집계되기 전에 필터링이 수행됩니다.<br>이 필드는 숫자 타입과 문자열 타입을 위한 **두 가지 필터링 방법**을 포함하고 있습니다.<br>데이터 차트에서 제공되는 필터는 **여러 단계로 중첩(nested)** 될 수 있으며, 논리 연산을 통해 하나의 계층 내에서 **"AND" 또는 "OR" 관계**를 설정할 수 있습니다. |

>type=warning
> **참고**: 데이터 차트에서는 여러 테이블의 데이터를 바인딩할 수도 있지만, 이러한 테이블들 간에는 사전에 정의된 관계(Relationship)가 있어야 하며, 관련 필드(Related Fields)를 통해 서로 연결되어 있어야 합니다.

## **코드 사용 예시**

다음 코드 샘플은 **데이터 매니저를 가져오고**, **단일 또는 다중 테이블을 바인딩하는 방법**을 보여줍니다.

```javascript
// 데이터 매니저 가져오기
const dataManager = spread.dataManager();

// 1. 데이터 바인딩
// 데이터 매니저에 테이블 추가
const salesTable = createSalesTable(dataManager);
await salesTable.fetch();
const sheet = spread.getSheet(0);
sheet.name("Binding Data");
sheet.setColumnCount(25);

const dataChart = sheet.dataCharts.add('data-chart', 10, 10, 600, 400, GC.Spread.Sheets.DataCharts.DataChartType.column);
dataChart.setChartConfig({
    tableName: 'Sales',
    plots: [
        {
            type: GC.Spread.Sheets.DataCharts.DataChartType.column,
            encodings: {
                values: [ // values 매핑: Y축에 표시할 필드를 지정
                    {
                        field: "Sales",
                        aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum
                    }
                ],
                category: { // 데이터를 범주별로 구분하여 분포나 추세를 쉽게 파악할 수 있도록 함
                    field: "Product",
                },
                details: [ // 데이터를 여러 하위 구간으로 나누어 클러스터 또는 스택 형태로 시각화
                    {
                        field: "Region"
                    }
                ],
                color: { // 색상 및 범례 생성을 위한 기준 필드 설정
                    field: "Product"
                },
                filter: { // 필터링은 데이터 집계 전에 수행됨
                    field: 'Region',
                    operate: GC.Spread.Sheets.DataCharts.StringOperator.contains,
                    value: 'East',
                }
            }
        }
    ]
});

// 2. 다중 테이블 바인딩
// 데이터 트에서는 여러 테이블의 데이터를 바인딩할 수 있으나,
// 이 테이블들 간에는 반드시 "관계(Relationship)"가 존재하고 관련 필드를 통해 연결되어야 함
// 데이터 매니저에 테이블 추가
const ordersTable = createOrders(dataManager);
await ordersTable.fetch();
// 데이터 매니저에 테이블 추가
const orderDetailsTable = createOrderDetails(dataManager);
await orderDetailsTable.fetch();
// 두 테이블 간 관계 추가
dataManager.addRelationship(ordersTable, "orderId", "orderDetailsTable", orderDetailsTable, "OrderId", "ordersTable");

let sheet2 = spread.getSheet(1);
sheet2.name("Binding Multiple Tables");
const dataChart3 = sheet2.dataCharts.add('data-chart-3', 10, 10, 600, 400, GC.Spread.Sheets.DataCharts.DataChartType.column);
dataChart3.setChartConfig({
    tableName: 'Orders',    // Orders Table
    plots: [
        {
            type: GC.Spread.Sheets.DataCharts.DataChartType.column,
            encodings: {
                values: [
                    { field: 'orderDetailsTable.OrderId', aggregate: GC.Spread.Sheets.DataCharts.Aggregate.sum }, // orderDetailsTable 필드 참조
                ],
                category: { field: 'shipRegion' },
                color: { field: 'shipRegion' },
            }
        }
    ]

});

function createOrders(dataManager) {
    const records = [
        [10248, "VINET", 5, "1996-07-04T00:00:00", "1996-08-01T00:00:00", "1996-07-16T00:00:00", 3, 32.38, "Vins et alcools Chevalier", "59 rue de l'Abbaye", "Reims", null, "51100", "France"],
        [10249, "TOMSP", 6, "1996-07-05T00:00:00", "1996-08-16T00:00:00", "1996-07-10T00:00:00", 1, 11.61, "Toms Spezialitäten", "Luisenstr. 48", "Münster", null, "44087", "Germany"],
        [10250, "HANAR", 4, "1996-07-08T00:00:00", "1996-08-05T00:00:00", "1996-07-12T00:00:00", 2, 65.83, "Hanari Carnes", "Rua do Paço, 67", "Rio de Janeiro", "RJ", "05454-876", "Brazil"],
        [10251, "VICTE", 3, "1996-07-08T00:00:00", "1996-08-05T00:00:00", "1996-07-15T00:00:00", 1, 41.34, "Victuailles en stock", "2, rue du Commerce", "Lyon", null, "69004", "France"],
        [10252, "SUPRD", 4, "1996-07-09T00:00:00", "1996-08-06T00:00:00", "1996-07-11T00:00:00", 2, 51.3, "Suprêmes délices", "Boulevard Tirou, 255", "Charleroi", null, "B-6000", "Belgium"],
        [10253, "HANAR", 3, "1996-07-10T00:00:00", "1996-07-24T00:00:00", "1996-07-16T00:00:00", 2, 58.17, "Hanari Carnes", "Rua do Paço, 67", "Rio de Janeiro", "RJ", "05454-876", "Brazil"],
        [10254, "CHOPS", 5, "1996-07-11T00:00:00", "1996-08-08T00:00:00", "1996-07-23T00:00:00", 2, 22.98, "Chop-suey Chinese", "Hauptstr. 31", "Bern", null, "3012", "Switzerland"],
        [10255, "RICSU", 9, "1996-07-12T00:00:00", "1996-08-09T00:00:00", "1996-07-15T00:00:00", 3, 148.33, "Richter Supermarkt", "Starenweg 5", "Genève", null, "1204", "Switzerland"],
        [10256, "WELLI", 3, "1996-07-15T00:00:00", "1996-08-12T00:00:00", "1996-07-17T00:00:00", 2, 13.97, "Wellington Importadora", "Rua do Mercado, 12", "Resende", "SP", "08737-363", "Brazil"],
        [10257, "HILAA", 4, "1996-07-16T00:00:00", "1996-08-13T00:00:00", "1996-07-22T00:00:00", 3, 81.91, "HILARION-Abastos", "Carrera 22 con Ave. Carlos Soublette #8-35", "San Cristóbal", "Táchira", "5022", "Venezuela"],
        [10258, "ERNSH", 1, "1996-07-17T00:00:00", "1996-08-14T00:00:00", "1996-07-23T00:00:00", 1, 140.51, "Ernst Handel", "Kirchgasse 6", "Graz", null, "8010", "Austria"],
        [10259, "CENTC", 4, "1996-07-18T00:00:00", "1996-08-15T00:00:00", "1996-07-25T00:00:00", 3, 3.25, "Centro comercial Moctezuma", "Sierras de Granada 9993", "México D.F.", null, "05022", "Mexico"],
        [10260, "OTTIK", 4, "1996-07-19T00:00:00", "1996-08-16T00:00:00", "1996-07-29T00:00:00", 1, 55.09, "Ottilies Käseladen", "Mehrheimerstr. 369", "Köln", null, "50739", "Germany"],
        [10261, "QUEDE", 4, "1996-07-19T00:00:00", "1996-08-16T00:00:00", "1996-07-30T00:00:00", 2, 3.05, "Que Delícia", "Rua da Panificadora, 12", "Rio de Janeiro", "RJ", "02389-673", "Brazil"],
        [10262, "RATTC", 8, "1996-07-22T00:00:00", "1996-08-19T00:00:00", "1996-07-25T00:00:00", 3, 48.29, "Rattlesnake Canyon Grocery", "2817 Milton Dr.", "Albuquerque", "NM", "87110", "USA"],
        [10263, "ERNSH", 9, "1996-07-23T00:00:00", "1996-08-20T00:00:00", "1996-07-31T00:00:00", 3, 146.06, "Ernst Handel", "Kirchgasse 6", "Graz", null, "8010", "Austria"],
        [10264, "FOLKO", 6, "1996-07-24T00:00:00", "1996-08-21T00:00:00", "1996-08-23T00:00:00", 3, 3.67, "Folk och fä HB", "Åkergatan 24", "Bräcke", null, "S-844 67", "Sweden"],
        [10265, "BLONP", 2, "1996-07-25T00:00:00", "1996-08-22T00:00:00", "1996-08-12T00:00:00", 1, 55.28, "Blondel père et fils", "24, place Kléber", "Strasbourg", null, "67000", "France"],
        [10266, "WARTH", 3, "1996-07-26T00:00:00", "1996-09-06T00:00:00", "1996-07-31T00:00:00", 3, 25.73, "Wartian Herkku", "Torikatu 38", "Oulu", null, "90110", "Finland"],
        [10267, "FRANK", 4, "1996-07-29T00:00:00", "1996-08-26T00:00:00", "1996-08-06T00:00:00", 1, 208.58, "Frankenversand", "Berliner Platz 43", "München", null, "80805", "Germany"],
        [10268, "GROSR", 8, "1996-07-30T00:00:00", "1996-08-27T00:00:00", "1996-08-02T00:00:00", 3, 66.29, "GROSELLA-Restaurante", "5ª Ave. Los Palos Grandes", "Caracas", "DF", "1081", "Venezuela"],
        [10269, "WHITC", 5, "1996-07-31T00:00:00", "1996-08-14T00:00:00", "1996-08-09T00:00:00", 1, 4.56, "White Clover Markets", "1029 - 12th Ave. S.", "Seattle", "WA", "98124", "USA"],
        [10270, "WARTH", 1, "1996-08-01T00:00:00", "1996-08-29T00:00:00", "1996-08-02T00:00:00", 1, 136.54, "Wartian Herkku", "Torikatu 38", "Oulu", null, "90110", "Finland"],
        [10271, "SPLIR", 6, "1996-08-01T00:00:00", "1996-08-29T00:00:00", "1996-08-30T00:00:00", 2, 4.54, "Split Rail Beer & Ale", "P.O. Box 555", "Lander", "WY", "82520", "USA"],
        [10272, "RATTC", 6, "1996-08-02T00:00:00", "1996-08-30T00:00:00", "1996-08-06T00:00:00", 2, 98.03, "Rattlesnake Canyon Grocery", "2817 Milton Dr.", "Albuquerque", "NM", "87110", "USA"],
        [10273, "QUICK", 3, "1996-08-05T00:00:00", "1996-09-02T00:00:00", "1996-08-12T00:00:00", 3, 76.07, "QUICK-Stop", "Taucherstraße 10", "Cunewalde", null, "01307", "Germany"],
        [10274, "VINET", 6, "1996-08-06T00:00:00", "1996-09-03T00:00:00", "1996-08-16T00:00:00", 1, 6.01, "Vins et alcools Chevalier", "59 rue de l'Abbaye", "Reims", null, "51100", "France"],
        [10275, "MAGAA", 1, "1996-08-07T00:00:00", "1996-09-04T00:00:00", "1996-08-09T00:00:00", 1, 26.93, "Magazzini Alimentari Riuniti", "Via Ludovico il Moro 22", "Bergamo", null, "24100", "Italy"],
        [10276, "TORTU", 8, "1996-08-08T00:00:00", "1996-08-22T00:00:00", "1996-08-14T00:00:00", 3, 13.84, "Tortuga Restaurante", "Avda. Azteca 123", "México D.F.", null, "05033", "Mexico"],
        [10277, "MORGK", 2, "1996-08-09T00:00:00", "1996-09-06T00:00:00", "1996-08-13T00:00:00", 3, 125.77, "Morgenstern Gesundkost", "Heerstr. 22", "Leipzig", null, "04179", "Germany"],
        [10278, "BERGS", 8, "1996-08-12T00:00:00", "1996-09-09T00:00:00", "1996-08-16T00:00:00", 2, 92.69, "Berglunds snabbköp", "Berguvsvägen  8", "Luleå", null, "S-958 22", "Sweden"],
        [10279, "LEHMS", 8, "1996-08-13T00:00:00", "1996-09-10T00:00:00", "1996-08-16T00:00:00", 2, 25.83, "Lehmanns Marktstand", "Magazinweg 7", "Frankfurt a.M.", null, "60528", "Germany"],
        [10280, "BERGS", 2, "1996-08-14T00:00:00", "1996-09-11T00:00:00", "1996-09-12T00:00:00", 1, 8.98, "Berglunds snabbköp", "Berguvsvägen  8", "Luleå", null, "S-958 22", "Sweden"],
        [10281, "ROMEY", 4, "1996-08-14T00:00:00", "1996-08-28T00:00:00", "1996-08-21T00:00:00", 1, 2.94, "Romero y tomillo", "Gran Vía, 1", "Madrid", null, "28001", "Spain"],
        [10282, "ROMEY", 4, "1996-08-15T00:00:00", "1996-09-12T00:00:00", "1996-08-21T00:00:00", 1, 12.69, "Romero y tomillo", "Gran Vía, 1", "Madrid", null, "28001", "Spain"],
        [10283, "LILAS", 3, "1996-08-16T00:00:00", "1996-09-13T00:00:00", "1996-08-23T00:00:00", 3, 84.81, "LILA-Supermercado", "Carrera 52 con Ave. Bolívar #65-98 Llano Largo", "Barquisimeto", "Lara", "3508", "Venezuela"],
        [10284, "LEHMS", 4, "1996-08-19T00:00:00", "1996-09-16T00:00:00", "1996-08-27T00:00:00", 1, 76.56, "Lehmanns Marktstand", "Magazinweg 7", "Frankfurt a.M.", null, "60528", "Germany"],
        [10285, "QUICK", 1, "1996-08-20T00:00:00", "1996-09-17T00:00:00", "1996-08-26T00:00:00", 2, 76.83, "QUICK-Stop", "Taucherstraße 10", "Cunewalde", null, "01307", "Germany"],
        [10286, "QUICK", 8, "1996-08-21T00:00:00", "1996-09-18T00:00:00", "1996-08-30T00:00:00", 3, 229.24, "QUICK-Stop", "Taucherstraße 10", "Cunewalde", null, "01307", "Germany"],
        [10287, "RICAR", 8, "1996-08-22T00:00:00", "1996-09-19T00:00:00", "1996-08-28T00:00:00", 3, 12.76, "Ricardo Adocicados", "Av. Copacabana, 267", "Rio de Janeiro", "RJ", "02389-890", "Brazil"],
        [10288, "REGGC", 4, "1996-08-23T00:00:00", "1996-09-20T00:00:00", "1996-09-03T00:00:00", 1, 7.45, "Reggiani Caseifici", "Strada Provinciale 124", "Reggio Emilia", null, "42100", "Italy"],
        [10289, "BSBEV", 7, "1996-08-26T00:00:00", "1996-09-23T00:00:00", "1996-08-28T00:00:00", 3, 22.77, "B's Beverages", "Fauntleroy Circus", "London", null, "EC2 5NT", "UK"],
        [10290, "COMMI", 8, "1996-08-27T00:00:00", "1996-09-24T00:00:00", "1996-09-03T00:00:00", 1, 79.7, "Comércio Mineiro", "Av. dos Lusíadas, 23", "Sao Paulo", "SP", "05432-043", "Brazil"],
        [10291, "QUEDE", 6, "1996-08-27T00:00:00", "1996-09-24T00:00:00", "1996-09-04T00:00:00", 2, 6.4, "Que Delícia", "Rua da Panificadora, 12", "Rio de Janeiro", "RJ", "02389-673", "Brazil"],
        [10292, "TRADH", 1, "1996-08-28T00:00:00", "1996-09-25T00:00:00", "1996-09-02T00:00:00", 2, 1.35, "Tradiçao Hipermercados", "Av. Inês de Castro, 414", "Sao Paulo", "SP", "05634-030", "Brazil"],
        [10293, "TORTU", 1, "1996-08-29T00:00:00", "1996-09-26T00:00:00", "1996-09-11T00:00:00", 3, 21.18, "Tortuga Restaurante", "Avda. Azteca 123", "México D.F.", null, "05033", "Mexico"],
        [10294, "RATTC", 4, "1996-08-30T00:00:00", "1996-09-27T00:00:00", "1996-09-05T00:00:00", 2, 147.26, "Rattlesnake Canyon Grocery", "2817 Milton Dr.", "Albuquerque", "NM", "87110", "USA"],
        [10295, "VINET", 2, "1996-09-02T00:00:00", "1996-09-30T00:00:00", "1996-09-10T00:00:00", 2, 1.15, "Vins et alcools Chevalier", "59 rue de l'Abbaye", "Reims", null, "51100", "France"],
        [10296, "LILAS", 6, "1996-09-03T00:00:00", "1996-10-01T00:00:00", "1996-09-11T00:00:00", 1, 0.12, "LILA-Supermercado", "Carrera 52 con Ave. Bolívar #65-98 Llano Largo", "Barquisimeto", "Lara", "3508", "Venezuela"],
        [10297, "BLONP", 5, "1996-09-04T00:00:00", "1996-10-16T00:00:00", "1996-09-10T00:00:00", 2, 5.74, "Blondel père et fils", "24, place Kléber", "Strasbourg", null, "67000", "France"],
        [10298, "HUNGO", 6, "1996-09-05T00:00:00", "1996-10-03T00:00:00", "1996-09-11T00:00:00", 2, 168.22, "Hungry Owl All-Night Grocers", "8 Johnstown Road", "Cork", "Co. Cork", null, "Ireland"],
        [10299, "RICAR", 4, "1996-09-06T00:00:00", "1996-10-04T00:00:00", "1996-09-13T00:00:00", 2, 29.76, "Ricardo Adocicados", "Av. Copacabana, 267", "Rio de Janeiro", "RJ", "02389-890", "Brazil"],
    ];
    const columns = ['orderId', 'customerId', 'employeeId', 'orderDate', 'requiredDate', 'shippedDate', 'shipVia', 'freight', 'shipName', 'shipAddress', 'shipCity', 'shipRegion', 'shipPostalCode', 'shipCountry'];

    return dataManager.addTable('Orders', {
        data: records.map((x) => {
            const record = {};
            columns.forEach((c, i) => record[c] = x[i]);
            return record;
        })
    });
}
function createOrderDetails(dataManager) {
    const records = [
        [10248, 11, 14, 12, 0],
        [10248, 42, 9.8, 10, 0],
        [10248, 72, 34.8, 5, 0],
        [10249, 14, 18.6, 9, 0],
        [10249, 51, 42.4, 40, 0],
        [10250, 41, 7.7, 10, 0],
        [10250, 51, 42.4, 35, 0.15],
        [10250, 65, 16.8, 15, 0.15],
        [10251, 22, 16.8, 6, 0.05],
        [10251, 57, 15.6, 15, 0.05],
        [10251, 65, 16.8, 20, 0],
        [10252, 20, 64.8, 40, 0.05],
        [10252, 33, 2, 25, 0.05],
        [10252, 60, 27.2, 40, 0],
        [10253, 31, 10, 20, 0],
        [10253, 39, 14.4, 42, 0],
        [10253, 49, 16, 40, 0],
        [10254, 24, 3.6, 15, 0.15],
        [10254, 55, 19.2, 21, 0.15],
        [10254, 74, 8, 21, 0],
        [10255, 2, 15.2, 20, 0],
        [10255, 16, 13.9, 35, 0],
        [10255, 36, 15.2, 25, 0],
        [10255, 59, 44, 30, 0],
        [10256, 53, 26.2, 15, 0],
        [10256, 77, 10.4, 12, 0],
        [10257, 27, 35.1, 25, 0],
        [10257, 39, 14.4, 6, 0],
        [10257, 77, 10.4, 15, 0],
        [10258, 2, 15.2, 50, 0.2],
        [10258, 5, 17, 65, 0.2],
        [10258, 32, 25.6, 6, 0.2],
        [10259, 21, 8, 10, 0],
        [10259, 37, 20.8, 1, 0],
        [10260, 41, 7.7, 16, 0.25],
        [10260, 57, 15.6, 50, 0],
        [10260, 62, 39.4, 15, 0.25],
        [10260, 70, 12, 21, 0.25],
        [10261, 21, 8, 20, 0],
        [10261, 35, 14.4, 20, 0],
        [10262, 5, 17, 12, 0.2],
        [10262, 7, 24, 15, 0],
        [10262, 56, 30.4, 2, 0],
        [10263, 16, 13.9, 60, 0.25],
        [10263, 24, 3.6, 28, 0],
        [10263, 30, 20.7, 60, 0.25],
        [10263, 74, 8, 36, 0.25],
        [10264, 2, 15.2, 35, 0],
        [10264, 41, 7.7, 25, 0.15],
        [10265, 17, 31.2, 30, 0],
        [10265, 70, 12, 20, 0],
        [10266, 12, 30.4, 12, 0.05],
        [10267, 40, 14.7, 50, 0],
        [10267, 59, 44, 70, 0.15],
        [10267, 76, 14.4, 15, 0.15],
        [10268, 29, 99, 10, 0],
        [10268, 72, 27.8, 4, 0],
        [10269, 33, 2, 60, 0.05],
        [10269, 72, 27.8, 20, 0.05],
        [10270, 36, 15.2, 30, 0],
        [10270, 43, 36.8, 25, 0],
        [10271, 33, 2, 24, 0],
        [10272, 20, 64.8, 6, 0],
        [10272, 31, 10, 40, 0],
        [10272, 72, 27.8, 24, 0],
        [10273, 10, 24.8, 24, 0.05],
        [10273, 31, 10, 15, 0.05],
        [10273, 33, 2, 20, 0],
        [10273, 40, 14.7, 60, 0.05],
        [10273, 76, 14.4, 33, 0.05],
        [10274, 71, 17.2, 20, 0],
        [10274, 72, 27.8, 7, 0],
        [10275, 24, 3.6, 12, 0.05],
        [10275, 59, 44, 6, 0.05],
        [10276, 10, 24.8, 15, 0],
        [10276, 13, 4.8, 10, 0],
        [10277, 28, 36.4, 20, 0],
        [10277, 62, 39.4, 12, 0],
        [10278, 44, 15.5, 16, 0],
        [10278, 59, 44, 15, 0],
        [10278, 63, 35.1, 8, 0],
        [10278, 73, 12, 25, 0],
        [10279, 17, 31.2, 15, 0.25],
        [10280, 24, 3.6, 12, 0],
        [10280, 55, 19.2, 20, 0],
        [10280, 75, 6.2, 30, 0],
        [10281, 19, 7.3, 1, 0],
        [10281, 24, 3.6, 6, 0],
        [10281, 35, 14.4, 4, 0],
        [10282, 30, 20.7, 6, 0],
        [10282, 57, 15.6, 2, 0],
        [10283, 15, 12.4, 20, 0],
        [10283, 19, 7.3, 18, 0],
        [10283, 60, 27.2, 35, 0],
        [10283, 72, 27.8, 3, 0],
        [10284, 27, 35.1, 15, 0.25],
        [10284, 44, 15.5, 21, 0],
        [10284, 60, 27.2, 20, 0.25],
        [10284, 67, 11.2, 5, 0.25],
        [10285, 1, 14.4, 45, 0.2],
        [10285, 40, 14.7, 40, 0.2],
        [10285, 53, 26.2, 36, 0.2],
        [10286, 35, 14.4, 100, 0],
        [10286, 62, 39.4, 40, 0],
        [10287, 16, 13.9, 40, 0.15],
        [10287, 34, 11.2, 20, 0],
        [10287, 46, 9.6, 15, 0.15],
        [10288, 54, 5.9, 10, 0.1],
        [10288, 68, 10, 3, 0.1],
        [10289, 3, 8, 30, 0],
        [10289, 64, 26.6, 9, 0],
        [10290, 5, 17, 20, 0],
        [10290, 29, 99, 15, 0],
        [10290, 49, 16, 15, 0],
        [10290, 77, 10.4, 10, 0],
        [10291, 13, 4.8, 20, 0.1],
        [10291, 44, 15.5, 24, 0.1],
        [10291, 51, 42.4, 2, 0.1],
        [10292, 20, 64.8, 20, 0],
        [10293, 18, 50, 12, 0],
        [10293, 24, 3.6, 10, 0],
        [10293, 63, 35.1, 5, 0],
        [10293, 75, 6.2, 6, 0],
        [10294, 1, 14.4, 18, 0],
        [10294, 17, 31.2, 15, 0],
        [10294, 43, 36.8, 15, 0],
        [10294, 60, 27.2, 21, 0],
        [10294, 75, 6.2, 6, 0],
        [10295, 56, 30.4, 4, 0],
        [10296, 11, 16.8, 12, 0],
        [10296, 16, 13.9, 30, 0],
        [10296, 69, 28.8, 15, 0],
        [10297, 39, 14.4, 60, 0],
        [10297, 72, 27.8, 20, 0],
        [10298, 2, 15.2, 40, 0],
        [10298, 36, 15.2, 40, 0.25],
        [10298, 59, 44, 30, 0.25],
        [10298, 62, 39.4, 15, 0],
        [10299, 19, 7.3, 15, 0],
        [10299, 70, 12, 20, 0],
    ];
    const columns = ['OrderId', 'ProductId', 'UnitPrice', 'Quantity', 'Discount'];

    const table = dataManager.addTable('OrderDetails', {
        data: records.map((x) => {
            const record = {};
            columns.forEach((c, i) => record[c] = x[i]);
            return record;
        })
    });
    table.fetch().then(() => {
        table.columns.Sales = {
            value: "=[@UnitPrice]*[@Quantity]*(1-[@Discount])",
            caption: "Sales"
        };
    });
    return table;
}
function createSalesTable(dataManager) {
    const records = [
        ['2021', 'East', 'SunLin', 'Drinks', 'Apple Juice', 140, 9],
        ['2021', 'East', 'JinShiPeng', 'Drinks', 'Apple Juice', 290, 17],
        ['2021', 'East', 'ZhangShang', 'Drinks', 'Apple Juice', 300, 28],
        ['2021', 'East', 'SunYang', 'Drinks', 'Apple Juice', 120, 10],
        ['2021', 'East', 'YuanChengJie', 'Drinks', 'Apple Juice', 220, 15],
        ['2021', 'North', 'ZhangYing', 'Drinks', 'Apple Juice', 250.0, 23],
        ['2021', 'North', 'WangWei', 'Drinks', 'Apple Juice', 180.0, 17],
        ['2021', 'North', 'ZhangWu', 'Drinks', 'Apple Juice', 233.0, 23],
        ['2021', 'North', 'HanWen', 'Drinks', 'Apple Juice', 123.0, 12],
        ['2021', 'East', 'SunLin', 'Drinks', 'Milk', 431.0, 38],
        ['2021', 'East', 'JinShiPeng', 'Drinks', 'Milk', 635.0, 56],
        ['2021', 'East', 'ZhangShang', 'Drinks', 'Milk', 324.0, 33],
        ['2021', 'East', 'SunYang', 'Drinks', 'Milk', 644.0, 68],
        ['2021', 'East', 'YuanChengJie', 'Drinks', 'Milk', 343.0, 19],
        ['2021', 'North', 'ZhangYing', 'Drinks', 'Milk', 234.0, 13],
        ['2021', 'North', 'WangWei', 'Drinks', 'Milk', 666.0, 39],
        ['2021', 'North', 'ZhangWu', 'Drinks', 'Milk', 700.0, 77],
        ['2021', 'North', 'HanWen', 'Drinks', 'Milk', 111.0, 8],
        ['2021', 'East', 'SunLin', 'Drinks', 'Orange Juice', 176.0, 18],
        ['2021', 'East', 'JinShiPeng', 'Drinks', 'Orange Juice', 500.0, 20],
        ['2021', 'East', 'ZhangShang', 'Drinks', 'Orange Juice', 340.0, 19],
        ['2021', 'East', 'SunYang', 'Drinks', 'Orange Juice', 540.0, 45],
        ['2021', 'East', 'YuanChengJie', 'Drinks', 'Orange Juice', 563.0, 56],
        ['2021', 'North', 'ZhangYing', 'Drinks', 'Orange Juice', 300.0, 19],
        ['2021', 'North', 'WangWei', 'Drinks', 'Orange Juice', 490.0, 44],
        ['2021', 'North', 'ZhangWu', 'Drinks', 'Orange Juice', 233.0, 25],
        ['2021', 'North', 'HanWen', 'Drinks', 'Orange Juice', 760.0, 78],
        ['2021', 'East', 'SunLin', 'Dessert', 'Chocolate', 333.0, 32],
        ['2021', 'East', 'JinShiPeng', 'Dessert', 'Chocolate', 420.0, 46],
        ['2021', 'East', 'ZhangShang', 'Dessert', 'Chocolate', 318.0, 37],
        ['2021', 'East', 'SunYang', 'Dessert', 'Chocolate', 256.0, 21],
        ['2021', 'East', 'YuanChengJie', 'Dessert', 'Chocolate', 583.0, 54],
        ['2021', 'North', 'ZhangYing', 'Dessert', 'Chocolate', 352.0, 33],
        ['2021', 'North', 'WangWei', 'Dessert', 'Chocolate', 384.0, 39],
        ['2021', 'North', 'ZhangWu', 'Dessert', 'Chocolate', 435.0, 42],
        ['2021', 'North', 'HanWen', 'Dessert', 'Chocolate', 356.0, 36],
        ['2021', 'East', 'SunLin', 'Dessert', 'Beef Jerky', 789.0, 73],
        ['2021', 'East', 'JinShiPeng', 'Dessert', 'Beef Jerky', 156.0, 14],
        ['2021', 'East', 'ZhangShang', 'Dessert', 'Beef Jerky', 289.0, 23],
        ['2021', 'East', 'SunYang', 'Dessert', 'Beef Jerky', 562.0, 45],
        ['2021', 'East', 'YuanChengJie', 'Dessert', 'Beef Jerky', 546.0, 56],
        ['2021', 'North', 'ZhangYing', 'Dessert', 'Beef Jerky', 218.0, 17],
        ['2021', 'North', 'WangWei', 'Dessert', 'Beef Jerky', 541.0, 56],
        ['2021', 'North', 'ZhangWu', 'Dessert', 'Beef Jerky', 219.0, 21],
        ['2021', 'North', 'HanWen', 'Dessert', 'Beef Jerky', 345.0, 21],
    ];
    const columns = ['Year', 'Region', 'Salesman', 'ProductCategory', 'Product', 'Sales', 'Return'];
    return dataManager.addTable('Sales', {
        data: records.map((x) => {
            const record = {};
            columns.forEach((c, i) => record[c] = x[i]);
            return record;
        })
    });
}
```