UNPKG

turnilo

Version:

Business intelligence, data exploration and visualization web application for Druid, formerly known as Swiv and Pivot

308 lines 13.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const chronoshift_1 = require("chronoshift"); const immutable_1 = require("immutable"); const plywood_1 = require("plywood"); const general_1 = require("../../utils/general/general"); const dimension_1 = require("../dimension/dimension"); const dimensions_1 = require("../dimension/dimensions"); const filter_clause_1 = require("../filter-clause/filter-clause"); const filter_1 = require("../filter/filter"); const measures_1 = require("../measure/measures"); const query_decorator_1 = require("../query-decorator/query-decorator"); const refresh_rule_1 = require("../refresh-rule/refresh-rule"); const series_list_1 = require("../series-list/series-list"); const splits_1 = require("../splits/splits"); const queryable_data_cube_1 = require("./queryable-data-cube"); exports.DEFAULT_INTROSPECTION = "autofill-all"; const INTROSPECTION_VALUES = new Set(["none", "no-autofill", "autofill-dimensions-only", "autofill-measures-only", "autofill-all"]); exports.DEFAULT_DEFAULT_TIMEZONE = chronoshift_1.Timezone.UTC; const DEFAULT_DEFAULT_FILTER = filter_1.EMPTY_FILTER; const DEFAULT_DEFAULT_SPLITS = splits_1.EMPTY_SPLITS; exports.DEFAULT_DEFAULT_DURATION = chronoshift_1.Duration.fromJS("P1D"); exports.DEFAULT_MAX_SPLITS = 3; exports.DEFAULT_MAX_QUERIES = 500; function checkDimensionsAndMeasuresNamesUniqueness(dimensions, measures, dataCubeName) { if (dimensions != null && measures != null) { const dimensionNames = Object.keys(dimensions.byName); const measureNames = Object.keys(measures.byName); const duplicateNames = immutable_1.List(measureNames) .concat(dimensionNames) .groupBy(name => name) .filter(names => names.count() > 1) .map((names, name) => name) .toList(); if (duplicateNames.size > 0) { throw new Error(`data cube: '${dataCubeName}', names: ${general_1.quoteNames(duplicateNames)} found in both dimensions and measures'`); } } } function readDescription({ description, extendedDescription }) { if (!description) { return { description: "" }; } if (extendedDescription) { return { description, extendedDescription }; } const segments = description.split(/\n---\n/); if (segments.length === 0) { return { description }; } return { description: segments[0], extendedDescription: segments.splice(1).join("\n---\n ") }; } function readIntrospection(config) { const introspection = config.introspection || exports.DEFAULT_INTROSPECTION; if (!INTROSPECTION_VALUES.has(introspection)) { throw new Error(`invalid introspection value ${introspection}, must be one of ${[...INTROSPECTION_VALUES].join(", ")}`); } return introspection; } function readName(config) { const name = config.name; if (!name) throw new Error("DataCube must have a name"); general_1.verifyUrlSafeName(name); return name; } function verifyCluster(config, cluster) { if (config.clusterName === "native") return; if (cluster === undefined) { throw new Error(`Could not find non-native cluster with name "${config.clusterName}" for data cube "${config.name}"`); } if (config.clusterName !== cluster.name) { throw new Error(`Cluster name '${config.clusterName}' was given but '${cluster.name}' cluster was supplied (must match)`); } } function readAttributes(config) { const attributeOverrides = plywood_1.AttributeInfo.fromJSs(config.attributeOverrides || []); const attributes = plywood_1.AttributeInfo.fromJSs(config.attributes || []); const derivedAttributes = config.derivedAttributes ? plywood_1.Expression.expressionLookupFromJS(config.derivedAttributes) : {}; return { attributes, attributeOverrides, derivedAttributes }; } function readTimeAttribute(config, cluster, dimensions, logger) { const isFromDruidCluster = config.clusterName !== "native" && cluster.type === "druid"; if (isFromDruidCluster) { if (!general_1.isTruthy(config.timeAttribute)) { logger.warn(`DataCube "${config.name}" should have property timeAttribute. Setting timeAttribute to default value "__time"`); } if (general_1.isTruthy(config.timeAttribute) && config.timeAttribute !== "__time") { logger.warn(`timeAttribute in DataCube "${config.name}" should have value "__time" because it is required by Druid. Overriding timeAttribute to "__time"`); } const timeAttribute = plywood_1.$("__time"); if (!general_1.isTruthy(dimensions_1.findDimensionByExpression(dimensions, timeAttribute))) { return { timeAttribute, dimensions: dimensions_1.prepend(dimension_1.timeDimension(timeAttribute), dimensions) }; } else { return { timeAttribute, dimensions }; } } else { if (!general_1.isTruthy(config.timeAttribute)) { throw new Error(`DataCube "${config.name}" must have defined timeAttribute property`); } const timeAttribute = plywood_1.$(config.timeAttribute); return { timeAttribute, dimensions }; } } function readDimensions(config) { return dimensions_1.fromConfig(config.dimensions || []); } function readColumns(config) { const name = config.name; try { const dimensions = readDimensions(config); const measures = measures_1.fromConfig(config.measures || []); checkDimensionsAndMeasuresNamesUniqueness(dimensions, measures, name); return { dimensions, measures }; } catch (e) { throw new Error(`data cube: '${name}', ${e.message}`); } } function verifyDefaultSortMeasure(config, measures) { if (config.defaultSortMeasure) { if (!measures_1.hasMeasureWithName(measures, config.defaultSortMeasure)) { throw new Error(`Can not find defaultSortMeasure '${config.defaultSortMeasure}' in data cube '${config.name}'`); } } } function readDefaultFilter(config) { if (!config.defaultFilter) return undefined; try { return filter_1.Filter.fromJS(config.defaultFilter); } catch (_a) { throw new Error(`Incorrect format of default filter for ${config.name}. Ignoring field`); } } function fromConfig(config, cluster, logger) { const name = readName(config); const introspection = readIntrospection(config); verifyCluster(config, cluster); const { attributes, attributeOverrides, derivedAttributes } = readAttributes(config); const refreshRule = config.refreshRule ? refresh_rule_1.RefreshRule.fromJS(config.refreshRule) : refresh_rule_1.RefreshRule.query(); const { dimensions: initialDimensions, measures } = readColumns(config); const { timeAttribute, dimensions } = readTimeAttribute(config, cluster, initialDimensions, logger); verifyDefaultSortMeasure(config, measures); const subsetFormula = config.subsetFormula || config.subsetFilter; const defaultFilter = readDefaultFilter(config); const { description, extendedDescription } = readDescription(config); return { name, title: config.title || config.name, description, extendedDescription, clusterName: config.clusterName || "druid", source: config.source || config.name, group: config.group || null, subsetExpression: subsetFormula ? plywood_1.Expression.fromJSLoose(subsetFormula) : plywood_1.Expression.TRUE, rollup: Boolean(config.rollup), options: config.options || {}, introspection, attributeOverrides, attributes, derivedAttributes, dimensions, measures, timeAttribute, defaultFilter, defaultTimezone: config.defaultTimezone ? chronoshift_1.Timezone.fromJS(config.defaultTimezone) : exports.DEFAULT_DEFAULT_TIMEZONE, defaultSplitDimensions: config.defaultSplitDimensions || [], defaultDuration: config.defaultDuration ? chronoshift_1.Duration.fromJS(config.defaultDuration) : exports.DEFAULT_DEFAULT_DURATION, defaultSortMeasure: getDefaultSortMeasure(config, measures), defaultSelectedMeasures: config.defaultSelectedMeasures || [], defaultPinnedDimensions: config.defaultPinnedDimensions || [], maxSplits: config.maxSplits || exports.DEFAULT_MAX_SPLITS, maxQueries: config.maxQueries || exports.DEFAULT_MAX_QUERIES, queryDecorator: config.queryDecorator ? query_decorator_1.QueryDecoratorDefinition.fromJS(config.queryDecorator) : null, refreshRule, cluster }; } exports.fromConfig = fromConfig; function serialize(dataCube) { const { attributes, clusterName, defaultDuration, defaultFilter, defaultPinnedDimensions, defaultSelectedMeasures, defaultSortMeasure, defaultSplitDimensions, defaultTimezone, description, dimensions, extendedDescription, group, maxSplits, measures, name, options, refreshRule, rollup, source, timeAttribute, title } = dataCube; return { attributes: attributes.map(a => a.toJS()), clusterName, defaultDuration: defaultDuration && defaultDuration.toJS(), defaultFilter: defaultFilter && defaultFilter.toJS(), defaultPinnedDimensions, defaultSelectedMeasures, defaultSortMeasure, defaultSplitDimensions, defaultTimezone: defaultTimezone.toJS(), description, dimensions: dimensions_1.serialize(dimensions), extendedDescription, group, maxSplits, measures: measures_1.serialize(measures), name, options, refreshRule: refreshRule.toJS(), rollup, source, timeAttribute: timeAttribute.name, title }; } exports.serialize = serialize; function fromClusterAndExternal(name, cluster, external, logger) { const dataCube = fromConfig({ name, clusterName: cluster.name, source: String(external.source), refreshRule: refresh_rule_1.RefreshRule.query().toJS() }, cluster, logger); return queryable_data_cube_1.attachExternalExecutor(dataCube, external); } exports.fromClusterAndExternal = fromClusterAndExternal; function getMaxTime({ name, refreshRule }, timekeeper) { if (refreshRule.isRealtime()) { return timekeeper.now(); } else if (refreshRule.isFixed()) { return refreshRule.time; } else { return timekeeper.getTime(name); } } exports.getMaxTime = getMaxTime; function getDimensionsByKind(dataCube, kind) { return dimensions_1.allDimensions(dataCube.dimensions).filter(d => d.kind === kind); } exports.getDimensionsByKind = getDimensionsByKind; function isTimeAttribute(dataCube, ex) { return ex instanceof plywood_1.RefExpression && ex.name === dataCube.timeAttribute; } exports.isTimeAttribute = isTimeAttribute; function getTimeDimension(dataCube) { const dimension = dimensions_1.findDimensionByExpression(dataCube.dimensions, plywood_1.$(dataCube.timeAttribute)); if (dimension === null) { throw new Error(`Expected DataCube "${dataCube.name}" to have timeAttribute property defined with expression of existing dimension`); } return dimension; } exports.getTimeDimension = getTimeDimension; function getTimeDimensionReference(dataCube) { return getTimeDimension(dataCube).name; } exports.getTimeDimensionReference = getTimeDimensionReference; function getDefaultFilter(dataCube) { const filter = dataCube.defaultFilter || DEFAULT_DEFAULT_FILTER; if (!dataCube.timeAttribute) return filter; const timeDimensionReference = getTimeDimensionReference(dataCube); return filter.insertByIndex(0, new filter_clause_1.RelativeTimeFilterClause({ period: filter_clause_1.TimeFilterPeriod.LATEST, duration: dataCube.defaultDuration, reference: timeDimensionReference })); } exports.getDefaultFilter = getDefaultFilter; function getDefaultSplits(dataCube) { if (dataCube.defaultSplitDimensions) { const dimensions = dataCube.defaultSplitDimensions.map(name => dimensions_1.findDimensionByName(dataCube.dimensions, name)); return splits_1.Splits.fromDimensions(dimensions); } return DEFAULT_DEFAULT_SPLITS; } exports.getDefaultSplits = getDefaultSplits; function getDefaultSeries(dataCube) { if (dataCube.defaultSelectedMeasures.length > 0) { return series_list_1.SeriesList.fromMeasures(dataCube.defaultSelectedMeasures.map(name => measures_1.findMeasureByName(dataCube.measures, name))); } const first4Measures = measures_1.allMeasures(dataCube.measures).slice(0, 4); return series_list_1.SeriesList.fromMeasures(first4Measures); } exports.getDefaultSeries = getDefaultSeries; function getDefaultSortMeasure(dataCube, measures) { if (dataCube.defaultSortMeasure) return dataCube.defaultSortMeasure; const firstMeasure = measures_1.allMeasures(measures)[0]; if (firstMeasure) return firstMeasure.name; return undefined; } exports.getDefaultSortMeasure = getDefaultSortMeasure; //# sourceMappingURL=data-cube.js.map