'use strict'; /** * Licensed Materials - Property of IBM * IBM Cognos Products: BI Cloud (C) Copyright IBM Corp. 2017, 2019 * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp. * * VisQueryDataItemBuilder * The VisQueryDataItemBuilder is responsible for turning the information in a dataItem into a dss query. */ define(['underscore', '../../../lib/@waca/core-client/js/core-client/ui/core/Class', './postprocess/LocalAggregateSortInfo'], function (_, Class, LocalAggregateSortInfo) { 'use strict'; // noinspection JSAnnotator /** * INTENT: VisQueryDataItemBuilder is used to construct a dataItem in the form needed by the query. * building the dataItem involves a number of 'on the fly' steps such as: * inserting the appropriate initial selection based on the current drill state, * deciding if a filter should be a dataItem selection * ensuring that selections are performed in a predictable order (initial selection (root members or drill members), keep/remove, topN, sort) */ var MAX_DECIMAL_PLACES = 2; var VisQueryDataItemBuilder = Class.extend({ init: function init(attributes) { VisQueryDataItemBuilder.inherited('init', this, arguments); this.visAPI = attributes.visAPI; this._querySlotSortPriority = 0; this._localAggregateSortInfo = attributes.localAggregateSortInfo || new LocalAggregateSortInfo(attributes.visAPI); }, setQuerySlotSortPriority: function setQuerySlotSortPriority(priority) { this._querySlotSortPriority = priority; }, /** * @return serverSort boolean true to use server sort. */ getServerSort: function getServerSort() { return this._serverSort; }, /** * @param serverSort true to use server sort. */ setServerSort: function setServerSort(serverSort) { this._serverSort = serverSort; }, /* * @private * @return {Array} An array of Data Item objects assigned to a single slot. **/ build: function build(slotAPI, dataItemAPI, queryContext, dataItemIdx) { var oDataItemJSON = dataItemAPI.toQueryJSON(); oDataItemJSON.layerId = slotAPI.getLayerId(); if (slotAPI.getFinalSlotType(dataItemIdx) === 'ordinal') { // If the final slot type is ordinal, it means we are querying the data item // in a ordinal form, which in this case we want to respect the aggregation type // except 'none' oDataItemJSON.aggregate = slotAPI.getFinalAggregationType(dataItemIdx); } else { delete oDataItemJSON.aggregate; } var finalSelection = this._buildFinalItemSelections(slotAPI, dataItemAPI, queryContext, oDataItemJSON); if (finalSelection.length) { oDataItemJSON.selection = finalSelection; } else if (oDataItemJSON.selection && oDataItemJSON.selection.length === 0) { // avoid having an empty selection in query spec delete oDataItemJSON.selection; } var binning = this._buildBinningSpec(dataItemAPI); //binned dataItem type will be attribute, so if it has aggregate we need to remove it. // binningDefinition is part of toQueryJSON (need for copy/past), but dss query has different format if (binning) { //remove old selection, since it may have top/bottom if (!finalSelection.length) { delete oDataItemJSON.selection; } delete oDataItemJSON.binningDefinition; delete oDataItemJSON.aggregate; oDataItemJSON.binning = binning; } return oDataItemJSON; }, _buildBinningSpec: function _buildBinningSpec(dataItemAPI) { var binningAPI = dataItemAPI.getBinningAPI(); if (binningAPI) { var format = dataItemAPI.getFormat(); var maxDecPlaces = format && (format.formatSpec.maximumFractionDigits || format.formatSpec.maximumFractionDigits === 0) ? format.formatSpec.maximumFractionDigits : MAX_DECIMAL_PLACES; binningAPI.setMaxDecPlaces(maxDecPlaces); return binningAPI.getBinning(); } return null; }, //unless selections are pre-defined and locked (WA2 upgrade), add selection actions in a specific order //to ensure that the results are predictable and explainable! _buildFinalItemSelections: function _buildFinalItemSelections(slotAPI, dataItemAPI, queryContext, oDataItemJSON) { //TODO: For WA2, the dataItemSelections will be locked. In that case we need to honour the selections from the dataItem 'as is'. if (!dataItemAPI.isBinned()) { //By default, return selections in the order agreed to by PM. return this._buildDrillStateForHierarchyOrSet(dataItemAPI, queryContext, oDataItemJSON).concat(this._buildItemSelectionsFromFilterCollections(dataItemAPI, queryContext), this._buildTopBottom(dataItemAPI), this._buildSort(slotAPI, dataItemAPI)); } else { return this._buildItemSelectionsFromFilterCollections(dataItemAPI, queryContext).concat(this._buildSort(slotAPI, dataItemAPI)); } }, //For hierarchies or OLAP set, build one of root members, children of single root or children of current drill member. _buildDrillStateForHierarchyOrSet: function _buildDrillStateForHierarchyOrSet(dataItemAPI, queryContext, oDataItemJSON) { var localFilters = queryContext && queryContext.entities && queryContext.entities.localFilters; var matchingLocalFilterEntry = localFilters && localFilters.getFilterEntry({ id: dataItemAPI.getItemId(), columnId: dataItemAPI.getItemId() }); var overrideHierarchyDefaultSelection = matchingLocalFilterEntry && matchingLocalFilterEntry.overrideHierarchyDefaultSelection; var selection = []; if (dataItemAPI.isHierarchy() || dataItemAPI.isNamedSet()) { var existingDrillSelection = dataItemAPI.getDrillSelection(); if (existingDrillSelection) { if (dataItemAPI.isNamedSet()) { oDataItemJSON.itemId = dataItemAPI.getRefToHierarchy(); } selection.push(existingDrillSelection); } else if (dataItemAPI.isHierarchy() && !overrideHierarchyDefaultSelection) { if (dataItemAPI.isSingleRootHierarchy()) { selection.push({ 'operation': 'add', 'children': dataItemAPI.getRootMember() }); } else { selection.push({ 'operation': 'add', 'rootMembers': true }); } } } return selection; }, _convertFilterOperatorToSetOperation: function _convertFilterOperatorToSetOperation(operator) { switch (operator) { case 'in': case 'containsignorecase': return 'keep'; case 'notin': return 'remove'; default: return; } }, _convertFilterEntryToSelection: function _convertFilterEntryToSelection(filterEntry) { var discreteOperation = this._convertFilterOperatorToSetOperation(filterEntry.operator); var result = null; if (discreteOperation) { var valuesArray = _.map(filterEntry.values.models, function (valueEntry) { return valueEntry.value.u ? valueEntry.value.u : valueEntry.value; }); result = { operation: discreteOperation }; if (filterEntry.operator === 'containsignorecase') { result.filter = { operator: filterEntry.operator, values: valuesArray }; } else { result.set = valuesArray; } } return result; }, _convertItemFiltersToSelections: function _convertItemFiltersToSelections(filters, queryContext, selections) { var _this = this; queryContext.filtersConvertedToDataItemSelections = queryContext.filtersConvertedToDataItemSelections || []; _.each(filters, function (filterEntry) { if (filterEntry && filterEntry.values.models.length > 0 && !filterEntry.conditions) { var selectionItem = _this._convertFilterEntryToSelection(filterEntry); if (selectionItem) { selections.push(selectionItem); } //Track which filters were converted to dataItemSelections (so we don't filter on them as well!) queryContext.filtersConvertedToDataItemSelections.push(filterEntry); } }); }, /** * Convert Filters into Selections. * * In cases where the dataItem has facetData enabled (most of the time) * AND there is a filter on that dataItem, convert the filter to a set selection on the dataItem. * Note: this ensures that a contextual topN on an item will return N members from the remaining set * EXAMPLE: top2 Products based on Revenue * Product1 $1000 * Product2 $2000 * Product3 $1500 * Product4 $1700 * Keep Product1, Product3, Product4 * setSelect Result = $1700, $1500 (the top2 of the selected set ==> which is what most people would expect) * Filter Result = $1700 (because only Product4 is in the Top2 of the entire set) * **/ _buildItemSelectionsFromFilterCollections: function _buildItemSelectionsFromFilterCollections(dataItemAPI, queryContext) { var dataItemId = dataItemAPI.getItemId(); var localFiltersAPI = queryContext && queryContext.entities && queryContext.entities.localFilters; var searchFiltersAPI = queryContext && queryContext.entities && queryContext.entities.searchFilters; var existingSetSelection = dataItemAPI.setSelectionsToQueryJSON(); var selections = existingSetSelection ? existingSetSelection : []; var isAuxQuery = queryContext && queryContext.auxQuery; if (!isAuxQuery && dataItemAPI.isDimensional()) { if (localFiltersAPI) { this._convertItemFiltersToSelections(localFiltersAPI.getFilterEntries({ id: dataItemId }), queryContext, selections); } if (searchFiltersAPI) { this._convertItemFiltersToSelections(searchFiltersAPI.getFilterEntries({ id: dataItemId }), queryContext, selections); } } return selections; }, _buildTopBottom: function _buildTopBottom(dataItemAPI) { var selection = []; var topBottom = dataItemAPI.getTopBottomSelection(); if (topBottom) { selection.push(topBottom); } return selection; }, _buildSort: function _buildSort(slotAPI, dataItemAPI) { var bClientSortEnabled = this.getServerSort() ? false : this._isClientSideSortEnabledForDataItem(dataItemAPI); var selection = []; if (!bClientSortEnabled) { // 1. Sort from data item. var sort = dataItemAPI.getSort(); var sortObj = sort && sort.type ? sort : null; var ignoreSlotSort = this.visAPI.isIgnoreDefaultSlotSort(dataItemAPI); // Preserve the hierarchical order for Olap column. if (!sortObj && !ignoreSlotSort) { sortObj = dataItemAPI.getDefaultSort(slotAPI, dataItemAPI); if (sortObj) { sortObj.priority = this._querySlotSortPriority++; } } if (sortObj) { var sortSelection = { 'operation': 'order', 'sort': _.pick(sortObj, 'type', 'by', 'priority') }; if (sortObj.context) { sortSelection.context = [{ itemId: sortObj.context }]; } selection = [sortSelection]; } } return selection; }, /** * If there will be any post processing client side sorting on the dataItem * optimize to not sort on server as well. */ _isClientSideSortEnabledForDataItem: function _isClientSideSortEnabledForDataItem(dataItemAPI) { var localSortInfo = this._localAggregateSortInfo.getSortInfo(); if (localSortInfo) { return localSortInfo.aggregateColumn === dataItemAPI.getUniqueId(); } return false; } }); return VisQueryDataItemBuilder; }); //# sourceMappingURL=VisQueryDataItemBuilder.js.map