Specify the Result Structure
Before you start the custom execution on an Analytical Backend, you have to specify details of how the result should be cross-tabulated: how the backend should lay out the data into dimensions and how to sort data in the dimensions.
Dimensions
The dimensions communicate to the Analytical Backend how to organize data into arrays. Imagine an attribute in columns vs. rows.
Each dimension specifies items. These items could be attributes' localIdentifier
's or a special measureGroup
identifier.
The measureGroup
identifier tells the executor to place all measures from the execution into the dimension.
Each dimension optionally specifies totals because the Analytical Backend calculates totals during the cross tabulation as it lays data into the dimensions.
Default dimensions
By default, an execution sets dimensions so that all attributes are in the first dimension and the measureGroup
identifier is in
the second dimension. To override this behavior, you can use the execution.withDimensions()
function and specify
dimensions yourself.
Creating a dimension specification
You can create a dimension specification using the newDimension
factory function:
newDimension(items, totals)
where:
items
is an array of attributelocalIdentifier
's, attributes, and/ormeasureGroup
.totals
is an array of total definitions; this is optional.
The definition and behavior of totals are described in Specify Table Totals.
The execution's withDimensions
function accepts an array of dimension specifications. You can define either one or
two dimensions.
Creating a two-dimensional specification
The GoodData.UI execution model provides a convenience function to create a two-dimensional specification:
newTwoDimensional(firstDimItems, secondDimItems)
where:
firstDimItems
are the items that you would normally send to thenewDimension
function; these items will be in the first dimension.secondDimItems
are the items that you would normally send to thenewDimension
function; these items will be in the second dimension.
Examples of dimensions
Execution with one measure and one attribute
Use case: one data point calculated from a measure scoped for each attribute value
import { MeasureGroupIdentifier, newDimension } from "@gooddata/sdk-model";
import * as Md from "./md/full";
execution()
.forItems([Md.LocationState, Md.$FranchiseFees])
.withDimensions(newDimension([Md.LocationState, MeasureGroupIdentifier]));
{
data: [ 32000, 41000, 77000 ]
}
Execution with two measures and one attribute
Use case: a simple table; row per attribute value, measures are in columns
import { MeasureGroupIdentifier, newTwoDimensional } from "@gooddata/sdk-model";
import * as Md from "./md/full";
execution()
.forItems([Md.LocationState, Md.$FranchiseFees, Md.$TotalSales])
.withDimensions(...newTwoDimensional([Md.LocationState], [MeasureGroupIdentifier]));
{
data: [
[ 32000, 300 ],
[ 41000, 345 ],
[ 77000, 590 ]
]
}
Alternative use case: a simple table; row per measure, one column per attribute value
import { MeasureGroupIdentifier, newTwoDimensional } from "@gooddata/sdk-model";
import * as Md from "./md/full";
execution()
.forItems([Md.LocationState, Md.$FranchiseFees, Md.$TotalSales])
.withDimensions(...newTwoDimensional([MeasureGroupIdentifier], [Md.LocationState]));
// `executionResult` is returned with measures in the first dimension:
{
data: [
[ 32000, 41000, 77000 ],
[ 300, 345, 590 ]
]
}
Execution with one measure and two attributes
Use case: stacked charts, pivot tables; one attribute used to create rows; one column per measure value calculated for each value of the second attribute
import { MeasureGroupIdentifier, newTwoDimensional } from "@gooddata/sdk-model";
import * as Md from "./md/full";
execution()
.forItems([Md.LocationState, Md.LocationCity, Md.$FranchiseFees])
.withDimensions(...newTwoDimensional([Md.LocationState], [Md.LocationCity, MeasureGroupIdentifier]));
{
data: [
[ 13000, 19000 ], // state of California, one element for each city
[ 15000, 26000 ], // state of Florida, one element for each city
[ 31000, 36000 ] // state of Texas, one element for each city
]
}
Totals
Optionally, you can define totals for each dimension. Totals are used to get aggregated data over several rows or columns of measure data.
For more information about how to define totals, see Specify Table Totals.
Sorting
When you are constructing an execution, you can optionally specify what server-sorting to apply. This is done by calling the
withSorting
function as you are preparing the execution.
import { MeasureGroupIdentifier, newTwoDimensional, newAttributeSort } from "@gooddata/sdk-model";
import * as Md from "./md/full";
execution()
.forItems([Md.LocationState, Md.LocationCity, Md.$FranchiseFees])
.withDimensions(...newTwoDimensional([Md.LocationState], [Md.LocationCity, MeasureGroupIdentifier]))
.withSorting(newAttributeSort(Md.LocationState, "desc"));
For more information about how to define sorts, see Execution Model.
Execution result
GoodData.UI models the execution as a two-phase process. When you start the execution, you almost immediately obtain the the execution result - or an error in case you did not specify the correct execution. A valid execution result contains descriptors of the shape and content of dimensions.
For example, if you start an execution for one attribute and one measure and ask for a two-dimensional result with rows for attribute values and a column for measure, the execution result will contain descriptors for two dimensions. In each dimension, the descriptor will be the essential detail about the attribute and measure respectively.
Once you have a valid execution result, the Analytical Backend is already running the computation in the background. You can
use the execution result's readAll
or readWindow
methods to access all computed data or just a window (page) of the
computed data.
When the data is available, these methods will return an instance of data view. This contains the calculated data and headers that describe the calculated data.
For the in-depth description of the result structures, see the analytical backend SPI documentation.
We do not recommend that you work directly with the raw execution results. Instead, use the abstractions and convenience
methods provided by the DataViewFacade
.
import { DataViewFacade } from "@gooddata/sdk-ui";
const executionResult = await execution().forItems(...).execute();
const dataView = await executionResult.readAll();
const facade = DataViewFacade.for(dataView);
Dimensions: Quick reference
items | dimensions | executionResult |
---|---|---|
One measure One attribute (A) |
[ "A", "measureGroup" ] |
|
Two measures (M1, M2) | [ "measureGroup" ] |
|
Two measures (M1, M2) One attribute (A) |
[ "A", "measureGroup" ] |
|
Empty first dimension Two measures (M1, M2) One attribute (A) |
[ ], [ "A", "measureGroup" ] |
|
Two measures (M1, M2) One attribute (A) // typical for a viewBy chart |
[ "A" ], [ "measureGroup" ] |
|
Two attributes (A, B) One measure (M1) // typical for a stackBy chart |
[ "A" ], [ "B", "measureGroup" ] |
|
Find out more
For more examples, sign up for the Live Examples and watch the Network tab in your browser's Developer console. You may also experiment by sending your own resultSpec
s: for example, use the Postman application.