Skip to content

Commit de46551

Browse files
better MDX parser, code optimizations, refactoring
1 parent f021005 commit de46551

File tree

7 files changed

+101
-100
lines changed

7 files changed

+101
-100
lines changed

gulpfile.js

+12-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,17 @@ var gulp = require("gulp"),
66
wrap = require("gulp-wrap"),
77
minifyCSS = require("gulp-minify-css"),
88
htmlReplace = require("gulp-html-replace"),
9-
replace = require('gulp-replace');
9+
header = require("gulp-header"),
10+
replace = require("gulp-replace"),
11+
pkg = require("./package.json");
12+
13+
var banner = [
14+
"/** <%= pkg.name %>: <%= pkg.description %>",
15+
" ** @author <%= pkg.author %>",
16+
" ** @version <%= pkg.version %>",
17+
" **/",
18+
""
19+
].join("\n");
1020

1121
gulp.task("clean", function () {
1222
return gulp.src("build", {read: false})
@@ -22,6 +32,7 @@ gulp.task("gatherScripts", ["clean"], function () {
2232
ascii_only: true
2333
}
2434
}))
35+
.pipe(header(banner, { pkg: pkg }))
2536
.pipe(gulp.dest("build/js/"));
2637
});
2738

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
{
22
"name": "LightPivotTable",
33
"author": "ZitRo",
4-
"version": "0.5.2",
5-
"description": "Light pivot table for MDX2JSON source for InterSystems Cache",
4+
"version": "0.6.0",
5+
"description": "A lightweight pivot table for MDX2JSON source for InterSystems Cache",
66
"main": "test/testServer.js",
77
"directories": {
88
"test": "test"

source/js/DataController.js

-2
Original file line numberDiff line numberDiff line change
@@ -282,8 +282,6 @@ DataController.prototype.sortByColumn = function (columnIndex) {
282282
.concat(newRawData)
283283
.concat(this.SUMMARY_SHOWN ? [data._rawDataOrigin[data._rawDataOrigin.length - 1]] : []);
284284

285-
console.log(data.rawData);
286-
287285
this._trigger();
288286

289287
};

source/js/DataSource.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ DataSource.prototype.getCurrentData = function (callback) {
109109
mdx = mdxParser.applyFilter(mdx, this.FILTERS[i]);
110110
}
111111

112-
console.log("MDX: " + mdx);
112+
console.log("Request MDX: " + mdx);
113113

114114
this._post(this.SOURCE_URL + "/" + this.ACTION, {
115115
MDX: mdx

source/js/LightPivotTable.js

+28-15
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
/**
2+
* Light pivot table global object.
3+
*
4+
* @param {object} configuration
5+
* @constructor
6+
*/
17
var LightPivotTable = function (configuration) {
28

39
var _ = this;
@@ -16,7 +22,7 @@ var LightPivotTable = function (configuration) {
1622
* @type {DataController}
1723
*/
1824
this.dataController = new DataController(this, function () {
19-
_.dataChangeTrigger.call(_);
25+
_.dataIsChanged.call(_);
2026
});
2127

2228
this.init();
@@ -70,6 +76,10 @@ LightPivotTable.prototype.clearFilters = function () {
7076

7177
};
7278

79+
/**
80+
* @param {object} config - part of dataSource configuration. Usually a part of config given to LPT.
81+
* @returns {DataSource}
82+
*/
7383
LightPivotTable.prototype.pushDataSource = function (config) {
7484

7585
var newDataSource;
@@ -92,12 +102,20 @@ LightPivotTable.prototype.popDataSource = function () {
92102

93103
};
94104

95-
LightPivotTable.prototype.dataChangeTrigger = function () {
105+
/**
106+
* Data change handler.
107+
*/
108+
LightPivotTable.prototype.dataIsChanged = function () {
96109

97110
this.pivotView.renderRawData(this.dataController.getData().rawData);
98111

99112
};
100113

114+
/**
115+
* Try to DrillDown with given filter.
116+
*
117+
* @param {string} filter
118+
*/
101119
LightPivotTable.prototype.tryDrillDown = function (filter) {
102120

103121
var _ = this,
@@ -108,8 +126,8 @@ LightPivotTable.prototype.tryDrillDown = function (filter) {
108126
for (var i in _.CONFIG.dataSource) { ds[i] = _.CONFIG.dataSource[i]; }
109127

110128
if (this.CONFIG.DrillDownExpression && this._dataSourcesStack.length < 2) {
111-
ds.basicMDX = this.mdxParser.customDrillDown(
112-
this.dataSource.BASIC_MDX, this.CONFIG.DrillDownExpression, filter
129+
ds.basicMDX = this.mdxParser.drillDown(
130+
this.dataSource.BASIC_MDX, filter, this.CONFIG.DrillDownExpression
113131
) || this.dataSource.BASIC_MDX;
114132
} else {
115133
ds.basicMDX = this.mdxParser.drillDown(this.dataSource.BASIC_MDX, filter) || this.dataSource.BASIC_MDX;
@@ -134,9 +152,11 @@ LightPivotTable.prototype.tryDrillDown = function (filter) {
134152
};
135153

136154
/**
155+
* Try to DrillThrough with given filters.
156+
*
137157
* @param {string[]} [filters]
138158
*/
139-
LightPivotTable.prototype.showDrillThrough = function (filters) {
159+
LightPivotTable.prototype.tryDrillThrough = function (filters) {
140160

141161
var _ = this,
142162
oldDataSource,
@@ -145,19 +165,12 @@ LightPivotTable.prototype.showDrillThrough = function (filters) {
145165
// clone dataSource config object
146166
for (var i in _.CONFIG.dataSource) { ds[i] = _.CONFIG.dataSource[i]; }
147167
ds.action = "MDXDrillthrough";
148-
if (filters instanceof Array) {
149-
console.log("BASIC MDX: " + this.dataSource.BASIC_MDX, "\n\nFILTERS: " + filters);
150-
ds.basicMDX = this.mdxParser.customDrillThrough(this.dataSource.BASIC_MDX, filters)
151-
|| this.dataSource.basicMDX;
152-
} else {
153-
ds.basicMDX = this.dataSource.BASIC_MDX;
154-
ds.basicMDX = this.mdxParser.drillThrough(ds.basicMDX) || ds.basicMDX;
155-
}
156168

157-
oldDataSource = this.dataSource;
169+
ds.basicMDX = this.mdxParser.drillThrough(this.dataSource.BASIC_MDX, filters)
170+
|| this.dataSource.basicMDX;
158171

172+
oldDataSource = this.dataSource;
159173
this.pushDataSource(ds);
160-
161174
this.dataSource.FILTERS = oldDataSource.FILTERS;
162175

163176
this.dataSource.getCurrentData(function (data) {

source/js/MDXParser.js

+52-74
Original file line numberDiff line numberDiff line change
@@ -5,114 +5,92 @@ var MDXParser = function () {
55
};
66

77
/**
8-
* Performs DrillDown on MDX query.
8+
* Debug method.
99
*
10-
* @param {string} basicMDX
11-
* @param {string} filter
12-
* @returns {string} - new query.
10+
* @param {string} mdx
11+
* @param {string} [message]
12+
* @private
1313
*/
14-
MDXParser.prototype.drillDown = function (basicMDX, filter) {
15-
16-
try {
17-
18-
var filterParts = filter.split(/(\(?)([^\)]*)(\)?)/),
19-
clearFilter = filterParts[2],
20-
parts = basicMDX.split(/(\s+ON\s+0,\s*)(.*)(\s+ON\s+1\s*)/i),
21-
oldPath = parts[2].split(/(\(?)(\[[^\(^\)]*)(\)?)/);
22-
23-
oldPath[2] = clearFilter + ".children";
24-
parts[2] = oldPath.join("");
25-
26-
//console.log("\n\nIN: "+basicMDX+"\n\nFILTER: " + filter + "\n\nCUSTOM: "+ parts.join("")
27-
// + " %FILTER " + filterParts.join(""));
28-
29-
return parts.join("") + " %FILTER " + filterParts.join("");
30-
31-
} catch (e) {
32-
33-
console.error("Unable to get DrillDown statement from", basicMDX, "with filter", filter);
34-
return "";
35-
36-
}
37-
14+
MDXParser.prototype._warnMDX = function (mdx, message) {
15+
console.warn("MDX is not parsed:\n\n%s\n\n" + (message ? "(" + message + ")" : ""), mdx);
3816
};
3917

4018
/**
41-
* Replace dimension [1] with expression.
19+
* Converts filter to setExpression that can be inserted to MDX.
4220
*
43-
* @param {string} basicMDX
44-
* @param {string} expression
45-
* @param {string} [filter]
46-
* @returns {string}
21+
* @param filterSpec
4722
*/
48-
MDXParser.prototype.customDrillDown = function (basicMDX, expression, filter) {
49-
50-
try {
51-
52-
var parts = basicMDX.split(/(\s+ON\s+0,\s*)(.*)(\s+ON\s+1\s*)/i);
53-
54-
parts[2] = expression;
55-
56-
if (filter) parts.push(" %FILTER " + filter);
57-
58-
//console.log("\n\nIN: "+basicMDX+"\n\nEXPR: " + expression + "\n\nFILTER: "
59-
// + filter + "\n\nCUSTOM: " + parts.join(""));
60-
61-
return parts.join("");
62-
63-
} catch (e) {
64-
65-
console.error("Unable to get DrillDown statement from", basicMDX, "by", expression,
66-
"with filter", filter);
67-
return "";
68-
23+
MDXParser.prototype.makeSetExpressionFromFilter = function (filterSpec) {
24+
if (filterSpec.match(/^\([^\),]*,[^\)]*\)$/)) {
25+
return "NONEMPTYCROSSJOIN" + filterSpec.slice(0, filterSpec.length - 1) + ".children)";
26+
} else {
27+
return filterSpec + ".children";
6928
}
70-
7129
};
7230

7331
/**
74-
* Returns DrillThrough query for given MDX query.
32+
* Performs DrillDown on MDX query.
7533
*
76-
* @param {string} basicMDX
77-
* @returns {string}
34+
* @param {string} mdx
35+
* @param {string} filter
36+
* @param {string} [expression] - if is set, "* ON 1" will be replaced with "{value} ON 1"
37+
* @returns {string} - new query.
7838
*/
79-
MDXParser.prototype.drillThrough = function (basicMDX) {
39+
MDXParser.prototype.drillDown = function (mdx, filter, expression) {
8040

81-
try {
41+
if (!filter) {
42+
this._warnMDX(mdx, "no filter specified");
43+
return "";
44+
}
8245

83-
var statement = ["DRILLTHROUGH SELECT "]
84-
.concat(basicMDX.split(/(\s+ON\s+0,\s*)(.*)(\s+ON\s+1\s*)/i).slice(2)).join("");
46+
var parts = mdx.split(/(select)(.*?)(from)/ig); // split by SELECT queries
8547

86-
console.log("DRILLTHROUGH STATEMENT:", statement);
48+
if (parts.length < 4) {
49+
this._warnMDX(mdx);
50+
return ""; // no select query matched
51+
}
8752

88-
return statement === "DRILLTHROUGH SELECT " ? "" : statement;
53+
var selectBody = parts[parts.length - 3],
54+
dimensions = selectBody.split(/(\s*ON\s*[01]\s*,?\s*)/);
8955

90-
} catch (e) {
56+
if (dimensions.length < 2) {
57+
this._warnMDX(mdx);
58+
return ""; // no dimensions matched
59+
}
9160

92-
console.error("Unable to get DrillThrough statement from", basicMDX);
93-
return "";
61+
var index = -1;
62+
dimensions.map(function(e,i){if(e.match(/\s*ON\s*[01]\s*,?\s*/)) index=i-1; return e;});
9463

64+
if (index === -1) {
65+
this._warnMDX(mdx, "DrillDown is impossible");
66+
return ""; // DrillDown is impossible (no "1" dimension)
9567
}
9668

69+
dimensions[index] = expression || this.makeSetExpressionFromFilter(filter);
70+
for (var i in dimensions) {
71+
if (dimensions[i].length === 1) { // "0" || "1"
72+
dimensions[i](parseInt(i), 1);
73+
}
74+
}
75+
parts[parts.length - 3] = dimensions.join("");
76+
77+
return this.applyFilter(parts.join(""), filter);
78+
9779
};
9880

9981
/**
10082
* @param {string} basicMDX
101-
* @param {string[]} filters
83+
* @param {string[]} [filters]
10284
*/
103-
MDXParser.prototype.customDrillThrough = function (basicMDX, filters) {
85+
MDXParser.prototype.drillThrough = function (basicMDX, filters) {
10486

10587
var cubeAndFilters = basicMDX.split(/(FROM\s*\[[^\]]*].*)/i)[1],
10688
query = "DRILLTHROUGH SELECT " + cubeAndFilters;
10789

108-
if (!(filters instanceof Array)) filters = [filters];
109-
11090
for (var i in filters) {
111-
query += " %FILTER " + filters[i];
91+
query = this.applyFilter(query, filters[i]);
11292
}
11393

114-
console.log("CUSTOM DRILLTHROUGH STATEMENT: " + query);
115-
11694
return query;
11795

11896
};

source/js/PivotView.js

+6-5
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ PivotView.prototype._backClickHandler = function (event) {
154154

155155
PivotView.prototype._drillThroughClickHandler = function (event) {
156156

157-
this.controller.showDrillThrough();
157+
this.controller.tryDrillThrough();
158158

159159
event.cancelBubble = true;
160160
event.stopPropagation();
@@ -164,7 +164,7 @@ PivotView.prototype._drillThroughClickHandler = function (event) {
164164
PivotView.prototype._cellClickHandler = function (x, y) {
165165

166166
var data = this.controller.dataController.getData(),
167-
f1, f2;
167+
f = [], f1, f2;
168168

169169
try {
170170
f1 = data.rawData[y][data.info.leftHeaderColumnsNumber - 1].source.path;
@@ -173,14 +173,15 @@ PivotView.prototype._cellClickHandler = function (x, y) {
173173
console.warn("Unable to get filters for cell (%d, %d)", x, y);
174174
}
175175

176-
if (!f1) return;
176+
if (f1) f.push(f1);
177+
if (f2) f.push(f2);
177178

178179
if (this.controller.CONFIG["drillDownTarget"]) {
179180
window.location = location.origin + location.pathname + "?DASHBOARD="
180181
+ encodeURIComponent(this.controller.CONFIG["drillDownTarget"]) + "&SETTINGS=FILTER:"
181-
+ encodeURIComponent(f1 + "~" + f2) + ";";
182+
+ encodeURIComponent(f.join("~")) + ";";
182183
} else {
183-
this.controller.showDrillThrough(f2 ? [f1, f2] : [f1]);
184+
this.controller.tryDrillThrough(f);
184185
}
185186

186187
};

0 commit comments

Comments
 (0)