[]
        
(Showing Draft Content)

데이터 원본 바인딩

데이터 차트에서 데이터를 효과적으로 표시하려면 차트를 데이터 소스에 연결하는 것이 필수입니다. 이 항목에서는 데이터 차트에서 데이터 소스로부터 데이터를 바인딩하는 방법을 설명합니다.

SpreadJS에서 데이터 소스는 일반적으로 데이터 매니저가 관리하는 테이블로 표현되며, 시각화 과정은 데이터 매니저의 테이블 정보를 바인딩함으로써 완성됩니다.

데이터 차트를 생성하고 관리하려면 GC.Spread.Sheets.DataCharts 네임스페이스의 IDataChartConfig 인터페이스를 사용합니다. 이 인터페이스에는 다음과 같은 속성이 포함됩니다:

속성 이름

설명

plots

[필수] 차트에 데이터를 어떻게 표시할지 정의하는 plot 옵션입니다.

config

[선택] 데이터 차트에 대한 추가 구성 옵션입니다.

tableName

[선택] 데이터 바인딩에 사용할 테이블의 이름입니다.

차트 내에서 바인딩된 필드가 어느 테이블에 속하는지를 명확히 지정할 수 있습니다.


config.tableName을 설정하면, 바인딩할 필드가 속한 테이블을 지정할 수 있습니다.

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


필드

설명

values

Y축에 표시할 필드를 지정하며, 여러 하위 속성을 지원합니다. 만약 데이터 차트에 values가 설정되지 않으면, SpreadJS에서 제공하는 기본 데이터가 자동으로 바인딩됩니다.

사용자는 특정 요구에 따라 각 필드에 서로 다른 요약 방식(aggregate)을 지정할 수 있습니다.

values 속성에 여러 필드를 설정하는 경우 legend 필드를 별도로 설정하더라도 적용되지 않으며, 대신 데이터 차트는 각 values 필드에 따라 자동으로 범례를 생성합니다.

참고: pie, donut 차트와 같은 일부 차트 유형에서는 values단일 필드만 바인딩할 수 있습니다.

category

데이터를 축(axis) 또는 차원(dimension) 기준으로 정의하고 그룹화합니다.

이는 데이터를 서로 다른 범주로 나누어, 사용자에게 데이터의 분포나 추세를 보다 쉽게 이해할 수 있도록 도와줍니다.

막대(bar) 차트나 라인(line) 차트에서는 category가 보통 시간(연도, 월 등) 또는 지역, 제품 유형 등의 분류 기준을 나타냅니다.

파이(pie) 차트에서는 category가 차트의 각 섹터가 어떤 범주에 해당하는지를 나타냅니다.

또한 category 필드를 바인딩할 때 정렬(sorting) 정보를 함께 지정할 수 있습니다. 정렬 옵션에는 Field, Aggregate, Order가 포함됩니다.

details

데이터를 여러 하위 섹션으로 나누며, 이를 통해 plot 내에서 클러스터(cluster) 또는 스택(stack) 방식으로 시각화할 수 있습니다.

데이터 차트에서는 sunburst 차트 유형을 제외한 모든 차트에서 details 필드를 최대 한 개까지만 바인딩할 수 있습니다.

color

서로 다른 기호(symbol)를 가진 차트 유형에서 색상 범례(color legend)를 표시합니다.

color 필드는 문자열(string), 불리언(boolean), 날짜(date), 숫자(number) 등 다양한 타입의 값을 허용합니다.

size

plot과 범례에 대해 연속적인 크기 기반의 스케일(scale) 을 적용할 필드를 지정합니다.

color 범례와 유사하게, size 범례는 보통 3차원 데이터를 숫자 스케일로 시각화할 때 사용됩니다.

해당 속성은 bubble plot에만 적용 가능합니다.

tooltip

plot에 표시될 툴팁 내에 어떤 필드들이 포함될지를 결정합니다.

데이터 차트는 tooltip 인코딩을 통해, 각 데이터 포인트 위에 마우스를 올렸을 때 표시될 툴팁 내용을 결정합니다.

툴팁이 표시되는 동안, 축 레이블(axis labels) 이나 기준선 레이블(reference line labels) 등은 무시됩니다.

filter

필터는 원본 데이터에 적용되며, 데이터가 집계되기 전에 필터링이 수행됩니다.

이 필드는 숫자 타입과 문자열 타입을 위한 두 가지 필터링 방법을 포함하고 있습니다.

데이터 차트에서 제공되는 필터는 여러 단계로 중첩(nested) 될 수 있으며, 논리 연산을 통해 하나의 계층 내에서 "AND" 또는 "OR" 관계를 설정할 수 있습니다.

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

코드 사용 예시

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

// 데이터 매니저 가져오기
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;
        })
    });
}