blob: 6201eca8cf2e3dc5a65ec5a66c9f6d3c67f082cb [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
KylinApp.controller('CubeEditCtrl', function ($scope, $q, $routeParams, $location, $templateCache, $interpolate, MessageService, TableService, CubeDescService, CubeService, loadingRequest, SweetAlert, $log, cubeConfig, CubeDescModel, MetaModel, TableModel, ModelDescService, modelsManager, cubesManager, ProjectModel, StreamingModel, StreamingService,VdmUtil, MessageBox) {
$scope.cubeConfig = cubeConfig;
$scope.metaModel = {};
$scope.modelsManager = modelsManager;
//add or edit ?
var absUrl = $location.absUrl();
$scope.cubeMode = absUrl.indexOf("/cubes/add") != -1 ? 'addNewCube' : absUrl.indexOf("/cubes/edit") != -1 ? 'editExistCube' : 'default';
$scope.cubeMode2 = 'default';
$scope.isMeasureEdit = false;
if ($scope.cubeMode === 'default' && absUrl.indexOf("/cubes/measure") != -1) {
$scope.cubeMode2 = 'measure';
$scope.isMeasureEdit = true;
$scope.cubeMode = 'editExistCube';
}
if ($scope.cubeMode == "addNewCube" &&ProjectModel.selectedProject==null) {
SweetAlert.swal('Oops...', 'Please select your project first.', 'warning');
$location.path("/models");
return;
}
//init encoding list
$scope.store = {
supportedEncoding:[],
encodingMaps:{}
}
$scope.getColumnsByAlias = function (alias) {
var temp = [];
angular.forEach(TableModel.selectProjectTables, function (table) {
if (table.name == $scope.aliasTableMap[alias]) {
temp = table.columns;
}
});
return temp;
};
$scope.getColumnsByTable = function (tableName) {
var temp = [];
angular.forEach(TableModel.selectProjectTables, function (table) {
if (table.name == tableName) {
temp = table.columns;
}
});
return temp;
};
//get columns from model
$scope.getDimColumnsByAlias = function (alias) {
if (!alias) {
return [];
}
var tableColumns = $scope.modelsManager.getColumnsByAlias(alias);
var tableDim = _.find($scope.metaModel.model.dimensions, function (dimension) {
return dimension.table == alias
});
if(!tableDim){
return [];
}
var tableDimColumns = tableDim.columns;
var avaColObject = _.filter(tableColumns, function (col) {
return tableDimColumns.indexOf(col.name) != -1;
});
return avaColObject;
};
$scope.getMetricColumnsByTable = function (tableName) {
if (!tableName) {
return [];
}
var tableColumns = $scope.getColumnsByTable(tableName);
var tableMetrics = $scope.metaModel.model.metrics;
var avaColObject = _.filter(tableColumns, function (col) {
return tableMetrics.indexOf(col.name) != -1;
});
return avaColObject;
};
$scope.getCommonMetricColumns = function () {
//metric from model
var me_columns = [];
if($scope.metaModel.model.metrics){
angular.forEach($scope.metaModel.model.metrics,function(metric,index){
me_columns.push(metric);
})
}
return me_columns;
};
$scope.getAllModelDimMeasureColumns = function () {
var me_columns = [];
if($scope.metaModel.model.metrics){
angular.forEach($scope.metaModel.model.metrics,function(metric){
me_columns.push(metric);
})
}
angular.forEach($scope.metaModel.model.dimensions,function(dimension){
if(dimension.columns){
angular.forEach(dimension.columns,function(column){
me_columns = me_columns.concat(dimension.table+"."+column);
});
}
});
return distinct_array(me_columns);
};
$scope.getAllModelDimColumns = function () {
var me_columns = [];
angular.forEach($scope.metaModel.model.dimensions,function(dimension,index){
if(dimension.columns){
angular.forEach(dimension.columns,function(column){
me_columns = me_columns.concat(dimension.table+"."+column);
});
}
})
return distinct_array(me_columns);
};
function distinct_array(arrays){
var arr = [];
for(var item in arrays){
if(arr.indexOf(arrays[item])==-1){
arr.push(arrays[item]);
}
}
return arr;
}
$scope.getExtendedHostColumn = function(){
var me_columns = [];
//add cube dimension column for specific measure
angular.forEach($scope.cubeMetaFrame.dimensions,function(dimension,index){
if($scope.modelsManager.availableFactTables.indexOf(dimension.table)==-1){
return;
}
if(dimension.column && dimension.derived == null){
me_columns.push(dimension.table+"."+dimension.column);
}
});
return me_columns;
}
$scope.getFactColumns = function () {
var me_columns = [];
angular.forEach($scope.cubeMetaFrame.dimensions,function(dimension,index){
if(dimension.column && dimension.derived == null){
me_columns.push(dimension.table+"."+dimension.column);
}
else{
angular.forEach(dimension.derived,function(derived){
me_columns.push(dimension.table+"."+derived);
});
}
});
angular.forEach($scope.cubeMetaFrame.measures,function(measure){
if(measure.function.parameter.type == "column"){
me_columns.push(measure.function.parameter.value);
}
});
return distinct_array(me_columns);
};
$scope.getColumnType = function (_column, alias) {
var columns = $scope.modelsManager.getColumnsByAlias(alias);
var type;
angular.forEach(columns, function (column) {
if (_column === column.name) {
type = column.datatype;
return;
}
});
return type;
};
$scope.isIntMeasure = function (measure) {
var column = measure.function.parameter.value;
if(column && (typeof column=="string")){
var colType = $scope.getColumnType(VdmUtil.removeNameSpace(column), VdmUtil.getNameSpaceAliasName(column));
if(colType==="tinyint"||colType==="smallint"||colType==="int"||colType==="integer"){
return true;
}
}
return false;
};
// ~ Define data
$scope.state = {
"cubeSchema": "",
"cubeInstance":"",
"mode": 'edit'
};
//fetch cube info and model info in edit model
// ~ init
if ($scope.isEdit = !!$routeParams.cubeName) {
CubeDescService.query({cube_name: $routeParams.cubeName}, function (detail) {
if (detail.length > 0) {
$scope.cubeMetaFrame = detail[0];
$scope.metaModel = {};
//get model from API when page refresh
if (!modelsManager.getModels().length) {
ModelDescService.query({model_name: $scope.cubeMetaFrame.model_name}, function (_model) {
$scope.metaModel.model = _model;
$scope.modelsManager.initAliasMapByModelSchema($scope.metaModel);
});
}
$scope.state.cubeSchema = angular.toJson($scope.cubeMetaFrame, true);
}
});
var queryParam = {
cubeId: $routeParams.cubeName
};
CubeService.getCube(queryParam, {},function(instance){
if (instance) {
$scope.instance = instance;
$scope.state.cubeInstance =angular.toJson($scope.instance,true);
} else {
SweetAlert.swal('Oops...', "No cube detail info loaded.", 'error');
}
},function(e){
if (e.data && e.data.exception) {
var message = e.data.exception;
var msg = !!(message) ? message : 'Failed to take action.';
SweetAlert.swal('Oops...', msg, 'error');
} else {
SweetAlert.swal('Oops...', "Failed to take action.", 'error');
}
});
} else {
// $scope.cubeMetaFrame = CubeDescModel.createNew();
$scope.cubeMetaFrame = CubeDescModel.createNew();
$scope.metaModel = {
model: modelsManager.getModel($scope.cubeMetaFrame.model_name)
}
//$scope.cubeMetaFrame.model_name = modelName;
$scope.state.cubeSchema = angular.toJson($scope.cubeMetaFrame, true);
}
$scope.prepareCube = function () {
//generate rowkey
reGenerateRowKey();
if ($scope.metaModel.model.partition_desc.partition_date_column && ($scope.cubeMetaFrame.partition_date_start | $scope.cubeMetaFrame.partition_date_start == 0)) {
if ($scope.metaModel.model.partition_desc.partition_date_column.indexOf(".") == -1) {
$scope.metaModel.model.partition_desc.partition_date_column = $scope.metaModel.model.fact_table + "." + $scope.metaModel.model.partition_desc.partition_date_column;
}
}
//set model ref for cubeDesc
if ($scope.cubeMetaFrame.model_name === "" || angular.isUndefined($scope.cubeMetaFrame.model_name)) {
$scope.cubeMetaFrame.model_name = $scope.cubeMetaFrame.name;
}
VdmUtil.removeElementInArrayByValue($scope.cubeMetaFrame.auto_merge_time_ranges, 0);
$scope.state.project = ProjectModel.getSelectedProject();
// delete $scope.cubeMetaFrame.project;
$scope.state.cubeSchema = angular.toJson($scope.cubeMetaFrame, true);
};
$scope.cubeResultTmpl = function (notification) {
// Get the static notification template.
var tmpl = notification.type == 'success' ? 'cubeResultSuccess.html' : 'cubeResultError.html';
return $interpolate($templateCache.get(tmpl))(notification);
};
$scope.saveCube = function () {
try {
angular.fromJson($scope.state.cubeSchema);
} catch (e) {
SweetAlert.swal('Oops...', 'Invalid cube json format..', 'error');
return;
}
SweetAlert.swal({
title: '',
text: 'Are you sure to save the cube ?',
type: '',
showCancelButton: true,
confirmButtonColor: '#DD6B55',
confirmButtonText: "Yes",
closeOnConfirm: true
}, function (isConfirm) {
if (isConfirm) {
loadingRequest.show();
if ($scope.isEdit) {
CubeService.update({}, {
cubeDescData: VdmUtil.filterNullValInObj($scope.state.cubeSchema),
cubeName: $routeParams.cubeName,
project: $scope.state.project
}, function (request) {
if (request.successful) {
$scope.state.cubeSchema = request.cubeDescData;
MessageBox.successNotify('Updated the cube successfully.');
$location.path("/models");
} else {
$scope.saveCubeRollBack();
$scope.cubeMetaFrame.project = $scope.state.project;
var message = request.message;
var msg = !!(message) ? message : 'Failed to take action.';
MessageService.sendMsg($scope.cubeResultTmpl({
'text': msg,
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
}
//end loading
loadingRequest.hide();
}, function (e) {
$scope.saveCubeRollBack();
if (e.data && e.data.exception) {
var message = e.data.exception;
var msg = !!(message) ? message : 'Failed to take action.';
MessageService.sendMsg($scope.cubeResultTmpl({
'text': msg,
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
} else {
MessageService.sendMsg($scope.cubeResultTmpl({
'text': 'Failed to take action.',
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
}
loadingRequest.hide();
});
} else {
CubeService.save({}, {
cubeDescData: VdmUtil.filterNullValInObj($scope.state.cubeSchema),
project: $scope.state.project
}, function (request) {
if (request.successful) {
$scope.state.cubeSchema = request.cubeDescData;
MessageBox.successNotify('Created the cube successfully.');
$location.path("/models");
//location.reload();
} else {
$scope.saveCubeRollBack();
$scope.cubeMetaFrame.project = $scope.state.project;
var message = request.message;
var msg = !!(message) ? message : 'Failed to take action.';
MessageService.sendMsg($scope.cubeResultTmpl({
'text': msg,
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
}
//end loading
loadingRequest.hide();
}, function (e) {
$scope.saveCubeRollBack();
if (e.data && e.data.exception) {
var message = e.data.exception;
var msg = !!(message) ? message : 'Failed to take action.';
MessageService.sendMsg($scope.cubeResultTmpl({
'text': msg,
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
} else {
MessageService.sendMsg($scope.cubeResultTmpl({
'text': "Failed to take action.",
'schema': $scope.state.cubeSchema
}), 'error', {}, true, 'top_center');
}
//end loading
loadingRequest.hide();
});
}
}
else {
$scope.saveCubeRollBack();
}
});
};
// reverse the date
$scope.saveCubeRollBack = function () {
}
$scope.updateMandatory = function (rowkey_column) {
if (!rowkey_column.mandatory) {
angular.forEach($scope.cubeMetaFrame.aggregation_groups, function (group, index) {
var index = group.indexOf(rowkey_column.column);
if (index > -1) {
group.splice(index, 1);
}
});
}
}
function reGenerateRowKey() {
//var fk_pk = {};
var tmpRowKeyColumns = [];
var tmpAggregationItems = [];//put all aggregation item
//var hierarchyItemArray = [];//put all hierarchy items
var pfkMap = {};
for( var i=0;i<$scope.metaModel.model.lookups.length;i++){
var lookup = $scope.metaModel.model.lookups[i];
var table = lookup.alias;
pfkMap[table] = {};
for(var j=0;j<lookup.join.primary_key.length;j++){
var pk = lookup.join.primary_key[j];
pfkMap[table][pk] = lookup.join.foreign_key[j];
}
}
angular.forEach($scope.cubeMetaFrame.dimensions, function (dimension, index) {
//derived column
if (dimension.derived && dimension.derived.length) {
var lookup = _.find($scope.metaModel.model.lookups, function (lookup) {
return lookup.alias == dimension.table
});
angular.forEach(lookup.join.foreign_key, function (fk, index) {
for (var i = 0; i < tmpRowKeyColumns.length; i++) {
if (tmpRowKeyColumns[i].column == fk)
break;
}
// push to array if no duplicate value
if (i == tmpRowKeyColumns.length) {
tmpRowKeyColumns.push({
"column": fk,
"encoding": "dict",
"isShardBy": "false"
});
tmpAggregationItems.push(fk);
}
})
}
//normal column
else if (dimension.column && !dimension.derived) {
var tableName = dimension.table;
var columnName = dimension.column;
var rowkeyColumn = dimension.table+"."+dimension.column;
if(pfkMap[tableName]&&pfkMap[tableName][columnName]){
//lookup table primary key column as dimension
rowkeyColumn = pfkMap[tableName][columnName];
}
for (var i = 0; i < tmpRowKeyColumns.length; i++) {
if (tmpRowKeyColumns[i].column == rowkeyColumn)
break;
}
if (i == tmpRowKeyColumns.length) {
tmpRowKeyColumns.push({
"column": rowkeyColumn,
"encoding": "dict",
"isShardBy": "false"
});
tmpAggregationItems.push(rowkeyColumn);
}
}
});
var rowkeyColumns = $scope.cubeMetaFrame.rowkey.rowkey_columns;
var newRowKeyColumns = sortSharedData(rowkeyColumns, tmpRowKeyColumns);
var increasedColumns = increasedColumn(rowkeyColumns, tmpRowKeyColumns);
newRowKeyColumns = newRowKeyColumns.concat(increasedColumns);
//! here get the latest rowkey_columns
$scope.cubeMetaFrame.rowkey.rowkey_columns = newRowKeyColumns;
if ($scope.cubeMode === "editExistCube") {
//clear dims will not be used
var aggregationGroups = $scope.cubeMetaFrame.aggregation_groups;
rmDeprecatedDims(aggregationGroups,tmpAggregationItems);
}
if ($scope.cubeMode === "addNewCube") {
//clear dims will not be used
if($scope.cubeMetaFrame.aggregation_groups.length){
var aggregationGroups = $scope.cubeMetaFrame.aggregation_groups;
rmDeprecatedDims(aggregationGroups,tmpAggregationItems);
return;
}
if (!tmpAggregationItems.length) {
$scope.cubeMetaFrame.aggregation_groups = [];
return;
}
var newUniqAggregationItem = [];
angular.forEach(tmpAggregationItems, function (item, index) {
if (newUniqAggregationItem.indexOf(item) == -1) {
newUniqAggregationItem.push(item);
}
});
$scope.cubeMetaFrame.aggregation_groups = [];
var initJointGroups = sliceGroupItemToGroups(newUniqAggregationItem);
var newGroup = CubeDescModel.createAggGroup();
newGroup.includes = newUniqAggregationItem;
for(var i=1;i<initJointGroups.length;i++){
if(initJointGroups[i].length>1){
newGroup.select_rule.joint_dims[i-1] = initJointGroups[i];
}
}
$scope.cubeMetaFrame.aggregation_groups.push(newGroup);
}
}
function rmDeprecatedDims(aggregationGroups,tmpAggregationItems){
angular.forEach(aggregationGroups, function (group, index) {
if (group) {
for (var j = 0; j < group.includes.length; j++) {
var elemStillExist = false;
for (var k = 0; k < tmpAggregationItems.length; k++) {
if (group.includes[j].toUpperCase() == tmpAggregationItems[k].toUpperCase()) {
elemStillExist = true;
break;
}
}
if (!elemStillExist) {
var deprecatedItem = group.includes[j];
//rm deprecated dimension from include
group.includes.splice(j, 1);
j--;
//rm deprecated dimension in mandatory dimensions
var mandatory = group.select_rule.mandatory_dims;
if(mandatory && mandatory.length){
var columnIndex = mandatory.indexOf(deprecatedItem);
if(columnIndex>=0){
group.select_rule.mandatory_dims.splice(columnIndex,1);
}
}
var hierarchys = group.select_rule.hierarchy_dims;
if(hierarchys && hierarchys.length){
for(var i=0;i<hierarchys.length;i++){
var hierarchysIndex = hierarchys[i].indexOf(deprecatedItem);
if(hierarchysIndex>=0) {
group.select_rule.hierarchy_dims[i].splice(hierarchysIndex, 1);
}
}
}
var joints = group.select_rule.joint_dims;
if(joints && joints.length){
for(var i=0;i<joints.length;i++){
var jointIndex = joints[i].indexOf(deprecatedItem);
if(jointIndex>=0) {
group.select_rule.joint_dims[i].splice(jointIndex, 1);
}
}
}
}
}
}
else {
aggregationGroups.splice(index, 1);
index--;
}
});
}
function sortSharedData(oldArray, tmpArr) {
var newArr = [];
for (var j = 0; j < oldArray.length; j++) {
var unit = oldArray[j];
for (var k = 0; k < tmpArr.length; k++) {
if (unit.column == tmpArr[k].column) {
newArr.push(unit);
}
}
}
return newArr;
}
function increasedData(oldArray, tmpArr) {
var increasedData = [];
if (oldArray && !oldArray.length) {
return increasedData.concat(tmpArr);
}
for (var j = 0; j < tmpArr.length; j++) {
var unit = tmpArr[j];
var exist = false;
for (var k = 0; k < oldArray.length; k++) {
if (unit == oldArray[k]) {
exist = true;
break;
}
}
if (!exist) {
increasedData.push(unit);
}
}
return increasedData;
}
function increasedColumn(oldArray, tmpArr) {
var increasedData = [];
if (oldArray && !oldArray.length) {
return increasedData.concat(tmpArr);
}
for (var j = 0; j < tmpArr.length; j++) {
var unit = tmpArr[j];
var exist = false;
for (var k = 0; k < oldArray.length; k++) {
if (unit.column == oldArray[k].column) {
exist = true;
break;
}
}
if (!exist) {
increasedData.push(unit);
}
}
return increasedData;
}
function sliceGroupItemToGroups(groupItems) {
if (!groupItems.length) {
return [];
}
var groups = [];
var j = -1;
for (var i = 0; i < groupItems.length; i++) {
if (i % 11 == 0) {
j++;
groups[j] = [];
}
groups[j].push(groupItems[i]);
}
return groups;
}
$scope.$watch('projectModel.selectedProject', function (newValue, oldValue) {
if(!$scope.projectModel.getSelectedProject()) {
return;
}
var param = {
ext: true,
project: newValue
};
if (newValue) {
TableModel.initTables();
TableModel.getcolumnNameTypeMap();
TableService.list(param, function (tables) {
angular.forEach(tables, function (table) {
table.name = table.database + "." + table.name;
TableModel.addTable(table);
});
});
}
});
$scope.$watch('cubeMetaFrame.model_name', function (newValue, oldValue) {
if (!newValue) {
return;
}
$scope.metaModel.model = modelsManager.getModel(newValue);
if($scope.metaModel.model){
$scope.modelsManager.initAliasMapByModelSchema($scope.metaModel);
//if(oldValue){
// $scope.cubeMetaFrame=CubeDescModel.createNew({
// model_name:newValue
// })
//}
}
if(!$scope.metaModel.model){
return;
}
});
$scope.removeNotificationEvents = function(){
if($scope.cubeMetaFrame.status_need_notify.indexOf('ERROR') == -1){
$scope.cubeMetaFrame.status_need_notify.unshift('ERROR');
}
}
function reGenerateAdvancedDict () {
if (!$scope.cubeMetaFrame.dictionaries) {
$scope.cubeMetaFrame.dictionaries = [];
}
var distinctMeasures = [];
angular.forEach($scope.cubeMetaFrame.measures, function (measure, index) {
if (measure.function.expression === 'COUNT_DISTINCT' && measure.function.returntype === 'bitmap' && !$scope.isIntMeasure(measure)) {
var measureColumn = measure.function.parameter.value;
distinctMeasures.push(measureColumn);
//keep backward compatibility
distinctMeasures.push(VdmUtil.removeNameSpace(measureColumn))
var isColumnExit = false;
angular.forEach($scope.cubeMetaFrame.dictionaries, function (dictionaries) {
if (!isColumnExit) {
//keep backward compatibility
if (dictionaries.column == measureColumn || dictionaries.column == VdmUtil.removeNameSpace(measureColumn))
isColumnExit = true;
}
});
if (!isColumnExit) {
var dict = CubeDescModel.createDictionaries();
dict.column = measureColumn;
dict.builder = cubeConfig.buildDictionaries[0].value;
$scope.cubeMetaFrame.dictionaries.push(dict)
}
}
});
//get all reuse columns
var reuseColumns = [];
angular.forEach($scope.cubeMetaFrame.dictionaries, function (dict, index) {
if (dict.reuse != null && reuseColumns.indexOf(dict.reuse) === -1) {
reuseColumns.push(dict.reuse);
}
});
//remove deprecated distinct measures
for (var i = $scope.cubeMetaFrame.dictionaries.length - 1; i >= 0; i--) {
var dictColumn = $scope.cubeMetaFrame.dictionaries[i].column;
if (distinctMeasures.indexOf(dictColumn) === -1 && reuseColumns.indexOf(dictColumn) === -1) {
$scope.cubeMetaFrame.dictionaries.splice(i, 1);
}
}
}
$scope.$on('MeasuresEdited', function (event) {
if ($scope.cubeMetaFrame) {
reGenerateAdvancedDict();
}
});
$scope.$on('AdvancedSettingEdited', function (event) {
if ($scope.cubeMetaFrame) {
reGenerateAdvancedDict();
}
});
$scope.$on('DimensionsEdited', function (event) {
if ($scope.cubeMetaFrame) {
reGenerateRowKey();
}
});
});