Commit 32887cce authored by Boris Kocherov's avatar Boris Kocherov

add CUBEMEMBER, CUBEVALUE

CUBEMEMBER and CUBEVALUE functions has been implemented, using xmla
library.

For this purpose the support of lazy calculations in parserFormula has
been added. Now, during the implementantion of a function a method for
lazy calculations  (CalculateLazy) can be defined. CalculateLazy returns
not a value, as it did Calculation, but RSVP.Queue. And now it is the
returned queue that is to return the value which is the result of the
calculations. During the calculation of CUBEVALUE the calculation
process waits until all the calculations of all the arguments are
finished. It is made to send an EXECUTE request to olap. This request
guarantees the consistency of the returned values. In case of success
CUBEMEMBER.CalculateLazy assigns mdx-expression to cube_value to form
the request and calculate its result. Functions are marked as those
which are calculated every time the document is being opened (.ca=true).
changeOffsetElem, which return the element of the olap-tree, adjacent to
the current one on the same level, have been implemented. It helps to
fill the table.
TODO:
Make the editor of connections to OLAP-servers. In the current
implementation connection parameters are hardcoded. The current
parameters describe a connection to SAIKU olap demoserver, which
supports XMLA-requests.
Use PivotCache as an intermediate structure to store the requests'
results. It will allow to use PivotTable to display the results.

In Russian:
Реализованы CUBEMEMBER, CUBEVALUE функции используя xmla библиотеку.

