123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637 |
- 'use strict';
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
- function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
- /*
- *+------------------------------------------------------------------------+
- *| Licensed Materials - Property of IBM
- *| IBM Cognos Products: BI Dashboard
- *| (C) Copyright IBM Corp. 2017, 2020
- *|
- *| US Government Users Restricted Rights - Use, duplication or disclosure
- *| restricted by GSA ADP Schedule Contract with IBM Corp.
- *+------------------------------------------------------------------------+
- */
- define(['jquery', 'underscore', './QueryPostProcessor', '../../../../widgets/livewidget/nls/StringResources', '../../../../widgets/livewidget/query/QueryResultDataUtils', '../../../../apiHelpers/SlotAPIHelper'], function ($, _, QueryPostProcessor, StringResources, QueryResultDataUtils, SlotAPIHelper) {
- /**
- * This Class does Query result response post-processing such as processing the original query result
- * into a data re-structure to support multiple measures as series.
- **/
- var PostProcessMeasuresAsSeries = function (_QueryPostProcessor) {
- _inherits(PostProcessMeasuresAsSeries, _QueryPostProcessor);
- /**
- * @Constructor
- * @param {Object} options
- */
- function PostProcessMeasuresAsSeries(options) {
- _classCallCheck(this, PostProcessMeasuresAsSeries);
- var _this = _possibleConstructorReturn(this, _QueryPostProcessor.call(this, options));
- _this.visualization = options.visualization;
- _this.queryDefinition = options.queryDefinition;
- return _this;
- }
- /**
- * Process Query result based on the passed options to constructor.
- * For instance, limit the rows of datapoints so that in the response, for
- * specified dataitem, 'product', there will be only three tuples returned
- *
- * @return {QueryResultData}
- */
- PostProcessMeasuresAsSeries.prototype._processData = function _processData() {
- if (this._queryResultData && this._queryResultData.data && this._canProcess()) {
- var result = this._categorizeDataItems();
- // post-process the data items
- this._processDataItems(result);
- // post-process the data points
- this._processDataPoints(result);
- }
- //Process Done
- return this._queryResultData;
- };
- /**
- * Determine whether multiple measure series is supported
- */
- PostProcessMeasuresAsSeries.prototype._canProcess = function _canProcess() {
- var slots = this._getDatasetSlotList();
- var hasMultiMeasuresSeries = false,
- hasMultiMeasuresVal = false;
- _.each(slots, function (slot) {
- hasMultiMeasuresSeries = hasMultiMeasuresSeries || SlotAPIHelper.isMultiMeasuresSeriesSlot(slot);
- hasMultiMeasuresVal = hasMultiMeasuresVal || SlotAPIHelper.isMultiMeasuresValueSlot(slot);
- });
- return hasMultiMeasuresSeries && hasMultiMeasuresVal;
- };
- /**
- * Get the mapped projectionId for each dataItem.
- *
- * note: the logic here should be the same in visQueryBuilder.js:_slotToProjections()
- */
- PostProcessMeasuresAsSeries.prototype._getQueryProjectionId = function _getQueryProjectionId(slot, dataItemIdx) {
- // We don't project MultiMeasureSeries dataItem
- if (SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, dataItemIdx)) {
- return null;
- }
- var dataItemList = slot.getDataItemList();
- var validDataItemIds = _.filter(dataItemList, function (dataItem, idx) {
- return !SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, idx);
- });
- if (slot.isStacked() && validDataItemIds.length > 1) {
- return slot.getId();
- }
- return dataItemList[dataItemIdx].getId();
- };
- /**
- * Categorize the existing data items by category and ordinals
- * Keep track of data items original index and also the slot Id it which the data item is mapped.
- * The original index later allows the data points (prior to restructure) to access the mapping slot and data item.
- *
- * Despite the result data being the original data, the slot mapping already includes the multi-measures series mapping.
- * The slot mapping for mult-measures series needs to be ignored at this stage in order to match with the original data.
- */
- PostProcessMeasuresAsSeries.prototype._categorizeDataItems = function _categorizeDataItems() {
- var result = {
- cats: [],
- ords: [],
- commonIndices: {},
- mappedIndices: {},
- extraDataItems: []
- };
- var resultQueryDataItems = this._queryResultData.dataItems;
- var allProjectionIds = _.map(resultQueryDataItems, function (dataItem) {
- return dataItem.itemClass.id;
- });
- var mappedProjectionIds = [];
- var slotList = this._getDatasetSlotList();
- for (var i = 0; i < slotList.length; i++) {
- var slot = slotList[i];
- var dataItemList = slot.getDataItemList();
- for (var dataItemIdx = 0; dataItemIdx < dataItemList.length; dataItemIdx++) {
- var queryProjectionId = this._getQueryProjectionId(slot, dataItemIdx);
- if (queryProjectionId) {
- var type = dataItemList[dataItemIdx].getType();
- var idx = allProjectionIds.indexOf(queryProjectionId);
- var newEntry = {
- item: idx !== -1 ? resultQueryDataItems[idx] : undefined,
- slotId: slot.getId(),
- index: idx !== -1 ? idx : undefined
- };
- if (newEntry.index !== undefined) {
- result.mappedIndices[newEntry.index] = true;
- if (type === 'fact' && !slot.getDefinition().isMultiMeasureSupported()) {
- result.commonIndices[newEntry.index] = true;
- }
- }
- (type === 'attribute' ? result.cats : result.ords).push(newEntry);
- mappedProjectionIds.push(queryProjectionId);
- }
- }
- }
- var extraDataItemIds = _.difference(allProjectionIds, mappedProjectionIds);
- resultQueryDataItems.forEach(function (dataItem, index) {
- if (extraDataItemIds.indexOf(dataItem.itemClass.id) !== -1) {
- result.commonIndices[index] = true;
- result.extraDataItems.push({ dataItem: dataItem });
- }
- });
- return result;
- };
- /**
- * Process the data items
- * Consolidate the multiple measures to one measure data item and a extra series to indicate the measure
- */
- PostProcessMeasuresAsSeries.prototype._processDataItems = function _processDataItems(dataItems) {
- var _this2 = this;
- var resultData = this._queryResultData;
- var slots = this._getDatasetSlotList();
- // rebuild the dataItems from scratch!
- resultData.dataItems.length = 0;
- // maintain the order based on slots
- for (var i = 0; i < slots.length; i++) {
- var slot = slots[i];
- var dataItem = void 0;
- var isCategory = slot.getDataItemList()[0].getType() === 'attribute';
- if (isCategory) {
- if (SlotAPIHelper.isMultiMeasuresSeriesSlot(slot)) {
- var measures = _.map(dataItems.ords, function (ordinal) {
- var slotDefinition = _this2.visualization.getDefinition().getSlot(ordinal.slotId);
- var supportsMultiMeasure = slotDefinition.getProperty('multiMeasure');
- if (ordinal && ordinal.item && ordinal.item.itemClass && supportsMultiMeasure) {
- return ordinal.item.itemClass.h[0];
- }
- return undefined;
- });
- measures = _.compact(measures);
- // apply the multiple measures as a series
- dataItem = this._getSeriesDataItemforMultipleMeasures(dataItems.cats, measures, slot);
- } else {
- dataItem = this._getDataItem(dataItems.cats, slot);
- }
- } else if (SlotAPIHelper.isMultiMeasuresValueSlot(slot)) {
- // apply the value for all measures
- dataItem = this._getValueDataItemForMultipleMeasures(slot);
- } else {
- var matchDataItems = this._getDataItem(dataItems.ords, slot, /*findAll*/true);
- resultData.dataItems.push.apply(resultData.dataItems, matchDataItems);
- }
- if (dataItem) {
- resultData.dataItems.push(dataItem);
- }
- }
- dataItems.extraDataItems.forEach(function (extraDataItem) {
- extraDataItem.index = resultData.dataItems.length;
- resultData.dataItems.push(extraDataItem.dataItem);
- });
- };
- /**
- * Find the data item mapped to a slot
- */
- PostProcessMeasuresAsSeries.prototype._getDataItem = function _getDataItem(items, slot, findAll) {
- var slotItems = [];
- items.forEach(function (item) {
- if (item.slotId === slot.getId()) {
- slotItems.push(item.item);
- }
- });
- return findAll ? slotItems : slotItems[0];
- };
- /**
- * Collect the unique values from a category data item
- */
- PostProcessMeasuresAsSeries.prototype._getCategoryDataItemValues = function _getCategoryDataItemValues(categoryItem, stackIndex) {
- if (stackIndex > -1) {
- var stackValues = {};
- // collect the unique values
- _.each(categoryItem.items, function (item) {
- stackValues[item.t[stackIndex].u] = item.t[stackIndex];
- });
- var keys = Object.keys(stackValues);
- return _.map(keys, function (key) {
- return stackValues[key];
- });
- }
- return [];
- };
- /**
- * Create a category data item for the multiple measures series
- */
- PostProcessMeasuresAsSeries.prototype._getSeriesDataItemforMultipleMeasures = function _getSeriesDataItemforMultipleMeasures(cats, measures, slot) {
- return {
- itemClass: this._getSeriesDataItemClass(cats, slot),
- items: this._getSeriesTupleItems(cats, measures, slot)
- };
- };
- /**
- * Collect column ids from data item list of a given slot
- */
- PostProcessMeasuresAsSeries.prototype._getSlotColumnIds = function _getSlotColumnIds(slot) {
- return _.map(slot.getDataItemList(), function (dataItem) {
- return dataItem.getColumnId();
- });
- };
- /**
- * Collect all series data item class
- */
- PostProcessMeasuresAsSeries.prototype._getSeriesDataItemClass = function _getSeriesDataItemClass(cats, slot) {
- var ids = this._getSlotColumnIds(slot);
- var stackedItem = this._getDataItem(cats, slot);
- return {
- id: slot.getId(),
- h: _.map(ids, function (id, index) {
- if (SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, index)) {
- return {
- u: SlotAPIHelper.MULTI_MEASURES_SERIES,
- d: StringResources.get('MeasuresCaption')
- };
- } else {
- if (stackedItem && stackedItem.itemClass) {
- return _.find(stackedItem.itemClass.h, function (header) {
- return header.u === id;
- });
- }
- return undefined;
- }
- })
- };
- };
- /**
- * Collect all series data item tuple items
- */
- PostProcessMeasuresAsSeries.prototype._getSeriesTupleItems = function _getSeriesTupleItems(cats, measures, slot) {
- var _this3 = this;
- var rows = [];
- var baseRows = [];
- var catItem = this._getDataItem(cats, slot);
- var stackIds = catItem ? _.map(catItem.itemClass.h, function (header) {
- return header.u;
- }) : [];
- var ids = this._getSlotColumnIds(slot);
- var _loop = function _loop(index) {
- var id = ids[index];
- var isMultiMeasureSeries = SlotAPIHelper.isMultiMeasuresSeriesOrValue(slot, index);
- var values = isMultiMeasureSeries ? measures : _this3._getCategoryDataItemValues(catItem, stackIds.indexOf(id));
- //clean up rows and baseRows, so that no defect occurs in the loop with more than 2 ids.
- if (rows.length != 0) {
- baseRows = rows;
- }
- rows = [];
- if (baseRows.length > 0) {
- if (values.length > 0) {
- // Should preserve the drop order in multi measure series slot so iterate from baseRows to values
- for (var j = 0; j < baseRows.length; j++) {
- var row = baseRows[j];
- // duplicate the values tuple
- var clone = values.slice();
- for (var valueIndex = 0; valueIndex < clone.length; valueIndex++) {
- var value = clone[valueIndex];
- var newRow = valueIndex === 0 ? row : $.extend(true, {}, row);
- newRow.t[index] = isMultiMeasureSeries ? {
- u: _this3._getMultiMeasureMun(valueIndex),
- d: value.d,
- aggregate: value.aggregate
- } : value;
- rows.push(newRow);
- }
- }
- } else {
- // since there are no data to duplicate, simply update the base rows with null data
- baseRows.forEach(function (row) {
- row.t[index] = {
- u: null,
- d: null
- };
- });
- }
- } else {
- // prepare the base tuple item rows
- baseRows.push.apply(baseRows, _.map(values, function (value, index) {
- return {
- t: [isMultiMeasureSeries ? {
- u: value && value.u ? _this3._getMultiMeasureMun(index) : 'undefined',
- d: value && value.d ? value.d : 'undefined',
- aggregate: value.aggregate
- } : value]
- };
- }));
- }
- };
- for (var index = 0; index < ids.length; index++) {
- _loop(index);
- }
- // finish with the base rows if not expecting a category (i.e. not nested)
- if (!catItem && rows.length === 0) {
- rows.push.apply(rows, baseRows.slice());
- }
- return rows;
- };
- /**
- * Create a measure data item for the multiple measure values
- */
- PostProcessMeasuresAsSeries.prototype._getValueDataItemForMultipleMeasures = function _getValueDataItemForMultipleMeasures(slot) {
- return {
- itemClass: {
- id: slot.getId(),
- h: [{
- u: SlotAPIHelper.MULTI_MEASURES_VALUE,
- d: StringResources.get('ValuesCaption')
- }]
- }
- };
- };
- /**
- * Process all data point to restructure the data for multiple measures value and series
- */
- PostProcessMeasuresAsSeries.prototype._processDataPoints = function _processDataPoints(dataItems) {
- // prepare the new index for each slot
- var newIndices = this._allocateDataPointIndices(dataItems.extraDataItems);
- // restructured data
- var newData = [];
- var resultData = this._queryResultData;
- var multiMeasureSeriesId = this._getMultiMeasureSeriesId();
- var resultDataData = resultData.data;
- for (var i = 0; i < resultDataData.length; i++) {
- var datapoint = resultDataData[i];
- this._restructureDataPoint(dataItems, datapoint, newIndices, newData, multiMeasureSeriesId);
- }
- resultData.data = newData;
- };
- /**
- * Allocate the datapoint index for each data item.
- * Since the multiple measures are wrapped into a single measure with an extra series for the measures,
- * None of the slots would have multiple data items.
- *
- * ie. category, multimeasure_series, category(series), value
- * indices: {
- * seriesIndex: 1,
- * valueIndex: 3,
- * catIndices: [0, 2]
- * }
- */
- PostProcessMeasuresAsSeries.prototype._allocateDataPointIndices = function _allocateDataPointIndices(extraDataItems) {
- var catIndex = 0;
- var commonValueIndex = 0;
- var slots = this._getDatasetSlotList();
- var indices = {
- seriesIndex: -1,
- valueIndex: -1,
- commonIndices: [],
- catIndices: []
- };
- slots.forEach(function (slot, slotIndex) {
- var isCategory = slot.getDataItemList()[0].getType() === 'attribute';
- if (isCategory) {
- if (SlotAPIHelper.isMultiMeasuresSeriesSlot(slot)) {
- indices.seriesIndex = slotIndex;
- } else {
- indices.catIndices[catIndex++] = slotIndex;
- }
- } else if (SlotAPIHelper.isMultiMeasuresValueSlot(slot)) {
- indices.valueIndex = slotIndex;
- } else {
- slot.getDataItemList().forEach(function (dataItem, index) {
- indices.commonIndices[commonValueIndex++] = slotIndex + index;
- });
- }
- });
- extraDataItems.forEach(function (dataItem) {
- return indices.commonIndices[commonValueIndex++] = dataItem.index;
- });
- return indices;
- };
- /**
- * Restructure a single data point.
- * Consolidate multiple measures to one value
- * Categorize the measures to one category
- */
- PostProcessMeasuresAsSeries.prototype._restructureDataPoint = function _restructureDataPoint(dataItems, datapoint, indices, newData, multiMeasureSeriesId) {
- var catValueIndex = 0;
- var commonValueIndex = 0;
- var point = [];
- var ordValues = []; // has to be in order
- var catItemInSeries = void 0;
- var seriesIndexValue = 0;
- var slots = this._getDatasetSlotList();
- // Prepare the common portion of the data point
- for (var valueIndex = 0; valueIndex < datapoint.pt.length; valueIndex++) {
- var value = datapoint.pt[valueIndex];
- var category = void 0;
- for (var i = 0; i < dataItems.cats.length; i++) {
- if (dataItems.cats[i].index === valueIndex) {
- category = dataItems.cats[i];
- break;
- }
- }
- if (category) {
- var slot = void 0;
- for (var j = 0; j < slots.length; j++) {
- if (slots[j].getId() === category.slotId) {
- slot = slots[j];
- break;
- }
- }
- if (SlotAPIHelper.isMultiMeasuresSeriesSlot(slot)) {
- catItemInSeries = category.item;
- seriesIndexValue = value;
- } else {
- point[indices.catIndices[catValueIndex++]] = value;
- }
- } else if (dataItems.commonIndices[valueIndex]) {
- point[indices.commonIndices[commonValueIndex++]] = value;
- } else if (dataItems.mappedIndices[valueIndex]) {
- // Keep track of the remaining ordinal values (as long as they're 'valid' - mapped to a slot)
- ordValues.push(value);
- }
- }
- // Duplicate the data point by each measure
- var processedIndices = [];
- for (var indexValue = 0; indexValue < ordValues.length; indexValue++) {
- var ordValue = ordValues[indexValue];
- var pt = indexValue === 0 ? point : point.slice();
- pt[indices.seriesIndex] = this._getSeriesIndex(dataItems, indexValue, catItemInSeries, seriesIndexValue, processedIndices, multiMeasureSeriesId);
- pt[indices.valueIndex] = ordValue;
- newData.push({ pt: pt });
- processedIndices.push(indexValue);
- }
- };
- PostProcessMeasuresAsSeries.prototype._getSeriesIndex = function _getSeriesIndex(dataItems, ordIndex, catItemInSeries, originalIndex, processedIndices, multiMeasureSeriesId) {
- // The multiSeriesResultItem has tuples built from _getSeriesTupleItems. It's a cross joined tuple set of the multi measure series
- var multiSeriesResultItem = this._queryResultData && this._queryResultData.dataItems && this._queryResultData.dataItems[QueryResultDataUtils.getDataItemIndex(this._queryResultData, multiMeasureSeriesId)];
- // The target tuple to search in multiSeriesResultItem and return its index
- var measureMun = this._getMultiMeasureMun(ordIndex);
- var targetTuple = catItemInSeries && catItemInSeries.items[originalIndex].t.map(function (tuple) {
- return tuple.u;
- }) || [];
- targetTuple.push(measureMun);
- // Search the target tuple in the cross joined tuple set of the multi measure series, and return the index
- var resultIndex = -1;
- if (multiSeriesResultItem.items.length !== 0) {
- for (var idx = 0; idx < multiSeriesResultItem.items.length; idx++) {
- var tuple = multiSeriesResultItem.items[idx];
- //check if tuple matches targetTuple & index is not in processedIndices
- var matched = this._isMatched(tuple, targetTuple, processedIndices, idx);
- if (matched) {
- resultIndex = idx;
- break;
- }
- }
- }
- return resultIndex;
- };
- PostProcessMeasuresAsSeries.prototype._foundInTarget = function _foundInTarget(u, targetTuple) {
- var found = false;
- for (var j = 0; j < targetTuple.length; j++) {
- if (targetTuple[j] === u) {
- found = true;
- break;
- }
- }
- return found;
- };
- PostProcessMeasuresAsSeries.prototype._isMatched = function _isMatched(tuple, targetTuple, processedIndices, idx) {
- //check if tuple matches targetTuple & index is not in processedIndices
- var idxContained = processedIndices.indexOf(idx) !== -1;
- if (idxContained === false) {
- var matches = 0;
- var len = tuple.t.length;
- for (var i = 0; i < len; i++) {
- if (this._foundInTarget(tuple.t[i].u, targetTuple)) {
- matches++;
- }
- }
- if (matches === tuple.t.length) {
- return true;
- }
- }
- return false;
- };
- PostProcessMeasuresAsSeries.prototype._getMultiMeasureMun = function _getMultiMeasureMun(index) {
- if (!this._multiMeasureSlotAPI) {
- var slotList = this._getDatasetSlotList();
- for (var i = 0; i < slotList.length; i++) {
- var slot = slotList[i];
- if (SlotAPIHelper.isMultiMeasuresValueSlot(slot)) {
- this._multiMeasureSlotAPI = slot;
- break;
- }
- }
- }
- var dataItemAPIs = this._multiMeasureSlotAPI.getDataItemList();
- return dataItemAPIs[index].getId();
- };
- /**
- * Find the slot ID where the multiMeasureSeries is
- */
- PostProcessMeasuresAsSeries.prototype._getMultiMeasureSeriesId = function _getMultiMeasureSeriesId() {
- var slots = this._getDatasetSlotList();
- var multiMeasureSeriesSlot = _.find(slots, function (slot) {
- return SlotAPIHelper.isMultiMeasuresSeriesSlot(slot);
- });
- return multiMeasureSeriesSlot && multiMeasureSeriesSlot.getId();
- };
- PostProcessMeasuresAsSeries.prototype._getDatasetSlotList = function _getDatasetSlotList() {
- var _this4 = this;
- if (!this._datasetSlotList) {
- var slots = this.visualization.getSlots().getMappedSlotList();
- this._datasetSlotList = slots.filter(function (slot) {
- return slot.getDefinition().getDatasetIdList().indexOf(_this4.queryDefinition.getId()) !== -1;
- });
- }
- return this._datasetSlotList;
- };
- return PostProcessMeasuresAsSeries;
- }(QueryPostProcessor);
- return PostProcessMeasuresAsSeries;
- });
- //# sourceMappingURL=PostProcessMeasuresAsSeries.js.map
|