Skip to content

Utility.Export.Cube to CSV

The below code is used to export data from a Cube to a CSV.

TIP

By changing cube.slice to cube.sliceStatic, formula values will be ignored, and only cells with a static value will be sliced.

Process Code

vb

/**
 * MODLR PROCESS SCRIPT: Cube -> CSV Export
 * -----------------------------------------
 * Purpose:
 *   Prompts for a cube name and an output CSV filename, reads that
 *   cube's full dimension structure to build a header row (one column
 *   per dimension + a "value" column), then streams every cell in the
 *   cube out to a CSV file on the datasource.
 *
 */

// --- Config -------------------------------------------------------------
var delim = ","   // Field delimiter used when writing the CSV

/**
 * pre()
 * Runs once before the process executes. Registers the two prompts
 * MODLR asks for at run time.
 */
function pre() {
    script.log('process pre-execution parameters parsed.');

    // Registers a text prompt named "cube name" — its answer becomes the
    // global `cube_name` variable available in begin()/data()/end().
    script.prompt("cube name", "cube_name", "");

    // Registers a text prompt named "csv name" — its answer becomes the
    // global `csv_name` variable, used as the output filename (without
    // the .csv extension, which is appended in begin()).
    script.prompt("csv name", "csv_name", "example_name");
}

/**
 * begin()
 * Runs once at the start of the process. This is where the whole
 * cube -> CSV export happens:
 *   1. Read the cube's dimension list to build the header row.
 *   2. Open the output CSV file and write the header.
 *   3. Stream every cell in the cube out as a CSV row.
 */
function begin() {
    script.log('process execution started.');

    // cube.dimensions() returns the cube's dimension metadata as a JSON string.
    let dimensions = JSON.parse(cube.dimensions(cube_name));

    let dimensionArray = [];   // header/column names for the CSV, in order
    let sliceArray = [];       // one "" per dimension = "give me every member" for cube.slice()

    for (let i = 0; i < dimensions.length; i++) {
        let dimension = dimensions[i];
        let dname = dimension.name.toLowerCase().replace(/\s+/g, '_');

        dimensionArray.push(dname);

        // Empty string = no filter on this dimension, i.e. pull every member
        sliceArray.push("");

        if (script.IsCancelled()) {
            return;
        }
    }

    // The measure itself is exported as an extra "value" column
    dimensionArray.push("value");

    // datasource.csv() opens/creates a CSV file on the datasource for writing.
    let csv = datasource.csv(`${csv_name}.csv`);
    csv.setDelimeter(delim);

    // First write is the header row (dimension names + "value").
    csv.write(dimensionArray);

    // cube.slice() returns a cursor over every cell matching sliceArray
    // (here: every member of every dimension, i.e. the whole cube).
    let slice = cube.slice(cube_name, sliceArray);
    console.log("slice", cube_name, sliceArray);

    for (let elms of slice) {
        elms = [...elms];

        // The last element is the cell's value. Popped and pushed back
        // as-is here — unlike the MySQL export variant of this script,
        // there's no fixed column width to clip against for a CSV file,
        // so the value is written out unchanged.
        let value = elms.pop();
        elms.push(value);

        csv.write(elms);

        if (script.IsCancelled()) {
            return;
        }
    }
}

/**
 * data(record)
 * Runs once per record on a second pass. Not used by this script —
 * the export is done entirely in begin() in a single pass.
 */
function data(record) {
}

/**
 * end()
 * Runs once at the end of the process.
 */
function end() {
    script.log('process execution finished.');
}