Для этого добавлена поддержка ленивых вычислений в parserFormula.При
реализации функции теперь можно опредилить метод для ленивых вычислений
CalculateLazy.  CalculateLazy возвращает очередь (RSVP.Queue), а не
значение, как при обычных вычислениях. И уже возвращаемая очередь должна
вернуть значение, которое является результатом вычисления. При
вычислении CUBEVALUE происходит ожидание вычисления всех аргументов всех
CUBEVALUE. Это сделано, для того чтобы посылать один запрос EXECUTE к
olap, что гарантирует согласованность значений, полученых в результате.
CUBEMEMBER.CalculateLazy в случае успеха присваивает cube_value mdx-
выражение которое использовано для вычисления
CUBEMEMBER в свою очередь CUBEVALUE использует cube_value для
формирования запроса и вычисления своего результата. Функции
промаркированы как вычисляемые при каждом открытии документа
(.ca=true). Реализованы changeOffsetElem, которые возвращают элемент
olap-дерева, соседний по отношению к текущему на том же уровне, что
позволяет быстро заполнять таблицу.
TODO:
Сделать редактор подключений к OLAP серверам. В текущей реализации
параметры соединений прописаны прямо в коде. Текущие параметры описывают
соединение с SAIKU olap demoserver, который поддерживает XMLA.
Использовать PivotCache как промежуточную структуру для хранения
результатов запросов, что позволит использовать PivotTable для
отображения результатов.
parent fbf10a1b
......@@ -273,6 +273,7 @@
"../common/externs/jquery-3.2.js",
"../common/externs/xregexp-3.0.0.js",
"../common/externs/sockjs.js",
"../common/externs/Xmla.js",
"../common/externs/jszip.min.js",
"../common/externs/jszip-utils.js"
],
......
This diff is collapsed.
......@@ -657,6 +657,10 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
this.value = val;
}
cBaseType.prototype.clone = function () {
return new this.constructor(this.value);
};
cBaseType.prototype.cloneTo = function (oRes) {
oRes.numFormat = this.numFormat;
oRes.value = this.value;
......@@ -670,6 +674,110 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseType.prototype.toLocaleString = function () {
return this.toString();
};
cBaseType.prototype.CalculatePromise = function (arg, opt_bbox, isDefName, ws) {
// depromise args
var t = this,
lazy_formulas = [],
lazy_found;
function check_args_promise() {
var promise_flag = false,
element,
length,
i;
function cellForge(cell) {
if (cell && cell.formulaParsed &&
(cell.formulaParsed.lazy_value || cell.formulaParsed.queue)) {
promise_flag = true;
lazy_formulas.push(cell.formulaParsed);
}
}
for (i = 0, length = arg.length; i < length; ++i) {
element = arg[i];
if (typeof element === "function") {
promise_flag = true;
}
if (element instanceof cArea || element instanceof cArea3D ||
element instanceof cRef || element instanceof cRef3D) {
element.getRange()._foreach(cellForge);
}
}
return promise_flag;
}
lazy_found = check_args_promise();
if (t.CalculateLazy) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy run it
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
});
return t.CalculateLazy(queue, opt_bbox, isDefName, ws);
};
} else {
if (lazy_found) {
return function () {
var queue = new RSVP.Queue();
if (lazy_formulas.length > 0) {
lazy_formulas.forEach(function (formula) {
var lazy = formula.lazy_value;
if (lazy) {
// if value lazy add it dependence list
lazy();
}
if (formula.queue) {
// add dependence from already
// running but not computed lazy
queue.push(function () {
return formula.queue;
});
}
});
}
return queue
.push(function () {
return RSVP.all(arg.map(function (z) {
if (typeof z === "function") {
return z();
} else {
return z;
}
}));
})
.push(function (arg) {
return t.Calculate(arg, opt_bbox, isDefName, ws);
});
};
} else {
return t.Calculate(arg, opt_bbox, isDefName, ws);
}
}
};
/*Basic types of an elements used into formulas*/
/**
......@@ -2347,6 +2455,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
cBaseOperator.prototype.Calculate = function () {
return null;
};
cBaseOperator.prototype.CalculatePromise = cBaseType.prototype.CalculatePromise;
cBaseOperator.prototype.Assemble = function (arg) {
var str = "";
if (this.argumentsCurrent === 2) {
......@@ -2394,6 +2503,7 @@ parserHelp.setDigitSeparator(AscCommon.g_oDefaultCultureInfo.NumberDecimalSepara
this.value = new cError(cErrorType.wrong_name);
return this.value;
};
cBaseFunction.prototype.CalculatePromise = cBaseOperator.prototype.CalculatePromise
cBaseFunction.prototype.DecrementArguments = function () {
--this.argumentsCurrent;
};
......@@ -4225,7 +4335,7 @@ function parserFormula( formula, parent, _ws ) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
}
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
} else if (AscCommon.c_oNotifyType.Changed === data.type) {
if (this.parent && this.parent.onFormulaEvent) {
this.parent.onFormulaEvent(AscCommon.c_oNotifyParentType.Change, eventData);
}
......@@ -5102,6 +5212,8 @@ parserFormula.prototype.parse = function(local, digitDelim) {
};
parserFormula.prototype.calculate = function (opt_defName, opt_bbox, opt_offset) {
var value,
formula = this;
if (this.isCalculate && (!this.calculateDefName || this.calculateDefName[opt_bbox ? opt_bbox.getName() :
opt_bbox])) {
//cycle
......@@ -5145,7 +5257,7 @@ parserFormula.prototype.parse = function(local, digitDelim) {
for (var ind = 0; ind < currentElement.getArguments(); ind++) {
arg.unshift(elemArr.pop());
}
_tmp = currentElement.Calculate(arg, opt_bbox, opt_defName, this.ws);
_tmp = currentElement.CalculatePromise(arg, opt_bbox, opt_defName, this.ws);
if (cNumFormatNull !== _tmp.numFormat) {
numFormat = _tmp.numFormat;
} else if (0 > numFormat || cNumFormatNone === currentElement.numFormat) {
......@@ -5163,8 +5275,31 @@ parserFormula.prototype.parse = function(local, digitDelim) {
elemArr.push(currentElement);
}
}
this.value = elemArr.pop();
this.value.numFormat = numFormat;
value = elemArr.pop();
if (typeof value === "function") {
this.value = new cError(cErrorType.getting_data);
this.queue = true;
this.lazy_value = function () {
formula.lazy_value = null;
formula.queue = value()
.push(function (ret) {
formula.value = ret;
formula.value.numFormat = numFormat;
formula._endCalculate();
// updateOnScreen cell
formula.ws.workbook.handlers.trigger("cleanCellCache",
formula.ws.getId(), {0: opt_bbox},
AscCommonExcel.c_oAscCanChangeColWidth.none);
formula.queue = false;
// formula.lazy_value = null;
return formula.value;
});
return formula.queue
}
} else {
this.value = value;
this.value.numFormat = numFormat;
}
this._endCalculate();
return this.value;
......
......@@ -908,6 +908,11 @@
this.buildDefName = {};
},
calcTree: function() {
var dependency_graph = this,
formula,
i,
tasks = [],
lazy_value;
if (this.lockCounter > 0) {
return;
}
......@@ -924,26 +929,44 @@
this._broadcastCells(notifyData, calcTrack);
}
this._broadcastCellsEnd();
for (var i = 0; i < noCalcTrack.length; ++i) {
var formula = noCalcTrack[i];
for (i = 0; i < noCalcTrack.length; ++i) {
formula = noCalcTrack[i];
//defName recalc when calc formula containing it. no need calc it
formula.setIsDirty(false);
}
for (var i = 0; i < calcTrack.length; ++i) {
var formula = calcTrack[i];
for (i = 0; i < calcTrack.length; ++i) {
formula = calcTrack[i];
if (formula.getIsDirty()) {
formula.calculate();
}
}
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = this.cleanCellCache;
this.cleanCellCache = {};
for (var i in tmpCellCache) {
this.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
for (i = calcTrack.length-1; i >= 0; --i) {
lazy_value = calcTrack[i].lazy_value;
if (lazy_value) {
tasks.push(lazy_value());
}
}
function end () {
//copy cleanCellCache to prevent recursion in trigger("cleanCellCache")
var tmpCellCache = dependency_graph.cleanCellCache;
dependency_graph.cleanCellCache = {};
for (var i in dependency_graph.cleanCellCache) {
dependency_graph.wb.handlers.trigger("cleanCellCache", i, {0: tmpCellCache[i]},
AscCommonExcel.c_oAscCanChangeColWidth.none);
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
}
if (tasks.length > 0) {
new RSVP.Queue()
.push(function () {
return RSVP.all(tasks);
})
.push(end);
} else {
end();
}
AscCommonExcel.g_oVLOOKUPCache.clean();
AscCommonExcel.g_oHLOOKUPCache.clean();
},
initOpen: function() {
this._foreachDefName(function(defName) {
......@@ -5387,6 +5410,11 @@
this.nCol = -1;
this.formulaParsed = null;
}
Cell.prototype.getId = function () {
return [this.ws.getId(), this.getName()].join(",");
};
Cell.prototype.getStyle=function(){
return this.xfs;
};
......@@ -5945,7 +5973,7 @@
this.setValue("");
};
Cell.prototype._checkDirty = function(){
if(this.formulaParsed && this.formulaParsed.getIsDirty()) {
if(this.formulaParsed && this.formulaParsed.getIsDirty() && !this.formulaParsed.queue) {
this.formulaParsed.calculate();
}
};
......
This diff is collapsed.
......@@ -69,3 +69,5 @@ function MSPointerMove(){}
function MSPointerUp(){}
var editor;
RSVP.Queue = function () {};
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment