MetadataColumn.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528
  1. 'use strict';
  2. /**
  3. * Licensed Materials - Property of IBM
  4. * IBM Watson Analytics (C) Copyright IBM Corp. 2017, 2020
  5. * US Government Users Restricted Rights - Use, duplication or disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
  6. */
  7. define(['../../lib/@waca/core-client/js/core-client/ui/core/Class', 'bi/moser/moser.min', '../../lib/@waca/core-client/js/core-client/i18n/V5Formatter'], function (Class, MoserJS, FormatUtils) {
  8. 'use strict';
  9. var UNKNOWN_AGGREGATE_TYPE = 'unknown';
  10. var DATA_TYPE_MAP = {
  11. 'BIGINT': 'integer',
  12. 'CHAR(': 'string',
  13. 'DATE': 'date',
  14. 'DATETIME': 'datetime',
  15. 'DECIMAL': 'decimal',
  16. 'DOUBLE': 'double',
  17. 'FLOAT': 'float',
  18. 'INTEGER': 'integer',
  19. 'NCHAR': 'string',
  20. 'NVARCHAR(': 'string',
  21. 'SMALLINT': 'integer',
  22. 'TIMESTAMP': 'datetime',
  23. 'TIME': 'time',
  24. 'TINYINT': 'integer',
  25. 'LONG': 'integer',
  26. 'VARCHAR(': 'string',
  27. 'STRING': 'string'
  28. };
  29. var SORT_ASCENDING = 'asc';
  30. var SORT_DESCENDING = 'desc';
  31. var MetadataColumn = Class.extend({
  32. /**
  33. * @param {Object} options.moserJSColumn
  34. */
  35. init: function init(options) {
  36. MetadataColumn.inherited('init', this, arguments);
  37. this.moserObject = options.moserObject;
  38. this.sourceId = options.sourceId;
  39. this.queryService = options.queryService;
  40. this.propertyOverride = this.getPropertyOverride();
  41. Object.defineProperty(this, 'type', {
  42. get: function get() {
  43. return this.getType();
  44. }
  45. });
  46. Object.defineProperty(this, 'dataType', {
  47. get: function get() {
  48. return this.getDataType();
  49. }
  50. });
  51. Object.defineProperty(this, 'label', {
  52. get: function get() {
  53. return this.getLabel();
  54. }
  55. });
  56. // leaf and non-leaf member cache used to avoid second getChildren() call for a member within same session.
  57. this._leafMemberCache = [];
  58. this._nonLeafMemberCache = [];
  59. },
  60. /**
  61. * Returns the metadata column unique identifer
  62. * @return {String} The identifier used for metadata column expression
  63. */
  64. getId: function getId() {
  65. return this.moserObject.getIdForExpression();
  66. },
  67. /**
  68. * Returns the metadata column label
  69. * @return {String} The metadata column display label
  70. */
  71. getLabel: function getLabel() {
  72. return this.moserObject.getLabel();
  73. },
  74. /**
  75. * Set the metadata column label
  76. * @param {String} label The metadata column display label
  77. */
  78. setLabel: function setLabel(label) {
  79. this.moserObject.setLabel(label);
  80. },
  81. /**
  82. * Determines whether the metadata column is hidden
  83. * @return {Booelan} true if metadata column is hidden, otherwise false
  84. */
  85. isHidden: function isHidden() {
  86. return this.moserObject.isHidden();
  87. },
  88. /**
  89. * Set the metadata column as hidden
  90. * @param {Booelan} isHidden true if metadata column is hidden, otherwise false
  91. */
  92. setHidden: function setHidden(isHidden) {
  93. this.moserObject.setHidden(isHidden);
  94. },
  95. /**
  96. * Returns the metadata column object type
  97. * @return {String} The metadata column object type
  98. */
  99. getObjectType: function getObjectType() {
  100. return this.moserObject.getObjectType();
  101. },
  102. /**
  103. * Returns the internal spec ID of the source
  104. * @return {String} The internal id used to map a widget to the source it's using
  105. */
  106. getSourceId: function getSourceId() {
  107. return this.sourceId;
  108. },
  109. /**
  110. * Returns the internal spec ID of the source
  111. * @return {String} The internal id used to map a widget to the source it's using
  112. */
  113. getFacetDefinition: function getFacetDefinition() {
  114. return this.moserObject.facetDefinition;
  115. },
  116. // returns true if is WA2 style hierarchy, i.e. user generated hiearchy from UI.
  117. isItemHierarchy: function isItemHierarchy() {
  118. return this.moserObject.getObjectType() === MoserJS['default'].MoserObjectTypes.ITEM_HIERARCHY;
  119. },
  120. // Returns true if metadata columns is missing in datasource.
  121. isMissing: function isMissing() {
  122. return this.moserObject.isMissing && this.moserObject.isMissing();
  123. },
  124. // returns true if is olap hierarchy.
  125. isHierarchy: function isHierarchy() {
  126. return this.getSourceCategory() === 'hierarchy/level' || this.getSourceCategory() === 'hierarchy/parent-child';
  127. },
  128. // returns true if isItemHierarchy or isHierarchy.
  129. hasMemberTree: function hasMemberTree() {
  130. return this.isItemHierarchy() || this.isHierarchy();
  131. },
  132. isSingleRootHierarchy: function isSingleRootHierarchy() {
  133. return !this.moserObject.isHierarchyHasMultipleRoots();
  134. },
  135. getRootMember: function getRootMember() {
  136. return this.moserObject.getRootMember();
  137. },
  138. // returns true if is olap level.
  139. isLevel: function isLevel() {
  140. return this.getSourceCategory() === 'level';
  141. },
  142. // returns true if is property.
  143. isProperty: function isProperty() {
  144. return this.getSourceCategory() === 'property';
  145. },
  146. // returns true if is namedSet.
  147. isNamedSet: function isNamedSet() {
  148. return this.getSourceCategory() === 'namedSet';
  149. },
  150. /**
  151. * Get the type of the column
  152. * @return {String} Returns eitehr 'fact' or 'attribute' as the column type. Null if the moserObject doesn't have a type
  153. */
  154. getType: function getType() {
  155. if (!this.moserObject.getUsage) {
  156. return null;
  157. }
  158. if (this.moserObject.getUsage() === MoserJS['default'].UsageType.FACT) {
  159. return 'fact';
  160. } else {
  161. return 'attribute';
  162. }
  163. },
  164. /**
  165. * @return the source category of the column, such as 'hierarchy', or 'level', etc, for relational type of dataset, this should return as 'column'
  166. **/
  167. getSourceCategory: function getSourceCategory() {
  168. if (!this.isMissing() && this.moserObject.getSourceCategory && this.moserObject.getSourceCategory() !== null) {
  169. return this.moserObject.getSourceCategory().value();
  170. }
  171. return null;
  172. },
  173. /**
  174. * Returns the data type of the column
  175. * @return {[type]} [description]
  176. */
  177. getDataType: function getDataType() {
  178. var dataType = void 0;
  179. // Should call getHighlevelDatatype for generic data types
  180. if (this.moserObject.getHighlevelDatatype) {
  181. var oHighLevelDataType = this.moserObject.getHighlevelDatatype();
  182. dataType = oHighLevelDataType && oHighLevelDataType.value();
  183. }
  184. if (!dataType || !dataType.toUpperCase /** if result is not a string */) {
  185. // If nothing provided from getHighlevelDatatype, follow the old logic to call getDatatype api
  186. dataType = this.moserObject.getDatatype ? this.moserObject.getDatatype() : null;
  187. }
  188. dataType = dataType ? dataType.toUpperCase() : null;
  189. if (dataType && DATA_TYPE_MAP[dataType]) {
  190. return DATA_TYPE_MAP[dataType];
  191. }
  192. return this.getType() === 'fact' ? 'decimal' : 'string';
  193. },
  194. /**
  195. * @param optionalOverrideDataType - takes an optional datatype to check if it is a date or time, if null will use the Column dataType
  196. * @returns true if this item is a date, datetime, time or year.
  197. */
  198. isDateOrTimeType: function isDateOrTimeType() {
  199. var dataType = this.getDataType();
  200. return dataType === 'date' || dataType === 'datetime' || dataType === 'time' || dataType === 'year';
  201. },
  202. getConcepts: function getConcepts() {
  203. var concepts = this.moserObject.getConcepts ? this.moserObject.getConcepts() : null;
  204. if (concepts) {
  205. return concepts.slice();
  206. }
  207. return null;
  208. },
  209. getTaxonomy: function getTaxonomy() {
  210. var taxonomy = this.moserObject.basicGetTaxonomy ? this.moserObject.basicGetTaxonomy() : null;
  211. if (taxonomy && taxonomy.length > 0) {
  212. if (this.isSupportedTaxonomy(taxonomy[0])) {
  213. return taxonomy.slice();
  214. } else {
  215. // Moser returns only 1 taxonomy element for each data item
  216. return null;
  217. }
  218. }
  219. return null;
  220. },
  221. /**
  222. * Some columns, depending on their meta data should not allow search
  223. * or filter conditions to be set. For example, lat and long columns.
  224. * @returns if a metadata column should allow filter conditions and search.
  225. */
  226. supportsConditionFilters: function supportsConditionFilters() {
  227. if (this.isNamedSet()) {
  228. return false;
  229. }
  230. var taxonomy = this.getTaxonomy();
  231. var supported = true;
  232. if (taxonomy) {
  233. taxonomy.forEach(function (taxonomyItem) {
  234. var family = taxonomyItem.getFamily();
  235. if (family === 'cLatitude' || family === 'cLongitude') {
  236. supported = false;
  237. }
  238. });
  239. }
  240. return supported;
  241. },
  242. // In R9, we support everything, except coordinates (family= cPosition)
  243. isSupportedTaxonomy: function isSupportedTaxonomy(taxonomy) {
  244. return taxonomy.getClazz() === 'cGeoLocation' && taxonomy.getFamily() === 'cPosition' ? false : true;
  245. },
  246. getDefaultAggregation: function getDefaultAggregation() {
  247. if (!this._defaultAggregation) {
  248. var regularAggregate = this.moserObject.getRegularAggregate && this.moserObject.getRegularAggregate() ? this.moserObject.getRegularAggregate().value() : null;
  249. this._defaultAggregation = MetadataColumn.AGGREGATION_TYPE_MAP[regularAggregate] || UNKNOWN_AGGREGATE_TYPE;
  250. // Default to 'count' aggregation type for attribute that has unsupported aggregation types
  251. if (MetadataColumn.SUPPORTED_AGGREGATION_FOR_ATTRIBUTE.indexOf(this._defaultAggregation) === -1 && this.getType() === 'attribute') {
  252. this._defaultAggregation = MetadataColumn.AGGREGATION_TYPE_MAP.countDistinct;
  253. }
  254. }
  255. return this._defaultAggregation;
  256. },
  257. getAggregateMode: function getAggregateMode() {
  258. if (this.moserObject.getAggregateMode) {
  259. var aggregateMode = this.moserObject.getAggregateMode();
  260. return aggregateMode ? aggregateMode.value() : null;
  261. }
  262. return null;
  263. },
  264. isFilter: function isFilter() {
  265. return this.moserObject.getObjectType() === MoserJS['default'].MoserObjectTypes.FILTER;
  266. },
  267. /**
  268. * @description Return the data format for this metadata item.
  269. * @function MetadataColumn#getFormat
  270. * @public
  271. *
  272. * @param {Object} options ignoreDefaultFormatting - Ignore any default formatting
  273. * @return {FormatSpec} return format spec
  274. */
  275. getFormat: function getFormat() {
  276. return FormatUtils.getFormatSpec(this.moserObject.getFormat && this.moserObject.getFormat());
  277. },
  278. /**
  279. * Returns the refToHierarchy of the moser object, e.g. refToHierarchy of a namedSet object
  280. * is the hierarchy the namedSet belongs to.
  281. * @return {string} hierarchy name.
  282. */
  283. getRefToHierarchy: function getRefToHierarchy() {
  284. return this.moserObject.getRefToHierarchy ? this.moserObject.getRefToHierarchy() : null;
  285. },
  286. /**
  287. * @param {Boolean} [isOlapPackage], Default False
  288. * @return {Boolean} True, if the column is regarded as a OLAP column
  289. */
  290. isOlapColumn: function isOlapColumn() {
  291. var isOlapPackage = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false;
  292. var sourceCategory = this.getSourceCategory();
  293. // For datasource of Module that is built on top of OLAP packages, stand alone calculation or folder is not considered as OLAP column in this function.
  294. // For datasource of OLAP package directly, calcualtion is considered as OLAP column
  295. var isOlapCalculation = isOlapPackage && this.getObjectType() === 'Calculation';
  296. return this.isNamedSet() || isOlapCalculation || sourceCategory && sourceCategory !== 'column' && this.getObjectType() === 'QueryItem';
  297. },
  298. getParent: function getParent() {
  299. var parent = this.moserObject.getParent();
  300. while (parent && parent.getObjectType() === MoserJS['default'].MoserObjectTypes.ITEM_TYPE) {
  301. parent = parent.getParent();
  302. }
  303. return parent ? new MetadataColumn({
  304. moserObject: parent,
  305. sourceId: this.sourceId
  306. }) : null;
  307. },
  308. isEditableCalculation: function isEditableCalculation() {
  309. return !this.moserObject.isImported() && (this.getObjectType() === MoserJS['default'].MoserObjectTypes.CALCULATION || this.getObjectType() === MoserJS['default'].MoserObjectTypes.QUERY_ITEM);
  310. },
  311. getDefaultSortType: function getDefaultSortType() {
  312. var facetDefinition = this.moserObject.getFacetDefinition ? this.moserObject.getFacetDefinition() : null;
  313. if (!facetDefinition || !facetDefinition.enabled || facetDefinition.enabled && (facetDefinition.enabled.enumValue === 'false' || facetDefinition.enabled.enumValue === 'automatic')) {
  314. return null;
  315. }
  316. var sortList = facetDefinition.getSortList();
  317. if (sortList) {
  318. var sortItems = sortList.basicGetSortItem();
  319. if (sortItems && sortItems.length > 0) {
  320. // MUI doesn't allow to set many columns in sort list but Moser schema allows.
  321. // Take the order of the first sort item
  322. var sortItem = sortItems[0];
  323. if (sortItem.order.enumValue === 'descending') {
  324. return SORT_DESCENDING;
  325. } else if (sortItem.order.enumValue === 'ascending') {
  326. return SORT_ASCENDING;
  327. } else {
  328. return null;
  329. }
  330. }
  331. }
  332. return null;
  333. },
  334. /**
  335. * @return true if it's a leaf member by checking the cached leaf members.
  336. */
  337. isCachedLeafMember: function isCachedLeafMember(mun) {
  338. return this._leafMemberCache.indexOf(mun) > -1;
  339. },
  340. /**
  341. * @return true if it's not a leaf member by checking the cached non-leaf members.
  342. */
  343. isCachedNonLeafMember: function isCachedNonLeafMember(mun) {
  344. return this._nonLeafMemberCache.indexOf(mun) > -1;
  345. },
  346. cacheMember: function cacheMember(mun, isLeafMember) {
  347. if (isLeafMember) {
  348. this._leafMemberCache.push(mun);
  349. } else {
  350. this._nonLeafMemberCache.push(mun);
  351. }
  352. },
  353. isLeafMember: function isLeafMember(memberId) {
  354. var _this = this;
  355. if (this.isCachedLeafMember(memberId)) {
  356. return Promise.resolve(true);
  357. }
  358. if (this.isCachedNonLeafMember(memberId)) {
  359. return Promise.resolve(false);
  360. }
  361. return this.queryService.getChildren(this.sourceId, [this.getId()], memberId, []).then(function (queryResult) {
  362. var isLeafMember = queryResult.getRowCount() === 0;
  363. _this.cacheMember(memberId, isLeafMember);
  364. return isLeafMember;
  365. });
  366. },
  367. getValues: function getValues() {
  368. return this.values;
  369. },
  370. setValues: function setValues(values) {
  371. this.values = values;
  372. },
  373. /**
  374. * Returns the table name where this metadata column is defined in
  375. *
  376. * @return {string} the retrieved table name
  377. */
  378. getTableName: function getTableName() {
  379. var table = this.getTable();
  380. return table ? table.getIdentifier() : '';
  381. },
  382. /**
  383. * Returns the table where this metadata column belong
  384. *
  385. * @return {object} return valid table object if one found else return undefined
  386. */
  387. getTable: function getTable() {
  388. if (!this.table) {
  389. var parent = this.moserObject.getParent();
  390. while (parent) {
  391. if (parent.getObjectType() === MoserJS.default.MoserObjectTypes.QUERY_SUBJECT) {
  392. this.table = parent;
  393. break;
  394. }
  395. parent = parent.getParent();
  396. }
  397. }
  398. return this.table;
  399. },
  400. isNumericDataType: function isNumericDataType() {
  401. var type = this.getDataType();
  402. return ['string', 'attribute', 'date', 'time', 'datetime'].indexOf(type) === -1;
  403. },
  404. isDateTimeDataType: function isDateTimeDataType() {
  405. var type = this.getDataType();
  406. return ['date', 'time', 'datetime'].indexOf(type) !== -1;
  407. },
  408. /*
  409. * @return {string array} an array of properties that had been overriden
  410. */
  411. getPropertyOverride: function getPropertyOverride() {
  412. return this.moserObject.getPropertyOverride().filter(function (property) {
  413. return property.indexOf('property') === -1;
  414. }).map(function (property) {
  415. return { property: property, value: this.getProperty(property) };
  416. }.bind(this));
  417. },
  418. getProperty: function getProperty(propertyName) {
  419. if (this.moserObject[propertyName] && this.moserObject[propertyName].value) {
  420. return this.moserObject[propertyName].value();
  421. }
  422. return this.moserObject[propertyName];
  423. },
  424. getMoserObject: function getMoserObject() {
  425. return this.moserObject;
  426. },
  427. /**
  428. * Compatible with MetadataColumnAPI#getHierarchyLevelIds
  429. * @description Returns the levelIds of the hierarchy in natural order.
  430. *
  431. * @return {String[]} an array of levelIds
  432. */
  433. getHierarchyLevelIds: function getHierarchyLevelIds() {
  434. return this.moserObject.getItem().filter(function (item) {
  435. return item.queryItem;
  436. }).map(function (item) {
  437. return item.queryItem.getIdForExpression();
  438. }) || [];
  439. }
  440. });
  441. MetadataColumn.AGGREGATION_TYPE_MAP = {
  442. none: 'none',
  443. average: 'avg',
  444. automatic: 'automatic',
  445. calculated: 'calculated',
  446. count: 'count',
  447. countDistinct: 'countdistinct',
  448. maximum: 'max',
  449. median: 'median',
  450. minimum: 'min',
  451. standardDeviation: 'stddev',
  452. total: 'sum',
  453. variance: 'variance'
  454. };
  455. // Currently we only support none, count and countdistinct
  456. MetadataColumn.SUPPORTED_AGGREGATION_FOR_ATTRIBUTE = [MetadataColumn.AGGREGATION_TYPE_MAP.none, MetadataColumn.AGGREGATION_TYPE_MAP.count, MetadataColumn.AGGREGATION_TYPE_MAP.countDistinct];
  457. //The set of aggregation types that are valid for fact type columns
  458. MetadataColumn.SUPPORTED_AGGREGATION_FOR_FACT = [MetadataColumn.AGGREGATION_TYPE_MAP.average, MetadataColumn.AGGREGATION_TYPE_MAP.total, MetadataColumn.AGGREGATION_TYPE_MAP.minimum, MetadataColumn.AGGREGATION_TYPE_MAP.maximum, MetadataColumn.AGGREGATION_TYPE_MAP.count, MetadataColumn.AGGREGATION_TYPE_MAP.countDistinct];
  459. return MetadataColumn;
  460. });
  461. //# sourceMappingURL=MetadataColumn.js.map