/** * The query class for data fetching. */ Ext.define("Terrasoft.data.queries.EntitySchemaQuery", { extend: "Terrasoft.data.queries.BaseFilterableQuery", alternateClassName: "Terrasoft.EntitySchemaQuery", statics: { /** * Cache sample results. * @type {Object} */ cache: {}, /** * Clears the client cache of the results of the samples. * @param {String[]|String} [keys] The key or array of cache entry keys to be freed. * If not specified, the cache is completely cleared. */ clearCache: function (keys) { if (!keys) { this.cache = {}; return; } if (!Ext.isArray(keys)) { keys = [keys]; } keys.forEach(function (key) { delete this.cache[key]; }, this); } }, /** * A collection of query columns. * @type {Terrasoft.QueryColumns} */ columns: null, /** * Determines if all columns must be queried. * @type {Boolean} */ allColumns: false, /** * Determines if merge duplicates in query result. * @type {Boolean} */ isDistinct: false, /** * Rows count to query. All rows will be queried by default. * @type {Number} */ rowCount: -1, /** * Rows count to skip. * @type {Number} */ rowsOffset: -1, /** * The name of the primary column by default. * @type {String} */ defaultPrimaryColumnName: "Id", /** * A characteristic of a page-by-page sampling of data. * @type {Boolean} */ isPageable: false, /** * Conditions for constructing a page request. * @type {Terrasoft.ColumnValues} */ conditionalValues: null, /** * A characteristic of a hierarchical selection of data. * @type {Boolean} */ isHierarchical: false, /** * The maximum level of nesting a hierarchical query. * @type {Number} */ hierarchicalMaxDepth: 0, /** * The name of the column used to build the hierarchical query. * @type {String} */ hierarchicalColumnName: "", /** * The initial value of the hierarchical column from which the hierarchy will be built. * @type {String} */ hierarchicalColumnValue: "", /** * The parameter that determines whether to use the localized data. * @type {Boolean} */ useLocalization: true, /** * Represent parameter which determines disabling data in filtering. * @type {Boolean} */ useRecordDeactivation: false, /** * Name of the model class of the representation of the returned object based on the query results. * @type {String} */ rowViewModelClassName: "Terrasoft.BaseViewModel", /** * @property {Object} serverESQCacheParameters ESQ caching parameters on the server. * @property {Terrasoft.ESQServerCacheLevels} serverESQCacheParameters.cacheLevel Level of ESQ cache placement * It is indicated on the basis of Terrasoft.ESQServerCacheLevels. * @property {String} serverESQCacheParameters.cacheGroup Caching group. * @property {String} serverESQCacheParameters.cacheItemName The record key in the repository. */ serverESQCacheParameters: null, /** * @property {Object} clientESQCacheParameters ESQ caching parameters on the client. * @property {String} clientESQCacheParameters.cacheItemName The record key in the repository. */ clientESQCacheParameters: null, /** * @inheritdoc Terrasoft.BaseObject#constructor * @override * @param {Object|String} config Config object or root schema name. */ constructor: function (config) { this.callParent(arguments); this.serverESQCacheParameters = { cacheLevel: Terrasoft.ESQServerCacheLevels.SESSION, cacheGroup: "", cacheItemName: "" }; if (!Ext.Object.isEmpty(config) && config.hasOwnProperty("serverESQCacheParameters")) { var serverESQCacheParameters = config.serverESQCacheParameters; if (serverESQCacheParameters.hasOwnProperty("cacheLevel")) { this.serverESQCacheParameters.cacheLevel = serverESQCacheParameters.cacheLevel; } if (serverESQCacheParameters.hasOwnProperty("cacheGroup")) { this.serverESQCacheParameters.cacheGroup = serverESQCacheParameters.cacheGroup; } if (serverESQCacheParameters.hasOwnProperty("cacheItemName")) { this.serverESQCacheParameters.cacheItemName = serverESQCacheParameters.cacheItemName; } } this.clientESQCacheParameters = { cacheItemName: "" }; if (!Ext.Object.isEmpty(config) && config.hasOwnProperty("clientESQCacheParameters")) { var clientESQCacheParameters = config.clientESQCacheParameters; if (clientESQCacheParameters.hasOwnProperty("cacheItemName")) { this.clientESQCacheParameters.cacheItemName = clientESQCacheParameters.cacheItemName; } } this.addEvents( /** * @event createviewmodel * Triggers when you create an instance of ViewModel based on query results. If in the event handler * view model was not created - an instance of the {@link #rowViewModelClassName base} class is instantiated. * @param {Object} eventArgs * @param {Object} eventArgs.rawData Column values. * @param {Object} eventArgs.rowConfig Column types. * @param {Object} eventArgs.viewModel View model. */ "createviewmodel"); }, /** * @private */ _destroy: function () { this.columns.destroy(); delete this.columns; }, /** * Creates an instance of ViewModel based on query results. * @private * @param {Object} rawData The result string of the query. * @param {Object} rowConfig Contains the types of all columns that were transferred from the server. * @return {Terrasoft.BaseViewModel} An instance of the entity from rawData data, taking into account rowConfig. */ createViewModelByQueryResult: function (rawData, rowConfig) { var viewModelRowConfig = this.getViewModelRowConfig(rowConfig); var eventArgs = { viewModel: {}, rawData: rawData, rowConfig: viewModelRowConfig }; this.fireEvent("createviewmodel", eventArgs); if (Ext.Object.isEmpty(eventArgs.viewModel)) { return Ext.create(this.rowViewModelClassName, { entitySchema: this.rootSchema, rowConfig: viewModelRowConfig, values: rawData, isNew: false, isDeleted: false }); } return eventArgs.viewModel; }, /** * Creates a column configuration for the BaseViewModel instance. * @private * @param {Object} rowConfig Contains the types of all columns that were transferred from the server. * @return {Object} Returns the configuration of the columns of the entity. */ getViewModelRowConfig: function (rowConfig) { var result = rowConfig; this.columns.eachKey(function (key, column) { if (result[key]) { result[key].columnPath = column.columnPath; } }, this); return result; }, /** * Generates an instance of ViewModel based on query results. * @private * @param {Object} rawData The result string of the query. * @param {Object} rowConfig Contains the types of all columns that were transferred from the server. * @return {Terrasoft.BaseViewModel} Returns the created instance of the entity from rawData data, * taking into account rowConfig. */ getViewModelByQueryResult: function (rawData, rowConfig) { Terrasoft.each(rowConfig, function (column, columnName) { var rowDataType = column.dataValueType; if (rowDataType === Terrasoft.DataValueType.DATE || rowDataType === Terrasoft.DataValueType.DATE_TIME || rowDataType === Terrasoft.DataValueType.TIME) { var rawDate = rawData[columnName]; rawData[columnName] = Ext.isEmpty(rawDate) ? null : Terrasoft.parseDate(rawDate); } }, this); return this.createViewModelByQueryResult(rawData, rowConfig); }, /** * Returns the selection caching key on the client. * @private * @param {String/Number} [primaryColumnValue] The value of the primary sampling key. Used for caching * the results of a selection of individual records (such as, #getEntity). * @return {String} */ getClientCacheItemName: function (primaryColumnValue) { var cacheItemName = ""; if (this.clientESQCacheParameters) { cacheItemName = this.clientESQCacheParameters.cacheItemName; } if (!cacheItemName && this.serverESQCacheParameters) { cacheItemName = this.serverESQCacheParameters.cacheItemName; } if (cacheItemName && primaryColumnValue) { cacheItemName = cacheItemName + "__" + primaryColumnValue; } return cacheItemName; }, /** * Copies the properties for serialization to the serialized object. Implements the mixin interface. * {@link Terrasoft.Serializable} * @protected * @override * @param {Object} serializableObject Serializable object. */ getSerializableObject: function (serializableObject) { this.callParent(arguments); serializableObject.columns = this.getSerializableProperty(this.columns); serializableObject.isDistinct = this.isDistinct; serializableObject.rowCount = this.rowCount; serializableObject.rowsOffset = this.rowsOffset; serializableObject.isPageable = this.isPageable; serializableObject.allColumns = this.allColumns; serializableObject.useLocalization = this.useLocalization; serializableObject.useRecordDeactivation = this.useRecordDeactivation; serializableObject.serverESQCacheParameters = this.serverESQCacheParameters; if (this.isPageable) { serializableObject.conditionalValues = this.getSerializableProperty(this.conditionalValues); } serializableObject.isHierarchical = this.isHierarchical; if (this.isHierarchical) { serializableObject.hierarchicalMaxDepth = this.hierarchicalMaxDepth; serializableObject.hierarchicalColumnName = this.hierarchicalColumnName; serializableObject.hierarchicalColumnValue = this.hierarchicalColumnValue; } }, /** * Initializes a configuration object to create query elements. * @protected * @override */ initQueryItems: function () { this.callParent(arguments); this.columns = Ext.create("Terrasoft.QueryColumns"); }, /** * Adds column {@link Terrasoft.EntityQueryColumn}. Creates column instance and adds to column collection. * @param {String/Terrasoft.BaseQueryColumn} column Column path relative {@link #rootSchema}. * Or instance of query column {@Link Terrasoft.BaseQueryColumn}. * @param {String} [columnAlias] Column alias. * @param {Object} [config] Entity query column initial config. * @throws {Terrasoft.ArgumentNullOrEmptyException} If first argument is {@Link Terrasoft.BaseQueryColumn}, * then argument columnAlias becomes required. * @return {Terrasoft.EntityQueryColumn} Returns instance of added column. */ addColumn: function (column, columnAlias, config) { return this.columns.addColumn(column, columnAlias, config); }, /** * Adds a column-parameter {@link Terrasoft.ParameterQueryColumn}. An instance of the column is created and * is added to the column collection. * @param {Mixed} paramValue The value of the parameter. The value must match the data type. * @param {Terrasoft.DataValueType} paramDataType The data type parameter. * @param {String} columnAlias (optional) The alias of the column. * @return {Terrasoft.ParameterQueryColumn} Returns an instance of the added column. */ addParameterColumn: function (paramValue, paramDataType, columnAlias) { return this.columns.addParameterColumn(paramValue, paramDataType, columnAlias); }, /** * Add a function column {@link Terrasoft.FunctionQueryColumn}, * with the macro type {@Link Terrasoft.FunctionType.MACROS}. An instance of the column is created and * is added to the column collection. The function adds a column with a macro type that does not require * parameterization. * For example, the current month, the current user, the Primary column, etc. * @param {Terrasoft.QueryMacrosType} macrosType Macro type of the column. * @param {String} columnAlias (optional) The alias of the column. * @return {Terrasoft.FunctionQueryColumn} Returns an instance of the added column. */ addMacrosColumn: function (macrosType, columnAlias) { return this.columns.addMacrosColumn(macrosType, columnAlias); }, /** * Add a function column {@link Terrasoft.FunctionQueryColumn}, * with the macro type {@Link Terrasoft.FunctionType.MACROS}. An instance of the column is created and * is added to the column collection. The function adds a column with a macro type that requires parameterization. * For example, the following N days, the 3rd quarter of the year, etc. * @param {Terrasoft.QueryMacrosType} macrosType Macro type of the column. * @param {Number/Date} macrosValue (optional) Auxiliary variable for the macro. * @param {String} columnAlias (optional) The alias of the column. * @return {Terrasoft.FunctionQueryColumn} Returns an instance of the added column. */ addDatePeriodMacrosColumn: function (macrosType, macrosValue, columnAlias) { return this.columns.addDatePeriodMacrosColumn(macrosType, macrosValue, columnAlias); }, /** * Add a function column {@link Terrasoft.FunctionQueryColumn}, * with the date part type {@Link Terrasoft.FunctionType.DATE_PART}. * An instance of the column is created and is added to the column collection. * @param {String} columnPath The path of the column to add. The path is relative to {@link #rootSchema}. * @param {Terrasoft.DatePartType} datePartType The date portion used as the value. * @param {String} columnAlias (optional) The alias of the column. * @return {Terrasoft.FunctionQueryColumn} Returns an instance of the added column. */ addDatePartFunctionColumn: function (columnPath, datePartType, columnAlias) { return this.columns.addDatePartFunctionColumn(columnPath, datePartType, columnAlias); }, /** * Add a function column {@link Terrasoft.FunctionQueryColumn}, * with the aggregation type {@Link Terrasoft.FunctionType.AGGREGATION}. An instance of the column is created and * is added to the column collection. * @param {String} columnPath The path of the column to add. The path is relative to {@link #rootSchema}. * @param {Terrasoft.AggregationType} aggregationType The type of aggregation used. * @param {String} columnAlias (optional) The alias of the column. * @param {Terrasoft.AggregationEvalType} aggregationEvalType The scope of the aggregating function * @return {Terrasoft.FunctionQueryColumn} Returns an instance of the added column. */ addAggregationSchemaColumn: function (columnPath, aggregationType, columnAlias, aggregationEvalType) { return this.columns.addAggregationSchemaColumn(columnPath, aggregationType, columnAlias, aggregationEvalType); }, /** * Adds function query column {@link Terrasoft.FunctionQueryColumn}. * @param {String} columnPath Column path from {@link #rootSchema}. * @param {Terrasoft.FunctionType} functionType Function type. * @param {String} columnAlias (optional) Column alias. * @return {Terrasoft.FunctionQueryColumn} Instance of column. */ addFunctionColumn: function (columnPath, functionType, columnAlias) { return this.columns.addFunctionColumn(columnPath, functionType, columnAlias); }, /** * @param response * @param primaryColumnValue * @param callback * @param scope * @private */ _parseGetEntityResponse: function (response, primaryColumnValue, callback, scope) { if (scope && scope.destroyed) { return; } var result = { success: response.success, entity: null }; if (response.success) { if (response.entity) { result.entity = response.entity; } else if (response.collection) { result.entity = response.collection.find(primaryColumnValue) || response.collection.first(); } else { this._storeQueryResponseToCache(response, primaryColumnValue); result.entity = this._getEntityFromQueryResponse(response); } } Ext.callback(callback, scope || this, [result]); if (!response.success) { throw new Terrasoft.exceptions.QueryExecutionException(response.errorInfo); } }, /** * @param response * @returns {Terrasoft.BaseViewModel} * @private */ _getEntityFromQueryResponse: function (response) { var viewModel = null; if (response.rowsAffected > 0) { if (!response.rowConfig) { this._setDefaultRowConfig(response); } viewModel = this.getViewModelByQueryResult(response.rows[0], response.rowConfig); } return viewModel; }, /** * @param response * @param primaryColumnValue * @private */ _storeQueryResponseToCache: function (response, primaryColumnValue) { if (response.success) { var cache = Terrasoft.EntitySchemaQuery.cache; var cacheItemName = this.getClientCacheItemName(primaryColumnValue); if (cacheItemName && !cache[cacheItemName]) { cache[cacheItemName] = Terrasoft.deepClone(response); } } }, /** * @param primaryColumnValue * @returns {Terrasoft.BaseViewModel} * @private */ _restoreQueryResponseFromCache: function (primaryColumnValue) { var cache = Terrasoft.EntitySchemaQuery.cache; var cacheItemName = this.getClientCacheItemName(primaryColumnValue); return cacheItemName && cache[cacheItemName] && Terrasoft.deepClone(cache[cacheItemName]); }, /** * Returns instance of entity by primary column value * @param {String/Number} primaryColumnValue Value of primary column value. * @param {Function} callback Callback function. * @param {Object} scope Context of callback execution. * @throws {Terrasoft.exceptions.ArgumentNullOrEmptyException} * Throws exception if primaryColumnValue is empty. */ getEntity: function (primaryColumnValue, callback, scope) { if (!primaryColumnValue) { throw new Terrasoft.ArgumentNullOrEmptyException(); } if (this.destroyed !== true) { var response = this._restoreQueryResponseFromCache(primaryColumnValue); if (response) { Ext.callback(callback, scope || this, [{ success: response.success, entity: this._getEntityFromQueryResponse(response) }]); } else { this.enablePrimaryColumnFilter(primaryColumnValue); Terrasoft.DataProvider.executeQuery(this, function (response) { this.disablePrimaryColumnFilter(); this._parseGetEntityResponse(response, primaryColumnValue, callback, scope); }, this); } } }, /** * Returns collection of entities. * @param {Function} callback Callback function. * @param {Object} scope Scope of callback function. */ getEntityCollection: function (callback, scope) { this.execute(callback, scope); }, /** * @param response * @private */ _setDefaultRowConfig: function (response) { response.rowConfig = {}; this.columns.eachKey(function (key) { response.rowConfig[key] = {}; }, this); }, /** * @param response * @private */ _updateRowConfigWithNotFoundColumns: function (response) { var notFoundColumns = response.notFoundColumns || []; notFoundColumns.forEach(function (column) { response.rowConfig[column] = { dataValueType: Terrasoft.DataValueType.TEXT, isNotFound: true }; }); }, /** * @param response * @returns {Terrasoft.BaseViewModelCollection} * @private */ _getResponseEntityCollection: function (response) { var rowConfig = response.rowConfig; var result = Ext.create("Terrasoft.BaseViewModelCollection", { entitySchema: this.rootSchema, rowConfig: rowConfig }); if (response.success && response.rowsAffected > 0) { var primaryColumnName = this.rootSchema ? this.rootSchema.primaryColumnName : this.defaultPrimaryColumnName; Terrasoft.each(response.rows, function (row) { var entity = this.getViewModelByQueryResult(row, rowConfig); if (rowConfig[primaryColumnName]) { var key = entity.get(primaryColumnName); if (result.contains(key)) { result.addItem(entity); } else { result.add(key, entity); } } else { result.addItem(entity); } }, this); } return result; }, /** * Parses query execution response. * @protected * @override * @param {Object} response Response object. * @param {Function} callback Callback function. * @param {Object} scope Callback function context. */ parseResponse: function (response, callback, scope) { if (response.collection) { Ext.callback(callback, scope, [response]); } else { this._storeQueryResponseToCache(response); if (!response.rowConfig) { this._setDefaultRowConfig(response); } this._updateRowConfigWithNotFoundColumns(response); var result = { success: response.success, collection: this._getResponseEntityCollection(response), errorInfo: response.errorInfo }; this.callParent([result, callback, scope]); } }, /** * @inheritdoc Terrasoft.data.queries.BaseQuery#execute * @override */ execute: function (callback, scope) { var cacheItemName = this.getClientCacheItemName(); var cache = Terrasoft.EntitySchemaQuery.cache; if (!cacheItemName || !cache[cacheItemName]) { this.callParent(arguments); return; } var response = cache[cacheItemName]; this.parseResponse(Terrasoft.deepClone(response), callback, scope); }, /** * @inheritdoc Terrasoft.core.BaseObject#onDestroy * @override */ onDestroy: function () { if (this.destroyed) { return; } var onParentDestroy = this.getParentMethod(this, arguments); if (Terrasoft.DataProvider.autoBatch) { Terrasoft.delay(function () { this._destroy(); onParentDestroy(); }, this, Terrasoft.DataProvider.autoBatchDelay * 10); } else { this._destroy(); onParentDestroy(); } } });