123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499 |
- 'use strict';
- /**
- * Licensed Materials - Property of IBM
- * IBM Cognos Products: Dashboard
- * (C) Copyright IBM Corp. 2013, 2019
- * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
- *
- * VisQueryBuilder
- * The VisQueryBuilder is responsible for turning the information in the dataMappingManager into a DSS query.
- */
- define(['underscore', '../../../lib/@waca/core-client/js/core-client/ui/core/Class', './VisQueryDataItemBuilder', './QueryFilterSpec', './VisQueryDefinition'], function (_, Class, VisQueryDataItemBuilder, QueryFilterSpec, VisQueryDefinition) {
- 'use strict';
- var VisQueryBuilder = null; // class declaration
- VisQueryBuilder = Class.extend({
- init: function init(attributes) {
- console.debug('visQueryBuilder (lw) init');
- VisQueryBuilder.inherited('init', this, arguments);
- _.forEach(attributes, function (attribute, key) {
- this[key] = attribute;
- }.bind(this));
- this.visQueryDataItemBuilder = new VisQueryDataItemBuilder({ visAPI: attributes.visAPI });
- },
- /**
- * create a visualization query
- * @param queryContext - the context of the query.
- * @returns the queryDefinition in a form that can be executed.
- */
- create: function create(queryContext) {
- var aMappedLayers = [];
- var oLayers = this._getQueryLayers();
- if (oLayers) {
- _.each(oLayers.getLayers(), function (layer) {
- if (this.visAPI.isMappingComplete(layer.id)) {
- aMappedLayers.push(layer);
- }
- }.bind(this));
- }
- if (aMappedLayers.length === 0) {
- aMappedLayers.push(this.visAPI.getDefaultLayer());
- }
- queryContext.visibleItemsMap = this._createVisibleItemsMapFromLiveWidgetModel();
- var aDataItems = this._createQueryDataItemsFromLiveWidgetModel(queryContext);
- var aProjections = this._createQueryProjectionsFromLiveWidgetModel(queryContext);
- //Mapping of all the projected data items (necessary for local filters)
- queryContext.projectedDataItemsMap = this._createVisibleItemsMapFromLiveWidgetModel(true);
- //Remove aFields, it should not be needed.
- var aFields = this._createQueryDefinitionFieldsFromLiveWidgetModel();
- var aFilters = this.createLocalFilters(queryContext, aMappedLayers);
- var aQueryHints = this._createQueryHints(queryContext);
- var oVisQueryDefinition = new VisQueryDefinition({ 'layers': aMappedLayers,
- 'layeredEntities': {
- 'dataItems': aDataItems,
- 'projections': aProjections,
- 'fields': aFields,
- 'filters': aFilters
- },
- 'commonEntities': {
- 'queryHints': aQueryHints
- } });
- return oVisQueryDefinition;
- },
- /**
- * Turn on multiEdgeSort based on following rules:
- * 1: if slot is isMultiMeasuresSeries and is Rank, does not consider as edge
- * 2: if slot is isMultiMeasuresSeries and is stacked, considered as one edge
- * 3: if slot is stacked, it is considered as one edge
- * 4: if slot is of type ordinal, does not consider as edge
- * if number of edges is more than one, then turn on multiEdgeSort otherwise not
- * @param queryHints the queryHint for query
- * @return {boolean} true if number of edges to sort is more than 1.
- * @private
- */
- _queryHintMultiEdgeSort: function _queryHintMultiEdgeSort(queryHints) {
- var _this = this;
- var numberOfEdges = 0;
- if (queryHints && !queryHints.multiEdgeSort) {
- return false;
- }
- _.forEach(this.visAPI.getDataSlots(), function (slotAPI) {
- if (_this._canQuery(slotAPI) && slotAPI.getFinalSlotType() !== 'ordinal' && slotAPI.getDataItemAPIs().length > 0) {
- numberOfEdges++;
- }
- });
- return numberOfEdges > 1;
- },
- _createQueryHints: function _createQueryHints(queryContext) {
- var queryHints = {};
- if (queryContext.entities && queryContext.entities.definition && queryContext.entities.definition.queryHints) {
- _.extend(queryHints, queryContext.entities.definition.queryHints);
- }
- if (queryContext.entities.preferredModelItems && queryContext.entities.preferredModelItems.length) {
- queryHints.preferredModelItems = queryContext.entities.preferredModelItems;
- }
- // Multi edge sort is only required by the main data query
- // and is not required by auxiliary queries such as summary queries (for performance reasons)
- if (queryContext.auxQuery || !this._queryHintMultiEdgeSort(queryHints)) {
- delete queryHints.multiEdgeSort;
- }
- if (queryContext.renderContext && queryContext.renderContext.extraInfo && queryContext.renderContext.extraInfo.sender === 'realtimeRefresh') {
- // Set the dataCacheExpiry to 40% of the refresh time
- var refreshValue = queryContext.renderContext.extraInfo.queryRefresh && queryContext.renderContext.extraInfo.queryRefresh.value;
- var dataCacheExpiryValue;
- switch (queryContext.renderContext.extraInfo.queryRefresh && queryContext.renderContext.extraInfo.queryRefresh.unit) {
- case 'seconds':
- dataCacheExpiryValue = parseInt(refreshValue * 4 / 10) || 1;
- break;
- case 'minutes':
- dataCacheExpiryValue = parseInt(refreshValue * 60 * 4 / 10) || 1;
- break;
- case 'hours':
- dataCacheExpiryValue = parseInt(refreshValue * 3600 * 4 / 10) || 1;
- break;
- default:
- dataCacheExpiryValue = 1;
- }
- queryHints.dataCacheExpiry = dataCacheExpiryValue.toString();
- } else {
- switch (queryContext.entities && queryContext.entities.properties && queryContext.entities.properties.localCache) {
- case 'yes':
- queryHints.dataCacheExpiry = '3600';
- break;
- case 'no':
- queryHints.dataCacheExpiry = '0';
- break;
- default:
- }
- }
- return [queryHints];
- },
- //TODO: Remove the function and sub functions when 'fields' is not needed in the flow for Endor
- _createQueryDefinitionFieldsFromLiveWidgetModel: function _createQueryDefinitionFieldsFromLiveWidgetModel() {
- var fields = [];
- var slotAPIs = this._getQuerySlots();
- _.each(slotAPIs, function (slotAPI) {
- fields = fields.concat(this._slotToFields(slotAPI));
- }.bind(this));
- return fields;
- },
- _createQueryDataItemsFromLiveWidgetModel: function _createQueryDataItemsFromLiveWidgetModel(queryContext) {
- var slotAPIs = this._getQuerySlots();
- // Set slot sort priority to highest data items sort priority plus 1.
- var priority = 0;
- var aDataItems = [];
- _.each(slotAPIs, function (slotAPI) {
- _.each(slotAPI.getDataItemAPIs(), function (dataItemAPI) {
- var sortObj = dataItemAPI.getSort();
- if (sortObj && priority < sortObj.priority) {
- priority = sortObj.priority;
- }
- });
- });
- this.visQueryDataItemBuilder.setQuerySlotSortPriority(priority + 1);
- var multiMeasures = this.visAPI.findSlotDataItem(function (dataItemAPI) {
- return dataItemAPI.getItemId() === '_multiMeasuresSeries';
- });
- // If multi measures are dropped to one slot, should not do client (local) sort.
- var serverSort = multiMeasures ? true : false;
- this.visQueryDataItemBuilder.setServerSort(serverSort);
- _.each(slotAPIs, function (slotAPI) {
- aDataItems = aDataItems.concat(this._slotToDataItems(slotAPI, queryContext));
- }.bind(this));
- if (queryContext && queryContext.queryOptions) {
- // list of dataitems to be added to the generated dataitems list
- if (queryContext.queryOptions.extraDataItems) {
- aDataItems = aDataItems.concat(queryContext.queryOptions.extraDataItems);
- }
- // list of dataitem ids to be removed from the generated dataitems list
- if (typeof queryContext.queryOptions.filterDataItems === 'function') {
- aDataItems = _.filter(aDataItems, queryContext.queryOptions.filterDataItems);
- }
- }
- return aDataItems;
- },
- /**
- * @param {boolean} includeAllDataItems As long as it can query, it will add the data items
- * This is for local filters, where we need all the project data items to pass.
- */
- //Use the slots and assigned dataItems to create a map of all visible items in this visualization.
- _createVisibleItemsMapFromLiveWidgetModel: function _createVisibleItemsMapFromLiveWidgetModel(includeAllDataItems) {
- var visibleItemsMap = {};
- this.visAPI.eachSlotDataItem(function (dataItemAPI, slotAPI) {
- // Exclude ordinal data items in visibleItemsMap.
- // Brushing should be converted to a filter when data item is in an ordinal slot type.
- // Adding isLatLong conditon for Lat/Ling data. It's a special type in that it's technically treated as categorical slots.
- if (this._canQuery(slotAPI) && (slotAPI.isLatLong() || slotAPI.getType() !== 'ordinal' || includeAllDataItems)) {
- visibleItemsMap[dataItemAPI.getItemId()] = visibleItemsMap[dataItemAPI.getItemId()] || [];
- var visibleItemInfo = { dataItemAPI: dataItemAPI, layerId: slotAPI.getLayerId(), viewId: slotAPI.getViewId() };
- visibleItemsMap[dataItemAPI.getItemId()].push(visibleItemInfo);
- }
- }.bind(this));
- return visibleItemsMap;
- },
- _createQueryProjectionsFromLiveWidgetModel: function _createQueryProjectionsFromLiveWidgetModel(queryContext) {
- var aProjections = [];
- var slotAPIs = this._getQuerySlots();
- _.each(slotAPIs, function (slotAPI) {
- aProjections = aProjections.concat(this._slotToProjections(slotAPI, queryContext));
- }.bind(this));
- if (queryContext && queryContext.queryOptions) {
- // list of projections to be added to the generated projections list
- if (queryContext.queryOptions.extraProjections) {
- aProjections = aProjections.concat(queryContext.queryOptions.extraProjections);
- }
- // list of projection ids to be removed from the generated projections list
- if (typeof queryContext.queryOptions.filterProjections === 'function') {
- aProjections = _.filter(aProjections, queryContext.queryOptions.filterProjections);
- }
- }
- return aProjections;
- },
- _createQueryFilterSpec: function _createQueryFilterSpec(visibleItemsMap) {
- return new QueryFilterSpec(visibleItemsMap);
- },
- //@returns the categorical items for the specified layer
- _getCategoryItemsForLayer: function _getCategoryItemsForLayer(layerId) {
- var slotAPIs = this._getQuerySlots();
- var catSlotsForLayer = _.filter(slotAPIs, function (slotAPI) {
- return slotAPI.getLayerId() === layerId && slotAPI.getFinalSlotType() === 'category';
- });
- //Extract the dataItemId's for each slot in this layer.
- var itemIdsForLayer = [];
- _.each(catSlotsForLayer, function (categorySlot) {
- _.each(categorySlot.getDataItemAPIs(), function (dataItemAPI) {
- itemIdsForLayer.push(dataItemAPI.getItemId());
- });
- });
- return itemIdsForLayer;
- },
- //@returns true if categorySet1 and categorySet2 are different (in both directions)
- _categoriesAreDifferent: function _categoriesAreDifferent(categorySet1, categorySet2) {
- return _.difference(categorySet1 || [], categorySet2 || []).length > 0 || _.difference(categorySet2 || [], categorySet1 || []).length > 0;
- },
- /**
- * Return the local query filters
- * @param queryContext
- * @param mappedLayers
- * @returns query filters
- */
- createLocalFilters: function createLocalFilters(queryContext, mappedLayers) {
- var _this2 = this;
- var queryFilters = [];
- if (_.isEmpty(queryContext)) {
- return queryFilters;
- }
- var excludeNonProjectedRangeFilters = false;
- var categoryItemsForFirstMappedLayer = null;
- _.each(mappedLayers, function (mappedLayer) {
- //NonProjected range filters cannot be applied to any layer whose categories are not the same as the first mapped layer
- //(because the aggregate of different categories is different.)
- var categoryItemsForThisLayer = mappedLayers.length > 1 && _this2._getCategoryItemsForLayer(mappedLayer.id) || [];
- categoryItemsForFirstMappedLayer = categoryItemsForFirstMappedLayer || categoryItemsForThisLayer;
- excludeNonProjectedRangeFilters = _this2._categoriesAreDifferent(categoryItemsForFirstMappedLayer, categoryItemsForThisLayer);
- var filtersConvertedToItemSelections = [];
- _.each(queryContext.filtersConvertedToDataItemSelections, function (excludedFilterEntry) {
- filtersConvertedToItemSelections.push(excludedFilterEntry.getId());
- });
- var queryFilterSpec = _this2._createQueryFilterSpec( /*projected data items*/queryContext.projectedDataItemsMap);
- if (queryContext.entities) {
- queryFilterSpec.addFiltersToSpec(queryContext.entities.localFilters, {
- edgeFilterExceptions: filtersConvertedToItemSelections,
- layerId: mappedLayer.getId(),
- excludeNonProjectedRangeFilters: excludeNonProjectedRangeFilters,
- shouldApplyIsNullValueExpression: _this2._shouldApplyIsNullValueExpression.bind(_this2),
- addExtraQueryDataItem: queryContext && queryContext.queryOptions && queryContext.queryOptions.addExtraQueryDataItem
- });
- queryFilterSpec.addFiltersToSpec(queryContext.entities.searchFilters, {
- edgeFilterExceptions: filtersConvertedToItemSelections
- });
- var layerFilters = queryFilterSpec.hasFilterSpec() ? queryFilterSpec.getFilterSpec() : null;
- if (layerFilters) {
- _.each(layerFilters, function (layerFilter) {
- layerFilter.layerId = mappedLayer.getId();
- queryFilters.push(layerFilter);
- });
- }
- }
- });
- if (queryContext.queryOptions && queryContext.queryOptions.extraFilters) {
- // list of filters to be added to the generated filters list
- queryFilters = queryFilters.concat(queryContext.queryOptions.extraFilters);
- }
- return queryFilters;
- },
- /**
- * @returns true if need to combine isnull in exclude filter (in FilterEntry._buildEdgeNullValueExpresstionQuerySpec)
- * 'filters': [
- {
- 'type': 'pre',
- 'expression': {
- 'or': [{
- 'itemId': 'someItem',
- 'operator': 'isnull'
- },
- {
- 'operator': 'notin',
- 'itemId': 'someItem',
- 'values': [
- 'XXX'
- ]
- }
- ]
- }
- }]
- *
- * The function should return false for an OLAP column
- */
- _shouldApplyIsNullValueExpression: function _shouldApplyIsNullValueExpression(filterEntry) {
- if (!filterEntry.columnId) {
- return false;
- }
- var column = this.visAPI.getMetadataColumn(filterEntry.columnId);
- return column && !column.isOlapColumn();
- },
- _canQuery: function _canQuery(slotAPI) {
- return !(slotAPI.isMultiMeasuresSeries() && !slotAPI.isStacked());
- },
- _getQuerySlots: function _getQuerySlots() {
- return _.filter(this.visAPI.getDataSlots(), function (slotAPI) {
- return this._canQuery(slotAPI);
- }.bind(this));
- },
- _getQueryLayers: function _getQueryLayers() {
- return this.visAPI.getLayers();
- },
- //TODO: Remove the function and sub functions when 'fields' is not needed in the flow for Endor
- _slotToFields: function _slotToFields(slotAPI) {
- //, bDoNotInverseSort) {
- var fields = [];
- _.each(slotAPI.getDataItemAPIs(), function (dataItemAPI, index) {
- if (!slotAPI.isMultiMeasuresSeriesOrValue(index)) {
- fields.push(this._slotItemToField(slotAPI, dataItemAPI));
- }
- }.bind(this));
- return fields;
- },
- /**
- * @private
- * @return {Array} An array of projection IDs of all items assigned to a single slot.
- **/
- _slotToProjections: function _slotToProjections(slotAPI, queryContext) {
- var projections = [];
- // filterProjections will be a function only for summary queries
- var filterProjections = queryContext && queryContext.queryOptions && queryContext.queryOptions.filterProjections;
- var isSummary = typeof filterProjections === 'function';
- var validDataItemIds = _.filter(slotAPI.getDataItemRefs(), function (id, index) {
- return !slotAPI.isMultiMeasuresSeriesOrValue(index);
- });
- // If the slot is stacked and if this is not a summary query, produce a single projection for the slot (using the slot id)
- if (slotAPI.isStacked() && validDataItemIds.length > 1 && !isSummary) {
- projections.push({
- id: slotAPI.getId(),
- layerId: slotAPI.getLayerId()
- });
- } else {
- _.each(validDataItemIds, function (dataItemId) {
- projections.push({
- id: dataItemId,
- layerId: slotAPI.getLayerId()
- });
- });
- }
- return projections;
- },
- /**
- * @private
- * @param slotAPI - the slotAPI of interest
- * @param queryContext - the context of the query.
- * @return {Array} An array of Data Item objects assigned to a single slot.
- **/
- _slotToDataItems: function _slotToDataItems(slotAPI, queryContext) {
- var aDataItems = [];
- var nestList = [];
- _.each(slotAPI.getDataItemAPIs(), function (dataItemAPI, index) {
- if (!slotAPI.isMultiMeasuresSeriesOrValue(index)) {
- aDataItems.push(this.visQueryDataItemBuilder.build(slotAPI, dataItemAPI, queryContext, index));
- //keep track of the list of dataItems assigned to a slot.
- //If a slot has multiple items and the slot is type stacked,
- //use this list as the nestList of a stacke
- nestList.push(dataItemAPI.getUniqueId());
- }
- }.bind(this));
- if (nestList.length > 1 && slotAPI.isStacked()) {
- //If the slot is stacked, produce a dataItem whose name matches the id
- //with nested items whose names match the dataItems.
- aDataItems.push({
- id: slotAPI.getId(),
- nest: nestList,
- layerId: slotAPI.getLayerId()
- });
- }
- return aDataItems;
- },
- /**
- * @returns the field equivalent of this dataslot.
- */
- //TODO: Remove the function and sub functions when 'fields' is not needed in the flow for Endor
- _slotItemToField: function _slotItemToField(slotAPI, dataItemAPI) {
- return {
- id: slotAPI.getId(),
- columnId: dataItemAPI.getItemId(),
- column: {
- isDateOrTimeType: function isDateOrTimeType() {
- return dataItemAPI.isDateOrTimeType();
- },
- getFormat: function getFormat() {
- return null; //Needed for summary view rendering.
- }
- },
- //sort: sortOrder,
- //limit: mapping ? mapping.limit : undefined,
- //sourceCategory: mapping ? mapping.getSourceCategory() : undefined,
- //filterException: this.getFilterException(),
- aggType: dataItemAPI.getAggregationType(),
- getType: function () {
- return this._getSlotType(slotAPI, dataItemAPI);
- }.bind(this),
- label: dataItemAPI.getLabel(),
- dataType: dataItemAPI.getDataType(),
- layerId: slotAPI.getLayerId()
- //useInTopBottomQueries: this.shouldBeUsedInTopBottomQueries(),
- //categorySubType: slot.getCategorySubType(),
- //ignoreSlotForViz: this.definition.ignoreSlotForViz,
- //postSortResultsDescending: this.postSortResultsDescending()
- };
- },
- /**
- * Returns the type of this slot as one of category, ordinal or 'any'.
- * Type 'any' will only be returned for unmapped slots
- * @returns the slot type. For slots of type 'any', the mapping will be used to determine the type.
- */
- _getSlotType: function _getSlotType(slotAPI, dataItemAPI) {
- if (slotAPI.getType() === 'any') {
- return dataItemAPI.getType() === 'attribute' ? 'category' : dataItemAPI.getType() === 'fact' ? 'ordinal' : 'category';
- }
- return slotAPI.getType();
- }
- });
- return VisQueryBuilder;
- });
- //# sourceMappingURL=VisQueryBuilder.js.map
|