const getQueryGranularity = (queries) => R.pipe( R.map(({ timeDimensions }) => timeDimensions[0] && timeDimensions[0].granularity || null), R.filter(Boolean), R.uniq )(queries)
collectLeafMeasures(fn) { const context = { leafMeasures: {} }; this.evaluateSymbolSqlWithContext( fn, context ); return R.pipe( R.toPairs, R.map(([measure, isLeaf]) => isLeaf && measure), R.filter(R.identity) )(context.leafMeasures); }
queueArray(queueObj, orderFilterLessThan) { return R.pipe( R.values, R.filter(orderFilterLessThan ? q => q.order < orderFilterLessThan : R.identity), R.sortBy(q => q.order), R.map(q => q.key) )(queueObj); }
dimensionColumns(tableDefinition) { const dimensionColumns = tableDefinition.filter( column => !column.name.startsWith('_') && this.columnType(column) === 'string' || column.attributes && column.attributes.indexOf('primaryKey') !== -1 || column.name.toLowerCase() === 'id' ); const timeColumns = R.pipe( R.filter(column => !column.name.startsWith('_') && this.columnType(column) === 'time'), R.sortBy(column => this.timeColumnIndex(column)), R.map(column => ({ ...column, columnType: 'time' })) // TODO do we need it? )(tableDefinition); return dimensionColumns.concat(timeColumns); }
queriesForContext(contextId) { // return All queries if no context pass if (R.isNil(contextId) || R.isEmpty(contextId)) { return this.queries; } const context = this.contextEvaluator.contextDefinitions[contextId]; // If contextId is wrong if (R.isNil(context)) { throw new UserError(`Context ${contextId} doesn't exist`); } // As for now context works on the cubes level return R.filter( (query) => R.contains(query.config.name, context.contextMembers) )(this.queries); }
findPreAggregationToUseForCube(cube) { const preAggregates = this.query.cubeEvaluator.preAggregationsForCube(cube); const originalSqlPreAggregations = R.pipe( R.toPairs, // eslint-disable-next-line no-unused-vars R.filter(([k, a]) => a.type === 'originalSql') )(preAggregates); if (originalSqlPreAggregations.length) { const [preAggregationName, preAggregation] = originalSqlPreAggregations[0]; return { preAggregationName, preAggregation, cube, references: this.evaluateAllReferences(cube, preAggregation) }; } return null; }
transform(cubeName, errorReporter) { const cube = this.getCubeDefinition(cubeName); const duplicateNames = R.compose( R.map(nameToDefinitions => nameToDefinitions[0]), R.toPairs, R.filter(definitionsByName => definitionsByName.length > 1), R.groupBy(nameToDefinition => nameToDefinition[0]), R.unnest, R.map(R.toPairs), R.filter(v => !!v) )([cube.measures, cube.dimensions, cube.segments]); if (duplicateNames.length > 0) { errorReporter.error(`${duplicateNames.join(', ')} defined more than once`); } return Object.assign( { cubeName: () => cube.name }, cube.measures || {}, cube.dimensions || {}, cube.segments || {} ); }
timeDimensionPathsForCube(cube) { return R.compose( R.map(nameToDefinition => `${cube}.${nameToDefinition[0]}`), R.toPairs, R.filter(d => d.type === 'time') )(this.evaluatedCubes[cube].dimensions || {}); }
fullKeyQueryAggregateMeasures() { const measureToHierarchy = this.collectRootMeasureToHieararchy(); const measuresToRender = (multiplied, cumulative) => R.pipe( R.values, R.flatten, R.filter( m => m.multiplied === multiplied && this.newMeasure(m.measure).isCumulative() === cumulative ), R.map(m => m.measure), R.uniq, R.map(m => this.newMeasure(m)) ); const multipliedMeasures = measuresToRender(true, false)(measureToHierarchy); const regularMeasures = measuresToRender(false, false)(measureToHierarchy); const cumulativeMeasures = R.pipe( R.map(multiplied => R.xprod([multiplied], measuresToRender(multiplied, true)(measureToHierarchy))), R.unnest )([false, true]); return { multipliedMeasures, regularMeasures, cumulativeMeasures }; }
buildJoin(cubesToJoin) { if (!cubesToJoin.length) { return null; } const key = JSON.stringify(cubesToJoin); if (!this.builtJoins[key]) { const join = R.pipe( R.map( cube => this.buildJoinTreeForRoot(cube, R.without([cube], cubesToJoin)) ), R.filter(R.identity), R.sortBy(joinTree => joinTree.joins.length) )(cubesToJoin)[0]; if (!join) { throw new UserError(`Can't find join path to join ${cubesToJoin.map(v => `'${v}'`).join(', ')}`); } this.builtJoins[key] = Object.assign(join, { multiplicationFactor: R.compose( R.fromPairs, R.map(v => [v, this.findMultiplicationFactorFor(v, join.joins)]) )(cubesToJoin) }); } return this.builtJoins[key]; }
findAutoRollupPreAggregationsForCube(cube, preAggregations) { if ( R.any(m => m.path() && m.path()[0] === cube, this.query.measures) || !this.query.measures.length && !this.query.timeDimensions.length && R.all(d => d.path() && d.path()[0] === cube, this.query.dimensions) ) { return R.pipe( R.toPairs, // eslint-disable-next-line no-unused-vars R.filter(([k, a]) => a.type === 'autoRollup'), R.map(([preAggregationName, preAggregation]) => { const cubeLattice = this.getCubeLattice(cube, preAggregationName, preAggregation); const optimalPreAggregation = cubeLattice.findOptimalPreAggregationFromLattice(this.query); return optimalPreAggregation && { preAggregationName: preAggregationName + this.autoRollupNameSuffix(cube, optimalPreAggregation), preAggregation: Object.assign( optimalPreAggregation, preAggregation ), cube, canUsePreAggregation: true, references: optimalPreAggregation }; }) )(preAggregations); } return []; }
preAggregationsDescriptionLocal() { const isInPreAggregationQuery = this.query.options.preAggregationQuery; if (!isInPreAggregationQuery) { const preAggregationForQuery = this.findPreAggregationForQuery(); if (preAggregationForQuery) { return this.preAggregationDescriptionsFor(preAggregationForQuery.cube, preAggregationForQuery); } } if ( !isInPreAggregationQuery || isInPreAggregationQuery && this.query.options.useOriginalSqlPreAggregationsInPreAggregation) { return R.pipe( R.map(cube => { const foundPreAggregation = this.findPreAggregationToUseForCube(cube); if (foundPreAggregation) { return this.preAggregationDescriptionsFor(cube, foundPreAggregation); } return null; }), R.filter(R.identity), R.unnest )(this.preAggregationCubes()); } return []; }
compile(cubes, errorReporter) { this.edges = R.compose( R.fromPairs, R.unnest, R.map(v => this.buildJoinEdges(v, errorReporter.inContext(`${v.name} cube`))), R.filter(this.cubeValidator.isCubeValid.bind(this.cubeValidator)) )(this.cubeEvaluator.cubeList); this.nodes = R.compose( R.map(groupedByFrom => R.fromPairs(groupedByFrom.map(join => [join.to, 1]))), R.groupBy(join => join.from), R.map(v => v[1]), R.toPairs )(this.edges); this.undirectedNodes = R.compose( R.map(groupedByFrom => R.fromPairs(groupedByFrom.map(join => [join.from, 1]))), R.groupBy(join => join.to), R.unnest, R.map(v => [v[1], { from: v[1].to, to: v[1].from }]), R.toPairs )(this.edges); this.graph = new Graph(this.nodes); }
collectFrom(membersToCollectFrom, fn, methodName, cache) { return R.pipe( R.map(f => f.getMembers()), R.flatten, R.map(s => ( (cache || this.compilerCache).cache( ['collectFrom', methodName].concat( s.path() ? [s.path().join('.')] : [s.cube().name, s.expressionName || s.definition().sql] ), () => fn(() => this.traverseSymbol(s)) ) )), R.unnest, R.uniq, R.filter(R.identity) )( membersToCollectFrom ); }
findRollupPreAggregationsForCube(cube, canUsePreAggregation, preAggregations) { return R.pipe( R.toPairs, // eslint-disable-next-line no-unused-vars R.filter(([k, a]) => a.type === 'rollup'), R.map(([preAggregationName, preAggregation]) => { const references = this.evaluateAllReferences(cube, preAggregation); return { preAggregationName, preAggregation, cube, canUsePreAggregation: canUsePreAggregation(references), references }; }) )(preAggregations); }