QueryProvider.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393
  1. 'use strict';
  2. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  3. /**
  4. * Licensed Materials - Property of IBM
  5. * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2019, 2021
  6. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  7. */
  8. define(['../../../lib/@waca/dashboard-common/dist/core/APIFactory', './api/QueryProviderAPI', '../../../filters/FilterQuerySpec', '../../../apiHelpers/SlotAPIHelper', './QueryDataItemBuilder', './DataQueryUtils', '../../../lib/@waca/core-client/js/core-client/utils/BrowserUtils', '../../../widgets/livewidget/query/CommonQueryBuilder', 'underscore'], function (APIFactory, QueryProviderAPI, FilterQuerySpec, SlotAPIHelper, QueryDataItemBuilder, DataQueryUtils, BrowserUtils, CommonQueryBuilder, _) {
  9. var _class, _temp;
  10. var QueryProvider = (_temp = _class = function () {
  11. function QueryProvider(_ref) {
  12. var content = _ref.content,
  13. dashboardAPI = _ref.dashboardAPI;
  14. _classCallCheck(this, QueryProvider);
  15. this.content = content;
  16. this.dashboard = dashboardAPI;
  17. this.content.getFeature('DataQueryExecution').registerQueryProvider(this.getAPI());
  18. }
  19. QueryProvider.prototype.getAPI = function getAPI() {
  20. if (!this.api) {
  21. this.api = APIFactory.createAPI(this, [QueryProviderAPI]);
  22. }
  23. return this.api;
  24. };
  25. QueryProvider.prototype.getType = function getType() {
  26. return 'main';
  27. };
  28. /**
  29. * @returns a list of query specs.
  30. */
  31. QueryProvider.prototype.getQuerySpecList = function getQuerySpecList() {
  32. var _this = this;
  33. var visualization = this.content.getFeature('Visualization');
  34. var dataSource = visualization.getDataSource();
  35. var dataSourceId = dataSource && dataSource.getId();
  36. var mappedSlotsList = visualization.getSlots().getMappedSlotList();
  37. var layerToQuerySlotsMap = new Map();
  38. var mappingCompleteLayerList = this._getMappingCompleteLayers() || [];
  39. var _loop = function _loop(i) {
  40. var slot = mappedSlotsList[i];
  41. if (_this._canQuery(slot)) {
  42. var layerIds = slot.getDefinition().getDatasetIdList();
  43. layerIds.forEach(function (layerId) {
  44. if (mappingCompleteLayerList.indexOf(layerId) === -1) {
  45. //skip the layer if the required slots for that layer are NOT filled
  46. return;
  47. }
  48. if (!layerToQuerySlotsMap.has(layerId)) {
  49. layerToQuerySlotsMap.set(layerId, []);
  50. }
  51. layerToQuerySlotsMap.get(layerId).push(slot);
  52. });
  53. }
  54. };
  55. for (var i = 0; i < mappedSlotsList.length; i++) {
  56. _loop(i);
  57. }
  58. var querySpecList = [];
  59. var mainQuerySpecList = this._getMainQuerySpecList(layerToQuerySlotsMap);
  60. querySpecList.push.apply(querySpecList, mainQuerySpecList);
  61. querySpecList.push.apply(querySpecList, this._getMinMaxQuerySpecList(layerToQuerySlotsMap, mainQuerySpecList));
  62. for (var j = 0; j < querySpecList.length; j++) {
  63. querySpecList[j].dataSourceId = dataSourceId;
  64. }
  65. return querySpecList;
  66. };
  67. QueryProvider.prototype._getMappingCompleteLayers = function _getMappingCompleteLayers() {
  68. var visualization = this.content.getFeature('Visualization');
  69. var datasetIds = visualization.getDefinition().getDatasetList().map(function (dataset) {
  70. return dataset.id;
  71. });
  72. var slots = visualization.getSlots();
  73. return datasetIds.filter(function (id) {
  74. return slots.isMappingComplete(id);
  75. });
  76. };
  77. QueryProvider.prototype._getQuerySpecLimit = function _getQuerySpecLimit() {
  78. //DEFAULT limit
  79. var dataRowLimit = QueryProvider.DEFAULT_ROWLIMIT;
  80. var definition = this.content.getFeature('Visualization').getDefinition();
  81. if (definition.getProperty('dataRowLimit')) {
  82. //Some charts have difficulty rendering a large set of rows, in this case, a smaller dataRowLimit is set in the definition.
  83. dataRowLimit = definition.getProperty('dataRowLimit');
  84. // Reduce data limit on IE
  85. if (BrowserUtils.isIE() && definition.getProperty('dataRowLimitIE')) {
  86. dataRowLimit = definition.getProperty('dataRowLimitIE');
  87. }
  88. }
  89. return dataRowLimit;
  90. };
  91. QueryProvider.prototype._getMainQuerySpecList = function _getMainQuerySpecList(layerToQuerySlotsMap) {
  92. var _this2 = this;
  93. var queryDataItemBuilder = this._getQueryDataItemBuilder(layerToQuerySlotsMap);
  94. var querySpecList = [];
  95. var outputEdgeFilterExceptions = [];
  96. var queryHints = this._getQueryHints(layerToQuerySlotsMap);
  97. layerToQuerySlotsMap.forEach(function (querySlots, layerId) {
  98. var specObject = {
  99. id: layerId,
  100. type: 'main',
  101. spec: {
  102. version: '1',
  103. dataItems: [],
  104. projections: [],
  105. limit: _this2._getQuerySpecLimit()
  106. }
  107. };
  108. querySlots.forEach(function (slot) {
  109. var _specObject$spec$proj, _specObject$spec$data;
  110. (_specObject$spec$proj = specObject.spec.projections).push.apply(_specObject$spec$proj, _this2._getProjectionsForSlot(slot));
  111. (_specObject$spec$data = specObject.spec.dataItems).push.apply(_specObject$spec$data, _this2._getDataItemsForSlot(slot, queryDataItemBuilder, outputEdgeFilterExceptions));
  112. });
  113. var excludeNonProjectedRangeFilters = DataQueryUtils.shouldExcludeNonProjectedRangeFilters(layerToQuerySlotsMap, layerId);
  114. var filters = _this2._getFilters(layerId, outputEdgeFilterExceptions, excludeNonProjectedRangeFilters);
  115. if (filters && filters.length) {
  116. specObject.spec.filters = filters;
  117. }
  118. /**
  119. * @todo: useAPI. should check for empty queryHints.
  120. * but for now we reserve the same behavior as current that it includes empty queryHints.
  121. * if (_.keys(queryHints).length) { specObject.spec.queryHints = queryHints; }
  122. */
  123. specObject.spec.queryHints = queryHints;
  124. var suppression = _this2._getSuppression();
  125. if (suppression) {
  126. specObject.spec.suppress = [suppression];
  127. }
  128. querySpecList.push(specObject);
  129. });
  130. return querySpecList;
  131. };
  132. QueryProvider.prototype._findDataItemInSlotsById = function _findDataItemInSlotsById(slots, id) {
  133. var _dataItem = void 0;
  134. slots.forEach(function (slot) {
  135. slot.getDataItemList().forEach(function (dataItem) {
  136. if (dataItem.getId() === id) {
  137. _dataItem = dataItem;
  138. }
  139. });
  140. });
  141. return _dataItem;
  142. };
  143. QueryProvider.prototype._getMinMaxQuerySpecList = function _getMinMaxQuerySpecList(layerToQuerySlotsMap, mainQuerySpecList) {
  144. var _this3 = this;
  145. var minMaxQuerySpecList = [];
  146. var shouldAxisScaleBeFixed = this.content.getPropertyValue('maintainAxisScales');
  147. layerToQuerySlotsMap.forEach(function (querySlots, layerId) {
  148. var containsCategorySlots = !!querySlots.find(function (slot) {
  149. return slot.getDataItemList()[0].getType() === 'attribute';
  150. });
  151. if (shouldAxisScaleBeFixed && containsCategorySlots) {
  152. var mainQuerySpecObj = mainQuerySpecList.find(function (obj) {
  153. return obj.id === layerId;
  154. });
  155. var projectionsAndDataItems = {
  156. dataItems: mainQuerySpecObj.spec.dataItems,
  157. limit: mainQuerySpecObj.spec.limit
  158. };
  159. var minMaxProjections = JSON.parse(JSON.stringify(mainQuerySpecObj.spec.projections));
  160. minMaxProjections = minMaxProjections.filter(function (id) {
  161. var dataItem = _this3._findDataItemInSlotsById(querySlots, id);
  162. return dataItem && dataItem.getType() === 'fact';
  163. });
  164. // No need for a minmax query when no measures are projected
  165. if (minMaxProjections && minMaxProjections.length) {
  166. projectionsAndDataItems.projections = minMaxProjections;
  167. var aItemsToExclude = [];
  168. querySlots.forEach(function (slot) {
  169. if (slot.getDefinition().getProperty('useInTopBottomQueries') === false) {
  170. aItemsToExclude = aItemsToExclude.concat(slot.getDataItemList().map(function (dataItem) {
  171. return dataItem.getId();
  172. }));
  173. } else if (slot.isStacked()) {
  174. // If the slot is stacked and has multiple data items then we can not add it to the min max query
  175. // In this scenario the slot is shown as a nested set of data items and we need to remove using the id.
  176. aItemsToExclude = aItemsToExclude.concat(slot.getId());
  177. }
  178. });
  179. // Make one query to get min and max only with the fields from the current widget, plus the global filters and slicers (filters from other widgets).
  180. var topBottomQuery = CommonQueryBuilder.buildMinMaxQueryFromDataItems(projectionsAndDataItems, {
  181. 'itemsToExclude': aItemsToExclude
  182. }, _this3.dashboard.getGlassCoreSvc('.Logger'));
  183. minMaxQuerySpecList.push({
  184. id: layerId,
  185. type: 'minmax',
  186. spec: topBottomQuery
  187. });
  188. }
  189. }
  190. });
  191. return minMaxQuerySpecList;
  192. };
  193. QueryProvider.prototype._canQuery = function _canQuery(slot) {
  194. // If the slot has a multimeasure series item and
  195. // it is the one and only item in the slot, we can't generate a query from the slot.
  196. return !(SlotAPIHelper.isMultiMeasuresSeriesSlot(slot) && !slot.isStacked());
  197. };
  198. QueryProvider.prototype._isSlotWithStackedProjection = function _isSlotWithStackedProjection(slot) {
  199. // if the slot contains at least 2 items(except multi-measure) and it is stacked(slotDefinition)
  200. var validDataItems = SlotAPIHelper.getValidDataItems(slot);
  201. return slot.isStacked() && validDataItems.length > 1;
  202. };
  203. /**
  204. * return the projections for the specified slot
  205. * @param {*} querySlot queriable slot
  206. */
  207. QueryProvider.prototype._getProjectionsForSlot = function _getProjectionsForSlot(querySlot) {
  208. if (this._isSlotWithStackedProjection(querySlot)) {
  209. return [querySlot.getId()];
  210. } else {
  211. var validDataItems = SlotAPIHelper.getValidDataItems(querySlot);
  212. return validDataItems.map(function (dataItem) {
  213. return dataItem.getId();
  214. });
  215. }
  216. };
  217. /**
  218. * return the dataItems for the specified slot
  219. * @param {*} querySlot queriable slot
  220. * @param {*} queryDataItemBuilder
  221. */
  222. QueryProvider.prototype._getDataItemsForSlot = function _getDataItemsForSlot(querySlot, queryDataItemBuilder, outputEdgeFilterExceptions) {
  223. var validDataItems = SlotAPIHelper.getValidDataItems(querySlot);
  224. var dataItemQuerySpecs = validDataItems.map(function (dataItem) {
  225. var itemSpec = queryDataItemBuilder.build(dataItem, querySlot, outputEdgeFilterExceptions);
  226. return itemSpec;
  227. });
  228. if (this._isSlotWithStackedProjection(querySlot)) {
  229. dataItemQuerySpecs.push({
  230. id: querySlot.getId(),
  231. nest: validDataItems.map(function (item) {
  232. return item.getId();
  233. })
  234. });
  235. }
  236. return dataItemQuerySpecs;
  237. };
  238. QueryProvider.prototype._getQueryDataItemBuilder = function _getQueryDataItemBuilder(layerToQuerySlotsMap) {
  239. var internalVisualization = this.content.getFeature('Visualization.internal');
  240. var dataItemList = internalVisualization.getSlots().getDataItemList();
  241. var applyServerSort = dataItemList && _.find(dataItemList, function (dataItem) {
  242. return dataItem.getColumnId() === '_multiMeasuresSeries';
  243. }) !== undefined;
  244. return new QueryDataItemBuilder({
  245. internalVisualization: internalVisualization,
  246. layerToQuerySlotsMap: layerToQuerySlotsMap,
  247. applyServerSort: applyServerSort
  248. });
  249. };
  250. QueryProvider.prototype._getFilters = function _getFilters(layerId, outputEdgeFilterExceptions, excludeNonProjectedRangeFilters) {
  251. var internalVisualization = this.content.getFeature('Visualization.internal');
  252. var localFilterList = internalVisualization.getLocalFilters().getFilterList();
  253. var updatedFilterSpecList = DataQueryUtils.convertExcludeEmptyValuesFilters(localFilterList, internalVisualization.getDataSource());
  254. var searchFilters = internalVisualization.getSearchFilterList();
  255. var filterSpec = new FilterQuerySpec(internalVisualization);
  256. var hasSharedSlot = !!internalVisualization.getDefinition().getSlotList().find(function (slot) {
  257. return slot.getDatasetIdList().length > 1;
  258. });
  259. filterSpec.addFiltersToSpec(updatedFilterSpecList, {
  260. layerId: layerId,
  261. edgeFilterExceptions: outputEdgeFilterExceptions,
  262. excludeNonProjectedRangeFilters: excludeNonProjectedRangeFilters,
  263. projectInAllLayers: hasSharedSlot
  264. });
  265. filterSpec.addFiltersToSpec(searchFilters, {
  266. layerId: layerId
  267. });
  268. return filterSpec.hasFilterSpec() ? filterSpec.getFilterSpec() : null;
  269. };
  270. /**
  271. * Turn on multiEdgeSort based on following rules:
  272. * 1: if slot is isMultiMeasuresSeries and is Rank, does not consider as edge
  273. * 2: if slot is isMultiMeasuresSeries and is stacked, considered as one edge
  274. * 3: if slot is stacked, it is considered as one edge
  275. * 4: if slot is of type ordinal, does not consider as edge
  276. * if number of edges is more than one, then turn on multiEdgeSort otherwise not
  277. * @return {boolean} true if number of edges to sort is more than 1.
  278. * @private
  279. */
  280. QueryProvider.prototype._isMultiEdgeSortNeeded = function _isMultiEdgeSortNeeded(layerToQuerySlotsMap) {
  281. var visualization = this.content.getFeature('Visualization');
  282. if (!visualization.getDefinition().getMultiEdgeSort()) {
  283. return false;
  284. }
  285. var numberOfEdges = 0;
  286. layerToQuerySlotsMap.forEach(function (querySlots) {
  287. querySlots.forEach(function (slot) {
  288. var dataItems = slot.getDataItemList();
  289. if (dataItems.length > 0 && dataItems[0].getType() !== 'fact') {
  290. numberOfEdges++;
  291. }
  292. });
  293. });
  294. return numberOfEdges > 1;
  295. };
  296. QueryProvider.prototype._getSuppression = function _getSuppression() {
  297. var suppression = this.content.getPropertyValue('suppression');
  298. if (suppression === QueryProvider.SUPPRESSION_NONE) {
  299. return QueryProvider.SUPPRESSION_NONE;
  300. }
  301. };
  302. QueryProvider.prototype._getQueryHints = function _getQueryHints(layerToQuerySlotsMap) {
  303. var queryHints = {};
  304. var suppression = this._getSuppression();
  305. if (suppression) {
  306. queryHints.suppress = suppression;
  307. }
  308. if (this._isMultiEdgeSortNeeded(layerToQuerySlotsMap)) {
  309. queryHints.multiEdgeSort = true;
  310. }
  311. var refreshProperty = this.content.getPropertyValue('queryRefresh');
  312. if (refreshProperty && refreshProperty.autoRefresh) {
  313. // Set the dataCacheExpiry to 40% of the refresh time
  314. var refreshValue = refreshProperty.value;
  315. var dataCacheExpiryValue = void 0;
  316. switch (refreshProperty.unit) {
  317. case 'seconds':
  318. dataCacheExpiryValue = parseInt(refreshValue * 4 / 10) || 1;
  319. break;
  320. case 'minutes':
  321. dataCacheExpiryValue = parseInt(refreshValue * 60 * 4 / 10) || 1;
  322. break;
  323. case 'hours':
  324. dataCacheExpiryValue = parseInt(refreshValue * 3600 * 4 / 10) || 1;
  325. break;
  326. default:
  327. dataCacheExpiryValue = 1;
  328. }
  329. queryHints.dataCacheExpiry = dataCacheExpiryValue.toString();
  330. queryHints.autoRefreshTime = '' + Date.now();
  331. }
  332. return queryHints;
  333. };
  334. return QueryProvider;
  335. }(), _class.DEFAULT_ROWLIMIT = 3000, _class.SUPPRESSION_NONE = 'none', _temp);
  336. return QueryProvider;
  337. });
  338. //# sourceMappingURL=QueryProvider.js.map