Saiku嵌入系统使用时传参数访问saiku
Saiku通过iframe嵌入其他系统使用时,我们可以设定参数信息,然后根据url中参数对结果进行筛选哦。
这里我们实现的是根据日期字段进行范围查询,URL请求中需要多加如下两个参数 startdate , enddate ,也就是开始日期与结束日期。
实现日期的范围查询 saiku date range,主要有以下步骤
1. 修改源代码 saiku-ui 项目下的 js\saiku\models\Query.js(如果你不是本地编译的源码也可以直接修改 saiku-server\tomcat\webapps\ROOT\js\saiku\models 目录下的 Query.js )
在Query.js下的 run方法中增加如下代码段:
//根據用戶輸入的開始日期與結束日期查詢範圍數據 var dimensionArr = exModel.queryModel.axes.ROWS.hierarchies; //取出行信息中的所有维度信息 dimension,用一个数组接收 for(var i=0;i<dimensionArr.length;i++){ //判断维度信息中是否有countdate这个时间维度(这是固定的) if(dimensionArr[i]!=null && dimensionArr[i].dimension == "countdate" ){ var paramsURI = Saiku.URLParams.paramsURI(); //得到URl中传过来的参数信息 //判断参数是否为空 if(paramsURI.startdate != null && paramsURI.startdate != undefined && paramsURI.startdate != ""&& paramsURI.enddate != null && paramsURI.enddate != undefined && paramsURI.enddate != ""){ var startdate=paramsURI.startdate; //获取开始日期 var enddate=paramsURI.enddate; //获取结束日期 //更改level下的mdx表达式,将日期范围的筛选信息添加到mdx表达式中 dimensionArr[i].levels.countdate.mdx="[countdate].[countdate].[countdate].["+startdate+"]:[countdate].[countdate].[countdate].["+enddate+"]"; } } }
这里最主要的是就是找到需要改动的位置了:(文末提供了修改后完整的Query.js文件)
从 exModel.queryModel.axes.ROWS.hierarchies 这个数组中找到countdate ,然后再修改countdate对应 levels.coundate下的mdx值。
层级关系如下图:(0是可变的哟,可能是下标1,2,3..... 所以上面代码中用到了 for循环去找 countdate )
2. saiku中xml文件中的cube中必须配置日期字段,我用的是countdate字段,配置如下
<Dimension name="countdate" foreignKey="ID" > <Hierarchy hasAll="true" primaryKey="ID" allMemberName="countdate" > <Level name="countdate" table="aaa" column="countdate" type='Date' uniqueMembers="false" /> <!--这里的 column=countdate ,countdate为数据库的表中的日期字段名,可根据自己的表灵活变动啦--> </Hierarchy> </Dimension>
3.Saiku配置数据信息的时候需要将日期字段拖到 行 数据框
4.使用url发请求访问saiku中需带入startdate,enddate参数 (如果不带入这两个参数的话就不会进行范围筛选啦!!!然后就会正常查询全量数据)
模板URL如下:
http://10.99.77.78:8080/?username=admin&password=aaa&startdate=2019-03-06%2000:00:00.0&enddate=2019-03-10%2000:00:00.0&plugin=false&mode=view#query/open//KPI/aa.saiku
URL解析
http://10.99.77.78:8080/ #是我的saiku的访问地址
username=admin&password=aaa #分别是我登录saiku的用户名以及密码
startdate=2019-03-06%2000:00:00.0&enddate=2019-03-10%2000:00:00.0 #是我设定的开始时间以及结束时间,我想查询countdate值为3.6到3.10的数据。这里需要说明一下我的countdate中有2019-03-06 00:00:00.0 ,2019-03-10 00:00:00.0 这两个值哦。
plugin=false&mode=view#query/open/ #这里指不以插件的方式嵌入,嵌入的模式为view(会带出导出excel那一排工具栏信息)
/KPI/aa.saiku #这个就是我保存的数据信息啦,我自己新建了个KPI目录,然后保存的数据命名为 aa .
5.过滤结果如下图:
解析:( 其实上面添加的注释已经比较详细的,嘻嘻 ,需要注意的主要有以下4点)
1. 数据库中必须有一个countdate日期字段(名字当然可以随意)
2.Schema文件中 对应的cube信息中必须配置 countdate字段如下:(关键的是 Dimension下的name Hierarchy下的allMemerName Level下的name 命名都为countdate )
<Dimension name="countdate" foreignKey="ID" > <Hierarchy hasAll="true" primaryKey="ID" allMemberName="countdate" > <Level name="countdate" table="aaa" column="countdate" type='Date' uniqueMembers="false" /> <!--这里的 column=countdate ,countdate为数据库的表中的日期字段名,可根据自己的表更改--> </Hierarchy> </Dimension>
3.如果需要根据日期进行范围筛选,url中必须添加参数 startdate,enddate 哦
4.想要获取正确的筛选结果,startdate以及enddate的值必须是当前cube中已有的值!!!(不然就会报错啦,就像你在saiku中使用参数一样,参数值一定要填写当前结果中已有的值,关于日期就很较真啦,时分秒都要对应上才行哦)
>>>更改后的Query.js文件
这里再提供一下修改之后的完整Query.js ,其它地方都没有改动,就只是添加了上方内容,详情如下
Query.js
/* * Copyright 2012 OSBI Ltd * * Licensed 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. */ /** * Workspace query */ var Query = Backbone.Model.extend({ formatter: Settings.CELLSET_FORMATTER, properties: null, initialize: function(args, options) { // Save cube _.extend(this, options); // Bind `this` _.bindAll(this, "run"); // Generate a unique query id this.uuid = 'xxxxxxxx-xxxx-xxxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }).toUpperCase(); this.model = _.extend({ name: this.uuid }, SaikuOlapQueryTemplate); if (args.cube) { this.model.cube = args.cube; } this.helper = new SaikuOlapQueryHelper(this); // Initialize properties, action handler, and result handler this.action = new QueryAction({}, { query: this }); this.result = new Result({ limit: Settings.RESULT_LIMIT }, { query: this }); this.scenario = new QueryScenario({}, { query: this }); // A flag to tell who changed selection members this.updatedSelectionFromModal = false; }, parse: function(response) { // Assign id so Backbone knows to PUT instead of POST this.id = this.uuid; if (response.name) { this.id = response.name; this.uuid = response.name; } this.model = _.extend(this.model, response); this.model.properties = _.extend({}, Settings.QUERY_PROPERTIES, this.model.properties); }, setProperty: function(key, value) { this.model.properties[key] = value; }, getProperty: function(key) { return this.model.properties[key]; }, syncSelectionsModalAndUpdateParameters: function() { if (this.updatedSelectionFromModal) { var mParameters = this.helper.model().parameters; for (var mKey in mParameters) { var mVal = mParameters[mKey]; var selections = this.helper.getSelectionsForParameter(mKey); mVal = selections.map(function(sel) { return sel.caption; }).join(); mParameters[mKey] = mVal; } } else { var mParameters = this.helper.model().parameters; for (var mKey in mParameters) { var mVal = mParameters[mKey]; var mLevel = this.helper.getLevelForParameter(mKey); var selections = this.helper.getSelectionsForParameter(mKey); if (mVal !== null && mVal !== undefined) { this.helper.setSelectionsForParameter(mKey, _.filter(selections, function(sel) { var containsParam = false; _.each(mVal.split(','), function (v) { if (sel.caption === v) { containsParam = true; return false; } }); return containsParam; })); } } } this.updatedSelectionFromModal = false; }, run: function(force, mdx) { this.syncSelectionsModalAndUpdateParameters(); var self = this; // Check for automatic execution Saiku.ui.unblock(); if (typeof this.model.properties != "undefined" && this.model.properties['saiku.olap.query.automatic_execution'] === false && (force === false || force === undefined || force === null)) { return; } this.workspace.unblock(); $(this.workspace.el).find(".workspace_results_info").empty(); this.workspace.trigger('query:run'); this.result.result = null; var validated = false; var errorMessage = '<span class="i18n">Query Validation failed!</span>'; var exModel = this.helper.model(); for(var k in this.attributes) { var att = this.attributes[k]; if(k.substring(0,5)==="PARAM"){ var p = k.substring(5, k.length); exModel.parameters[p] = att; } } if (exModel.queryType == "OLAP") { if (exModel.type == "QUERYMODEL") { var columnsOk = Object.keys(exModel.queryModel.axes.COLUMNS.hierarchies).length > 0; var rowsOk = Object.keys(exModel.queryModel.axes.ROWS.hierarchies).length > 0; var detailsOk = exModel.queryModel.details.axis == 'COLUMNS' && exModel.queryModel.details.measures.length > 0; if (!rowsOk || !columnsOk || !detailsOk) { errorMessage = ""; } if (!columnsOk && !detailsOk) { errorMessage += '<span class="i18n">You need to include at least one measure or a level on columns for a valid query.</span>'; } if(!rowsOk) { errorMessage += '<span class="i18n">You need to include at least one level on rows for a valid query.</span>'; } if ( (columnsOk || detailsOk) && rowsOk) { validated = true; } } else if (exModel.type == "MDX") { validated = (exModel.mdx && exModel.mdx.length > 0); if (!validated) { errorMessage = '<span class="i18n">You need to enter some MDX statement to execute.</span>'; } } } if (!validated) { this.workspace.table.clearOut(); $(this.workspace.processing).html(errorMessage).show(); this.workspace.adjust(); Saiku.i18n.translate(); return; } // Run it this.workspace.table.clearOut(); $(this.workspace.processing).html('<span class="processing_image"> </span> <span class="i18n">Running query...</span> [ <a class="cancel i18n" href="#cancel">Cancel</a> ]').show(); this.workspace.adjust(); this.workspace.trigger('query:fetch'); Saiku.i18n.translate(); var message = '<span class="processing_image"> </span> <span class="i18n">Running query...</span> [ <a class="cancel i18n" href="#cancel">Cancel</a> ]'; this.workspace.block(message); /* TODO: i wonder if we should clean up the model (name and captions etc.) delete this.model.queryModel.axes['FILTER'].name; */ //根據用戶輸入的開始日期與結束日期查詢範圍數據 var dimensionArr = exModel.queryModel.axes.ROWS.hierarchies; //取出行信息中的所有维度信息 dimension,用一个数组接收 for(var i=0;i<dimensionArr.length;i++){ //判断维度信息中是否有countdate这个时间维度(这是固定的) if(dimensionArr[i]!=null && dimensionArr[i].dimension == "countdate" ){ var paramsURI = Saiku.URLParams.paramsURI(); //get the param from url //判断参数是否为空 if(paramsURI.startdate != null && paramsURI.startdate != undefined && paramsURI.startdate != ""&& paramsURI.enddate != null && paramsURI.enddate != undefined && paramsURI.enddate != ""){ var startdate=paramsURI.startdate; var enddate=paramsURI.enddate; //更改level下的mdx表达式 dimensionArr[i].levels.countdate.mdx="[countdate].[countdate].[countdate].["+startdate+"]:[countdate].[countdate].[countdate].["+enddate+"]"; } } } this.result.save({},{ contentType: "application/json", data: JSON.stringify(exModel), error: function() { Saiku.ui.unblock(); var errorMessage = '<span class="i18n">Error executing query. Please check the server logs or contact your administrator!</span>'; self.workspace.table.clearOut(); $(self.workspace.processing).html(errorMessage).show(); self.workspace.adjust(); Saiku.i18n.translate(); } }); }, enrich: function() { var self = this; this.workspace.query.action.post("/../enrich", { contentType: "application/json", data: JSON.stringify(self.model), async: false, success: function(response, model) { self.model = model; } }); }, url: function() { return "api/query/" + encodeURI(this.uuid); } });