123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383 |
- 'use strict';
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: Dashboard
- * (C) Copyright IBM Corp. 2018, 2020
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- *
- */
- define(['underscore', './SummaryTypes', './SummaryQueryResult', '../../../widgets/livewidget/nls/StringResources', '../../../filters/FilterSpecHelper'], function (_, SummaryTypes, SummaryQueryResult, StringResources, FilterSpecHelper) {
- 'use strict';
- var OVERALL_SUMMARY_QUERY_KEY = '_overallSummaryQueryKey';
- /**
- * The current implementation requires a summary query for each summary set which may introduce performance issues.
- * Add limit to restrict number of queries until we can send the summary queries together
- */
- var NESTED_ITEM_LIMIT = 12;
- /**
- * @class SummaryFeature
- * Summary feature to run query for a visualization,
- * based on different combination of projections and query context.
- */
- var SummaryFeature = function () {
- function SummaryFeature(_ref) {
- var content = _ref.content,
- features = _ref.features;
- _classCallCheck(this, SummaryFeature);
- // Local object to store category and measure data item unique ids.
- // Used to build summary query projections and query UIs to get summary result.
- this._oQueryProjectionObj = {};
- // Object to store summary query result and is accessible by projected category unique ids.
- this._oQueriesResult = {};
- this.logger = features['Dashboard.Logger'];
- this.visualization = features['Visualization'];
- this.renderSequence = features['RenderSequence'];
- this.renderSequence.registerRenderStepProvider(this);
- this.content = content;
- this._api = {
- getSummary: this._getSummary.bind(this),
- isNotSupported: this.isNotSupported.bind(this)
- };
- }
- /**
- * @override
- * @public
- * @function getAPI
- * Get the Summary feature API
- * @returns the public api of the summary feature that is accessible by clients.
- */
- SummaryFeature.prototype.getAPI = function getAPI() {
- return this._api;
- };
- /**
- * Load the feature if a data slot has "summaries" in slot definition,
- * and "summaryValues" in the same (table) or another (e.g. crossTab) slot definition with data items
- * of aggregation type belongs to any of the following:
- * [
- 'none',
- 'countdistinct',
- 'avg',
- 'calculated',
- 'automatic'
- ]
- */
- SummaryFeature.prototype.isEnabled = function isEnabled() {
- return _.find(this.visualization.getSlots().getSlotList(), function (slot) {
- return slot.getDefinition().getProperty('summaries') || slot.getDefinition().getProperty('summaryValues');
- });
- };
- /**
- * Check unsupported conditions:
- * 1) If crossjoin of data items set is greater than limit
- * 2) no value filters in the widget (we can try to allow this in relational)
- * 3) no OLAP
- */
- SummaryFeature.prototype.isNotSupported = function isNotSupported() {
- // 1. Not supported when number of nesting is greater than limit.
- var numberOfNest = 1;
- var slotAPIs = this.visualization.getSlots().getMappedSlotList();
- _.each(slotAPIs, function (slotAPI) {
- numberOfNest = slotAPI.getDefinition().getProperty('summaries') ? numberOfNest * slotAPI.getDataItemList().length : numberOfNest;
- });
- var isNotSupported = numberOfNest > NESTED_ITEM_LIMIT;
- if (isNotSupported) {
- return isNotSupported;
- }
- // 2. Not supported when there is post fact filter.
- var localFilters = this.visualization.getLocalFilters().getFilterList();
- isNotSupported = _.find(localFilters, function (entry) {
- return entry.aggregationType && entry.preOrPost === 'post' && FilterSpecHelper.isRange(entry);
- }) !== undefined;
- if (isNotSupported) {
- return isNotSupported;
- }
- // 3. Not supported when is OLAP
- // Just check one dataItem since data items are from all the same data source
- var dataItemAPIs = slotAPIs && slotAPIs.length && slotAPIs[0].getDataItemList();
- isNotSupported = !dataItemAPIs || dataItemAPIs.length === 0 || !dataItemAPIs[0].getMetadataColumn() || dataItemAPIs[0].getMetadataColumn().isOlapColumn();
- if (isNotSupported) {
- return isNotSupported;
- }
- var topBottoms = {};
- slotAPIs.forEach(function (slot) {
- var dataItems = slot.getDataItemList();
- dataItems.forEach(function (dataItem) {
- if (dataItem.getTopBottom()) {
- // 4. Not supported when there is a topBottom defined on a fact data item and is in summaryValues slot (RTC#250663)
- if (slot.getDefinition().getProperty('summaryValues') && dataItem.getType() === 'fact') {
- isNotSupported = true;
- }
- // Map of topBottoms per slot
- if (!topBottoms[slot.getId()]) {
- topBottoms[slot.getId()] = [];
- }
- topBottoms[slot.getId()].push(dataItem);
- }
- });
- });
- if (isNotSupported) {
- return isNotSupported;
- }
- // 5. Not supported when there are top/bottom on different slots (RTC#243655)
- return _.size(topBottoms) > 1;
- };
- /**
- * @override
- * @public
- * @function getExtraRenderSequenceSteps
- * Get the extra render sequence task required for the summary feature
- * @returns {Object[]} Array of task definitions
- */
- SummaryFeature.prototype.getRenderStepList = function getRenderStepList() {
- return this.isEnabled() ? [{
- id: 'data-summary',
- dependencies: ['data'],
- modulePath: 'dashboard-analytics/extensions/client-summaries/content-features/SummaryTask',
- moduleOptions: {
- summaryFeature: this,
- visualization: this.visualization,
- content: this.content
- }
- }] : [];
- };
- /**
- * @private
- * @public
- * @function _getSummary
- * Get summary result by UIs
- * @param {string[]} categoryUIds - array of unique category data item ids to identify a summary query result
- * @param {string} measureUId - the unique id of the measure to get the summary
- * @param {string[]} tupleIds - array of unique tuple id to get a summary tuple cell
- * @returns {Promise} A promise which resolves the summary query result when the result is ready
- */
- SummaryFeature.prototype._getSummary = function _getSummary(categoryUIds, measureUId, tupleIds) {
- var _this = this;
- var originalCategoryUIds = categoryUIds.slice(0);
- var queryResultPromise = this._oQueriesResult[this._getCategoryQueryKey(categoryUIds)];
- if (queryResultPromise) {
- return queryResultPromise.then(function (summaryResult) {
- return summaryResult.getValue(originalCategoryUIds, measureUId, tupleIds);
- }).catch(function (error) {
- // return Promise.reject(this._buildErrorObj(error));
- return _this._buildErrorObj(error);
- });
- } else {
- return Promise.resolve(this._buildErrorObj());
- }
- };
- /**
- * @returns error object from different error case
- */
- SummaryFeature.prototype._buildErrorObj = function _buildErrorObj() {
- return {
- error: StringResources.get('errorCellWarning')
- };
- };
- /**
- * @returns {object} an object of an array of combination of category unique ids of the widget and an array of the measures with aggregation
- * types in SUMMARY_TYPES. If a widget has slot1 = [A, B, C], slot2 = [D, E, F], value slot = [G, H] where aggregate for G and H is avg.
- * @example result = {
- * aCategoryUIds: [[A],[AB],[ABC],[D],[DE],[EDF],[AD],[ADE],[ADEF],[ABD],[ABDE],[ABDEF],[ABCD],[ABCDE]],
- * aOrdinalUIds: [G, H]
- * }
- */
- SummaryFeature.prototype.getProjectionListObj = function getProjectionListObj() {
- this._oQueryProjectionObj = {};
- var aSummarySlotsDataItems = []; // An array of category slot dataItems array where the summary is corresponding to
- var aSummaryValuesDataItems = []; // An array of ordinal slot dataItems array where the summary is applied t
- var dataSlots = this.visualization.getSlots().getMappedSlotList();
- dataSlots.forEach(function (slotAPI) {
- if (slotAPI.getDefinition().getProperty('summaries')) {
- var categoryDataItems = _.filter(slotAPI.getDataItemList(), function (dataItem) {
- return dataItem.getType() === 'attribute';
- });
- if (categoryDataItems) {
- aSummarySlotsDataItems.push(categoryDataItems);
- }
- }
- if (slotAPI.getDefinition().getProperty('summaryValues')) {
- var ordinalDataItems = _.filter(slotAPI.getDataItemList(), function (dataItem) {
- return SummaryTypes.indexOf(dataItem.getAggregation()) !== -1 && dataItem.getType() === 'fact';
- });
- aSummaryValuesDataItems = aSummaryValuesDataItems.concat(ordinalDataItems);
- }
- });
- if (aSummarySlotsDataItems.length || aSummaryValuesDataItems.length) {
- if (aSummarySlotsDataItems.length) {
- // 1) Build unique id sets from all data items of all category slots. E.g. slot1 = [A, B, C], slot2 = [D, E, F]
- // aSlotDataItemUIdSets = [
- // [A, AB, ABC],
- // [D, DE, EDF]
- // ]
- var aSlotDataItemUIdSets = this._buildIdSetsFromSlotsDataItems(aSummarySlotsDataItems);
- // 2) Build cross join set from direct unique id sets
- if (aSlotDataItemUIdSets.length) {
- aSlotDataItemUIdSets = this._crossJoinSlotUIdSets(aSlotDataItemUIdSets);
- }
- var aResultCategoryUIds = _.flatten(aSlotDataItemUIdSets, true);
- aResultCategoryUIds.pop(); // Remove the detail unique id combinations
- this._oQueryProjectionObj.aCategoryUIds = aResultCategoryUIds;
- } else {
- this._oQueryProjectionObj.aCategoryUIds = [];
- }
- if (aSummaryValuesDataItems.length) {
- // Array of summary values slots dataItems UIs.
- var aSummaryValuesUIds = _.map(aSummaryValuesDataItems, function (valueDataItem) {
- return valueDataItem.getId();
- });
- this._oQueryProjectionObj.aOrdinalUIds = aSummaryValuesUIds;
- } else {
- this._oQueryProjectionObj.aOrdinalUIds = [];
- }
- }
- return this._oQueryProjectionObj;
- };
- SummaryFeature.prototype._buildIdSetsFromSlotsDataItems = function _buildIdSetsFromSlotsDataItems(aSummarySlotsDataItems) {
- var aSlotDataItemUIdSets = [];
- aSummarySlotsDataItems.forEach(function (dataItems) {
- var currentUIds = [];
- var aUIdsPerSlot = [];
- dataItems.forEach(function (dataItem) {
- currentUIds = currentUIds.concat(dataItem.getId());
- aUIdsPerSlot.push(currentUIds);
- });
- aSlotDataItemUIdSets.push(aUIdsPerSlot);
- });
- return aSlotDataItemUIdSets;
- };
- SummaryFeature.prototype._crossJoinSlotUIdSets = function _crossJoinSlotUIdSets(aSlotDataItemUIdSets) {
- var crossjointUIds = [];
- for (var i = 0; i < aSlotDataItemUIdSets.length; i++) {
- var aSet1 = aSlotDataItemUIdSets[i];
- var j = i + 1;
- while (aSlotDataItemUIdSets[j] && aSlotDataItemUIdSets[j].length) {
- crossjointUIds.push(this._crossJoin(aSet1, aSlotDataItemUIdSets[j]));
- j++;
- }
- }
- return aSlotDataItemUIdSets.concat(crossjointUIds);
- };
- /**
- * Build a crossjoin set of 2 sets.
- * @example
- * set1 = [A, AB, ABC]
- * set2 = [D, DE, DEF]
- * result = [
- * AD,
- * ADE,
- * ADEF,
- * ABD,
- * ABDE,
- * ABDEF,
- * ABCD,
- * ABCDE,
- * ABCDEF // The last element generated is a detail row
- * ]
- *
- * @param {Array} aUIdSet1 first set which is an array of unique UIs
- * @param {Array} aUIdSet2 second set. An array of unique UIs
- * @returns a crossjoin set of aUIdSet1 and aUIdSet2
- */
- SummaryFeature.prototype._crossJoin = function _crossJoin(aUIdSet1, aUIdSet2) {
- var crossjointUIds = [];
- aUIdSet1.forEach(function (keySet1) {
- var jointKeys = [];
- aUIdSet2.forEach(function (keySet2) {
- jointKeys = keySet1.concat(keySet2);
- crossjointUIds.push(jointKeys);
- });
- });
- return crossjointUIds;
- };
- /**
- * @private
- * @public
- * Add a summary result to queries map.
- * The corresponding entry is the query promise.
- * @param {integer} idx the index of the aCategoryUIds
- * @param {Object} queryPromise the summary query promise
- */
- SummaryFeature.prototype.addSummaryQueryResult = function addSummaryQueryResult(idx, queryPromise) {
- var _this2 = this;
- this._oQueriesResult[this._getCategoryQueryKey(this._oQueryProjectionObj.aCategoryUIds[idx])] = queryPromise.then(function (queryResult) {
- return new SummaryQueryResult(queryResult.getResult(), _this2.logger);
- }).catch(function (error) {
- _this2.logger.error(error);
- return Promise.reject(error);
- });
- };
- /**
- * @private
- * @param {string []} aCategoryUId - an array of strings represent the category unique ids.
- * @returns string - unique summary query key
- */
- SummaryFeature.prototype._getCategoryQueryKey = function _getCategoryQueryKey(aCategoryUId) {
- // The key is built from all category data item unique ids.
- // The overall summary query doesn't have any category unique id.
- var uniqueIds = aCategoryUId && aCategoryUId.length ? aCategoryUId : [OVERALL_SUMMARY_QUERY_KEY];
- uniqueIds.sort();
- return uniqueIds.join();
- };
- return SummaryFeature;
- }();
- return SummaryFeature;
- });
- //# sourceMappingURL=SummaryFeature.js.map
|