123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290 |
- '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
|