diff --git a/common/model/db/sql/sqlSelectOlapBuilder.js b/common/model/db/sql/sqlSelectOlapBuilder.js new file mode 100644 index 0000000000..7d0274d216 --- /dev/null +++ b/common/model/db/sql/sqlSelectOlapBuilder.js @@ -0,0 +1,39 @@ +import * as Survey from '@core/survey/survey' + +import SqlSelectBuilder from './sqlSelectBuilder' + +class SqlSelectOlapBuilder extends SqlSelectBuilder { + constructor({ table }) { + super() + this._table = table + } + + _selectNodeDef({ nodeDefUuid }) { + const { _table: table } = this + const { survey } = table + const nodeDef = Survey.getNodeDefByUuid(nodeDefUuid)(survey) + const columnName = table.getColumnNameByAttributeDef(nodeDef) + this.select(columnName) + return this + } + + selectMeasure({ nodeDefUuid }) { + const { _table: table } = this + const { survey } = table + const nodeDef = Survey.getNodeDefByUuid(nodeDefUuid)(survey) + const columnName = table.getColumnNameByAttributeDef(nodeDef) + const columnNameToDecimal = `CAST(${columnName} AS DECIMAL)` + this.select(`SUM(${columnNameToDecimal}) AS ${columnName}`) + this.select(`SUM(${columnNameToDecimal})/${SqlSelectOlapBuilder.areaAlias} AS ${columnName}_ha`) + this.groupBy(columnName) + return this + } + + selectDimension({ nodeDefUuid }) { + return this._selectNodeDef({ nodeDefUuid }) + } +} + +SqlSelectOlapBuilder.areaAlias = 'area' + +export default SqlSelectOlapBuilder diff --git a/common/model/db/tables/olapData/olapAreaView.js b/common/model/db/tables/olapData/olapAreaView.js new file mode 100644 index 0000000000..e186785340 --- /dev/null +++ b/common/model/db/tables/olapData/olapAreaView.js @@ -0,0 +1,25 @@ +import * as Survey from '@core/survey/survey' +import * as NodeDef from '@core/survey/nodeDef' + +import TableSurveyRdb from '../tableSurveyRdb' +import TableOlapData from './table' + +const generateViewName = ({ survey, cycle, entityDef, baseUnitDef }) => { + const dataTable = new TableOlapData({ survey, cycle, entityDef, baseUnitDef }) + return dataTable.name + '_area_view' +} + +export default class OlapAreaView extends TableSurveyRdb { + constructor({ survey, cycle, entityDef, baseUnitDef }) { + super(Survey.getId(survey), generateViewName({ survey, cycle, entityDef, baseUnitDef })) + this._baseUnitDef = baseUnitDef + } + + get baseUnitUuidColumnName() { + return NodeDef.getName(this._baseUnitDef) + '_uuid' + } + + get areaColumnName() { + return 'area' + } +} diff --git a/common/model/db/tables/olapData/table.js b/common/model/db/tables/olapData/table.js index 87a03a523f..9b75402a85 100644 --- a/common/model/db/tables/olapData/table.js +++ b/common/model/db/tables/olapData/table.js @@ -29,6 +29,22 @@ export default class TableOlapData extends TableSurveyRdb { this._baseUnitDef = baseUnitDef } + get survey() { + return this._survey + } + + get entityDef() { + return this._entityDef + } + + get baseUnitDef() { + return this._baseUnitDef + } + + getColumnNameByAttributeDef = (attributeDef) => { + return NodeDef.getName(attributeDef) + } + get attributeDefsForColumns() { const sortedAttributeDefs = [] const attributeDefsByUuid = {} @@ -64,7 +80,7 @@ export default class TableOlapData extends TableSurveyRdb { // base unit UUID this.baseUnitUuidColumnName, // attribute columns - ...this.attributeDefsForColumns.map(NodeDef.getName), + ...this.attributeDefsForColumns.map((attributeDef) => this.getColumnNameByAttributeDef(attributeDef)), ] } @@ -87,6 +103,10 @@ export default class TableOlapData extends TableSurveyRdb { return NodeDef.getName(this._baseUnitDef) + '_uuid' } + get expFactorColumnName() { + return baseColumnSet.expFactor + } + get columnNamesAndTypes() { return [ // base columns diff --git a/common/model/query/query/keys.js b/common/model/query/query/keys.js index efb4c7c8e3..432d6fa617 100644 --- a/common/model/query/query/keys.js +++ b/common/model/query/query/keys.js @@ -15,6 +15,7 @@ export const modes = { raw: 'raw', rawEdit: 'rawEdit', aggregate: 'aggregate', + olap: 'olap', } export const displayTypes = { diff --git a/common/model/query/query/query.js b/common/model/query/query/query.js index a6874fb5f3..075d6269f6 100644 --- a/common/model/query/query/query.js +++ b/common/model/query/query/query.js @@ -47,11 +47,12 @@ const isMode = (mode) => (query) => getMode(query) === mode export const isModeAggregate = isMode(modes.aggregate) export const isModeRaw = isMode(modes.raw) export const isModeRawEdit = isMode(modes.rawEdit) +export const isModeOlap = isMode(modes.olap) // utils export const hasSelection = (query) => !A.isEmpty(getEntityDefUuid(query)) && - (isModeAggregate(query) + (isModeAggregate(query) || isModeOlap(query) ? !A.isEmpty(getMeasures(query)) && !A.isEmpty(getDimensions(query)) : !A.isEmpty(getAttributeDefUuids(query))) diff --git a/core/i18n/resources/en/common.js b/core/i18n/resources/en/common.js index 3d30d8bb46..9c34195211 100644 --- a/core/i18n/resources/en/common.js +++ b/core/i18n/resources/en/common.js @@ -749,6 +749,7 @@ Please refine your query (e.g. adding a filter) to reduce the number of items. mode: { label: 'Mode:', aggregate: 'Aggregate', + olap: 'OLAP', raw: 'Raw', rawEdit: 'Raw edit', }, diff --git a/core/survey/nodeDef.js b/core/survey/nodeDef.js index fc007111d7..06b2360dd1 100644 --- a/core/survey/nodeDef.js +++ b/core/survey/nodeDef.js @@ -312,7 +312,13 @@ export const getMeta = R.propOr({}, keys.meta) export const getMetaHierarchy = R.pathOr([], [keys.meta, metaKeys.h]) // Utils -export const getLabel = (nodeDef, lang, type = NodeDefLabelTypes.label, defaultToName = true) => { +const getLabelSuffix = (nodeDef) => { + if (isVirtual(nodeDef)) return ' (V)' + if (isAnalysis(nodeDef) && isAttribute(nodeDef)) return ' (C)' + return '' +} + +export const getLabel = (nodeDef, lang, type = NodeDefLabelTypes.label, defaultToName = true, includeSuffix = true) => { let firstPart = '' const name = getName(nodeDef) @@ -333,12 +339,10 @@ export const getLabel = (nodeDef, lang, type = NodeDefLabelTypes.label, defaultT if (StringUtils.isBlank(firstPart) && defaultToName) { firstPart = name } - - const suffix = isVirtual(nodeDef) ? ' (V)' : isAnalysis(nodeDef) && isAttribute(nodeDef) ? ' (C)' : '' - - return firstPart + suffix + return firstPart + (includeSuffix ? getLabelSuffix(nodeDef) : '') } -export const getLabelWithType = ({ nodeDef, lang, type }) => getLabel(nodeDef, lang, type) +export const getLabelWithType = ({ nodeDef, lang, type, includeSuffix }) => + getLabel(nodeDef, lang, type, true, includeSuffix) export const getDescription = (lang) => (nodeDef) => R.propOr('', lang, getDescriptions(nodeDef)) diff --git a/server/modules/analysis/manager/chain/index.js b/server/modules/analysis/manager/chain/index.js index b48b3ebc9b..220b519701 100644 --- a/server/modules/analysis/manager/chain/index.js +++ b/server/modules/analysis/manager/chain/index.js @@ -46,6 +46,11 @@ export const create = async ({ user, surveyId }) => { // ====== READ export const { countChains, fetchChains, fetchChain } = ChainRepository +export const fetchChainWithSamplingDesign = async ({ surveyId }) => { + const chains = await ChainRepository.fetchChains({ surveyId }) + return chains.find(Chain.hasSamplingDesign) +} + // ====== UPDATE export const { updateChain } = ChainRepository diff --git a/server/modules/analysis/manager/index.js b/server/modules/analysis/manager/index.js index 8eb646dd3f..4770426521 100644 --- a/server/modules/analysis/manager/index.js +++ b/server/modules/analysis/manager/index.js @@ -1,4 +1,13 @@ // ====== Chain -export { create, countChains, fetchChains, fetchChain, updateChain, updateChainStatusExec, deleteChain } from './chain' +export { + create, + countChains, + fetchChains, + fetchChain, + fetchChainWithSamplingDesign, + updateChain, + updateChainStatusExec, + deleteChain, +} from './chain' export { cleanChains } from './chainsCleanManager' diff --git a/server/modules/surveyRdb/manager/surveyRdbManager.js b/server/modules/surveyRdb/manager/surveyRdbManager.js index 2d7370621a..52441fcc3f 100644 --- a/server/modules/surveyRdb/manager/surveyRdbManager.js +++ b/server/modules/surveyRdb/manager/surveyRdbManager.js @@ -9,26 +9,27 @@ import * as Record from '@core/record/record' import * as PromiseUtils from '@core/promiseUtils' import * as StringUtils from '@core/stringUtils' import { FileFormats, getExtensionByFileFormat } from '@core/fileFormats' - +import * as Chain from '@common/analysis/chain' +import { ChainStatisticalAnalysis } from '@common/analysis/chainStatisticalAnalysis' +import { ColumnNodeDef, TableDataNodeDef, ViewDataNodeDef } from '@common/model/db' +import { Query } from '@common/model/query' +import * as NodeDefTable from '@common/surveyRdb/nodeDefTable' + +import { db } from '@server/db/db' +import * as DbUtils from '@server/db/dbUtils' +import * as FlatDataWriter from '@server/utils/file/flatDataWriter' import * as FileUtils from '@server/utils/file/fileUtils' +import { StreamUtils } from '@server/utils/streamUtils' +import * as ChainManager from '@server/modules/analysis/manager' import * as RecordRepository from '@server/modules/record/repository/recordRepository' -import { db } from '../../../db/db' -import * as DbUtils from '../../../db/dbUtils' -import * as FlatDataWriter from '../../../utils/file/flatDataWriter' - -import { ColumnNodeDef, TableDataNodeDef, ViewDataNodeDef } from '../../../../common/model/db' - -import { Query } from '../../../../common/model/query' -import * as NodeDefTable from '../../../../common/surveyRdb/nodeDefTable' - import * as DataTableInsertRepository from '../repository/dataTableInsertRepository' import * as DataTableReadRepository from '../repository/dataTableReadRepository' import * as DataTableRepository from '../repository/dataTable' import * as DataViewRepository from '../repository/dataView' +import * as OlapDataRepository from '../repository/olapDataTable' import { SurveyRdbCsvExport } from './surveyRdbCsvExport' import { UniqueFileNamesGenerator } from './UniqueFileNamesGenerator' -import { StreamUtils } from '@server/utils/streamUtils' // ==== DDL @@ -55,6 +56,7 @@ export { createNodeKeysHierarchyView } from '../repository/nodeKeysHierarchyView export { deleteNodeResultsByChainUuid, MassiveUpdateData, MassiveUpdateNodes } from '../repository/resultNode' export { createOlapDataTable, insertOlapData, clearOlapData } from '../repository/olapDataTable' +export { createOlapAreaView } from '../repository/olapAreaView' const maxExcelCellsLimit = 1000000 @@ -119,42 +121,42 @@ export const fetchViewData = async (params, client = db) => { client ) - if (outputStream) { - const fields = columnNodeDefs - ? null // all fields will be included in the CSV file - : SurveyRdbCsvExport.getCsvExportFields({ - survey, - query, - addCycle, - includeCategoryItemsLabels, - expandCategoryItems, - includeInternalUuids, - includeDateCreated, - }) - const { transformers } = SurveyRdbCsvExport.getCsvObjectTransformer({ - survey, - query, - expandCategoryItems, - nullsToEmpty, - keepFileNamesUnique: true, - uniqueFileNamesGenerator, - }) - await DbUtils.stream({ - queryStream: result, - client, - processor: async (dbStream) => - FlatDataWriter.writeItemsStreamToStream({ - stream: dbStream, - fields, - options: { - objectTransformer: Objects.isEmpty(transformers) ? undefined : A.pipe(...transformers), - }, - outputStream, - fileFormat, - }), - }) + if (!outputStream) { + return result } - return result + const fields = columnNodeDefs + ? null // all fields will be included in the CSV file + : SurveyRdbCsvExport.getCsvExportFields({ + survey, + query, + addCycle, + includeCategoryItemsLabels, + expandCategoryItems, + includeInternalUuids, + includeDateCreated, + }) + const { transformers } = SurveyRdbCsvExport.getCsvObjectTransformer({ + survey, + query, + expandCategoryItems, + nullsToEmpty, + keepFileNamesUnique: true, + uniqueFileNamesGenerator, + }) + await DbUtils.stream({ + queryStream: result, + client, + processor: async (dbStream) => + FlatDataWriter.writeItemsStreamToStream({ + stream: dbStream, + fields, + options: { + objectTransformer: Objects.isEmpty(transformers) ? undefined : A.pipe(...transformers), + }, + outputStream, + fileFormat, + }), + }) } /** @@ -191,16 +193,61 @@ export const fetchViewDataAgg = async (params, client = db) => { client ) - if (outputStream) { - const fields = SurveyRdbCsvExport.getCsvExportFieldsAgg({ survey, query, options }) - return DbUtils.stream({ - queryStream: result, - client, - processor: async (dbStream) => - FlatDataWriter.writeItemsStreamToStream({ stream: dbStream, outputStream, fields, fileFormat }), - }) + if (!outputStream) { + return result } - return result + const fields = SurveyRdbCsvExport.getCsvExportFieldsAgg({ survey, query, options }) + return DbUtils.stream({ + queryStream: result, + client, + processor: async (dbStream) => + FlatDataWriter.writeItemsStreamToStream({ stream: dbStream, outputStream, fields, fileFormat }), + }) +} + +export const fetchOlapData = async ( + { survey, cycle, query, limit, offset, outputStream = null, options = {} }, + client = db +) => { + const { fileFormat = FileFormats.csv } = options + + const surveyId = Survey.getId(survey) + const chain = await ChainManager.fetchChainWithSamplingDesign({ surveyId }) + const baseUnitDef = chain ? Survey.getBaseUnitNodeDef({ chain })(survey) : null + const chainStatisticalAnalysis = Chain.getStatisticalAnalysis(chain) + const reportingEntityDefUuid = chainStatisticalAnalysis + ? ChainStatisticalAnalysis.getEntityDefUuid(chainStatisticalAnalysis) + : null + const reportingEntityDef = reportingEntityDefUuid ? Survey.getNodeDefByUuid(reportingEntityDefUuid)(survey) : null + + if (!chain || !reportingEntityDef) { + return null + } + // Fetch data + const result = await OlapDataRepository.fetchOlapData( + { + survey, + cycle, + query, + baseUnitDef, + entityDef: reportingEntityDef, + limit, + offset, + stream: Boolean(outputStream), + }, + client + ) + + if (!outputStream) { + return result + } + const fields = SurveyRdbCsvExport.getCsvExportFieldsAgg({ survey, query, options }) + return DbUtils.stream({ + queryStream: result, + client, + processor: async (dbStream) => + FlatDataWriter.writeItemsStreamToStream({ stream: dbStream, outputStream, fields, fileFormat }), + }) } const _determineRecordUuidsFilter = async ({ survey, cycle, recordsModifiedAfter, recordUuids, search }) => { diff --git a/server/modules/surveyRdb/repository/olapAreaView/create.js b/server/modules/surveyRdb/repository/olapAreaView/create.js new file mode 100644 index 0000000000..e43fe7888a --- /dev/null +++ b/server/modules/surveyRdb/repository/olapAreaView/create.js @@ -0,0 +1,20 @@ +import OlapAreaView from '@common/model/db/tables/olapData/olapAreaView' +import TableOlapData from '@common/model/db/tables/olapData/table' + +import { db } from '@server/db/db' + +export const createOlapAreaView = async ({ survey, cycle, baseUnitDef, entityDef }, client = db) => { + const view = new OlapAreaView({ survey, cycle, baseUnitDef, entityDef }) + const table = new TableOlapData({ survey, cycle, baseUnitDef, entityDef }) + + return client.query( + `CREATE OR REPLACE VIEW + ${view.nameQualified} + AS ( + SELECT DISTINCT (${table.baseUnitUuidColumnName}) AS ${view.baseUnitUuidColumnName}, + ${table.expFactorColumnName} AS ${view.areaColumnName} + FROM + ${table.nameQualified} + )` + ) +} diff --git a/server/modules/surveyRdb/repository/olapAreaView/index.js b/server/modules/surveyRdb/repository/olapAreaView/index.js new file mode 100644 index 0000000000..b1d54883b0 --- /dev/null +++ b/server/modules/surveyRdb/repository/olapAreaView/index.js @@ -0,0 +1 @@ +export { createOlapAreaView } from './create' diff --git a/server/modules/surveyRdb/repository/olapDataTable/index.js b/server/modules/surveyRdb/repository/olapDataTable/index.js index 7266909356..6c294b0a03 100644 --- a/server/modules/surveyRdb/repository/olapDataTable/index.js +++ b/server/modules/surveyRdb/repository/olapDataTable/index.js @@ -1,3 +1,4 @@ export { createOlapDataTable } from './create' export { insertOlapData } from './insert' export { clearOlapData } from './delete' +export { fetchOlapData } from './read' diff --git a/server/modules/surveyRdb/repository/olapDataTable/read.js b/server/modules/surveyRdb/repository/olapDataTable/read.js new file mode 100644 index 0000000000..186fee4f73 --- /dev/null +++ b/server/modules/surveyRdb/repository/olapDataTable/read.js @@ -0,0 +1,62 @@ +import { Objects } from '@openforis/arena-core' + +import SqlSelectOlapBuilder from '@common/model/db/sql/sqlSelectOlapBuilder' +import TableOlapData from '@common/model/db/tables/olapData/table' +import { Query, Sort } from '@common/model/query' + +import * as Survey from '@core/survey/survey' + +import { db } from '@server/db/db' + +const _getSelectQuery = ({ survey, cycle, query, baseUnitDef }) => { + const entityDefUuid = Query.getEntityDefUuid(query) + + const entityDef = Survey.getNodeDefByUuid(entityDefUuid)(survey) + const table = new TableOlapData({ survey, cycle, baseUnitDef, entityDef }) + + const queryBuilder = new SqlSelectOlapBuilder({ table, entityDef }) + + // base unit uuid + queryBuilder.select(table.baseUnitUuidColumnName) + + // SELECT dimensions + Query.getDimensions(query).forEach((nodeDefUuid) => queryBuilder.selectDimension({ nodeDefUuid })) + + // SELECT measures + Object.entries(Query.getMeasures(query)).forEach(([nodeDefUuid]) => queryBuilder.selectMeasure({ nodeDefUuid })) + + // FROM clause + queryBuilder.from(table.nameQualified) + + // ORDER BY clause + const sort = Query.getSort(query) + const { clause: sortClause, params: sortParams } = Sort.toSql(sort) + if (Objects.isNotEmpty(sortParams)) { + queryBuilder.orderBy(sortClause) + queryBuilder.addParams(sortParams) + } + return { select: queryBuilder.build(), queryParams: queryBuilder.params } +} + +export const fetchOlapData = async ({ survey, cycle, query, baseUnitDef, entityDef }, client = db) => { + const { select, queryParams } = _getSelectQuery({ survey, cycle, query, baseUnitDef }) + + const table = new TableOlapData({ survey, cycle, baseUnitDef, entityDef }) + const dimensionUuids = Query.getDimensions(query) + const dimensionColumnNames = dimensionUuids.map((dimensionUuid) => { + const dimensionDef = Survey.getNodeDefByUuid(dimensionUuid)(survey) + return table.getColumnNameByAttributeDef(dimensionDef) + }) + const dimensionColumnNamesJoint = dimensionColumnNames.join(', ') + + const areaTableSelect = `SELECT ${dimensionColumnNamesJoint}, SUM(${table.expFactorColumnName}) AS ${SqlSelectOlapBuilder.areaAlias} FROM ( + SELECT DISTINCT(${table.baseUnitUuidColumnName}), ${table.expFactorColumnName}, ${dimensionColumnNamesJoint} + FROM ${table.nameQualified} + ) GROUP BY ${dimensionColumnNamesJoint}` + + return client.query( + `WITH area_table AS (${areaTableSelect}) + ${select}`, + queryParams + ) +} diff --git a/server/modules/surveyRdb/service/surveyRdbCreationJob/surveyRdbOlapDataTablesCreationJob.js b/server/modules/surveyRdb/service/surveyRdbCreationJob/surveyRdbOlapDataTablesCreationJob.js index 1bbd163443..1f27229413 100644 --- a/server/modules/surveyRdb/service/surveyRdbCreationJob/surveyRdbOlapDataTablesCreationJob.js +++ b/server/modules/surveyRdb/service/surveyRdbCreationJob/surveyRdbOlapDataTablesCreationJob.js @@ -142,6 +142,8 @@ export default class SurveyRdbOlapDataTablesCreationJob extends Job { async (entityDef) => { this.logDebug(`create OLAP table for entity def ${NodeDef.getName(entityDef)}`) await SurveyRdbManager.createOlapDataTable({ survey, cycle, baseUnitDef, entityDef }, tx) + this.logDebug(`create OLAP area view for entity def ${NodeDef.getName(entityDef)}`) + await SurveyRdbManager.createOlapAreaView({ survey, cycle, baseUnitDef, entityDef }, tx) this.incrementProcessedItems() }, stopIfFunction diff --git a/server/modules/surveyRdb/service/surveyRdbService.js b/server/modules/surveyRdb/service/surveyRdbService.js index 41d33e7f53..5b9af7b8c6 100644 --- a/server/modules/surveyRdb/service/surveyRdbService.js +++ b/server/modules/surveyRdb/service/surveyRdbService.js @@ -70,31 +70,33 @@ export const fetchViewData = async (params) => { const survey = await _fetchSurvey({ surveyId, cycle }) const recordOwnerUuid = _getRecordOwnerUuidForQuery({ user, survey }) - const data = Query.isModeAggregate(parsedQuery) - ? await SurveyRdbManager.fetchViewDataAgg({ - survey, - cycle, - query, - recordOwnerUuid, - offset, - limit, - outputStream, - options, - }) - : await SurveyRdbManager.fetchViewData({ - survey, - cycle, - query, - columnNodeDefs, - recordOwnerUuid, - offset, - limit, - outputStream, - addCycle, - ...options, - }) - - return data + if (Query.isModeAggregate(parsedQuery)) { + return SurveyRdbManager.fetchViewDataAgg({ + survey, + cycle, + query, + recordOwnerUuid, + offset, + limit, + outputStream, + options, + }) + } + if (Query.isModeOlap(parsedQuery)) { + return SurveyRdbManager.fetchOlapData({ survey, cycle, query, limit, offset, outputStream, options }) + } + return SurveyRdbManager.fetchViewData({ + survey, + cycle, + query, + columnNodeDefs, + recordOwnerUuid, + offset, + limit, + outputStream, + addCycle, + ...options, + }) } /** diff --git a/webapp/components/DataQuery/ButtonBar/ButtonBar.js b/webapp/components/DataQuery/ButtonBar/ButtonBar.js index c1fcada14b..0801abafe7 100644 --- a/webapp/components/DataQuery/ButtonBar/ButtonBar.js +++ b/webapp/components/DataQuery/ButtonBar/ButtonBar.js @@ -42,14 +42,16 @@ const modeButtonItems = [ iconClassName: 'icon-sigma', label: 'dataView.dataQuery.mode.aggregate', }, + { + key: modes.olap, + iconClassName: 'icon-sigma', + label: 'dataView.dataQuery.mode.olap', + }, ] -const uiModeByQueryMode = { - [modes.raw]: modes.raw, +const getUiModeByQueryMode = (mode) => // raw edit mode shown as "raw" in mode button group - [modes.rawEdit]: modes.raw, - [modes.aggregate]: modes.aggregate, -} + mode === modes.rawEdit ? modes.raw : mode const ButtonBar = (props) => { const { @@ -80,7 +82,7 @@ const ButtonBar = (props) => { }, [onChangeQuery, query] ) - const selectedMode = uiModeByQueryMode[Query.getMode(query)] + const selectedMode = getUiModeByQueryMode(Query.getMode(query)) const modeEdit = Query.isModeRawEdit(query) const hasSelection = Query.hasSelection(query) const queryChangeDisabled = modeEdit || !dataLoaded || dataLoading diff --git a/webapp/components/DataQuery/QueryNodeDefsSelector/QueryNodeDefsSelector.js b/webapp/components/DataQuery/QueryNodeDefsSelector/QueryNodeDefsSelector.js index 97e0a62cac..18b6b6b992 100644 --- a/webapp/components/DataQuery/QueryNodeDefsSelector/QueryNodeDefsSelector.js +++ b/webapp/components/DataQuery/QueryNodeDefsSelector/QueryNodeDefsSelector.js @@ -16,6 +16,8 @@ const QueryNodeDefsSelector = (props) => { const query = DataExplorerSelectors.useQuery() const modeAggregate = Query.isModeAggregate(query) + const modeOlap = Query.isModeOlap(query) + const mode = Query.getMode(query) const survey = useSurvey() const hierarchy = Survey.getHierarchy(NodeDef.isEntityOrMultiple)(survey) @@ -24,15 +26,15 @@ const QueryNodeDefsSelector = (props) => { const onChangeEntity = useCallback( (entityDefUuid) => { let newQuery = Query.create({ entityDefUuid }) - if (modeAggregate) { - newQuery = Query.assocMode(Query.modes.aggregate)(newQuery) + if (modeAggregate || modeOlap) { + newQuery = Query.assocMode(mode)(newQuery) } onChangeQuery(newQuery) }, - [modeAggregate, onChangeQuery] + [mode, modeAggregate, modeOlap, onChangeQuery] ) - return modeAggregate ? ( + return modeAggregate || modeOlap ? ( { onChangeEntity={onChangeEntity} onChangeMeasures={(measuresUpdate) => onChangeQuery(Query.assocMeasures(measuresUpdate)(query))} onChangeDimensions={(dimensionsUpdate) => onChangeQuery(Query.assocDimensions(dimensionsUpdate)(query))} + olap={modeOlap} showAnalysisAttributes /> ) : ( diff --git a/webapp/components/survey/NodeDefsSelector/NodeDefsSelectorAggregate.js b/webapp/components/survey/NodeDefsSelector/NodeDefsSelectorAggregate.js index 76d721a2b8..50139f4e8d 100644 --- a/webapp/components/survey/NodeDefsSelector/NodeDefsSelectorAggregate.js +++ b/webapp/components/survey/NodeDefsSelector/NodeDefsSelectorAggregate.js @@ -33,12 +33,16 @@ const getPrevCalculations = ({ nodeDefUuidEntity, survey }) => { return variablesPrevCalculations } +const isNodeDefIncludedAsDimension = (nodeDef) => + NodeDef.isKey(nodeDef) || NodeDef.isBoolean(nodeDef) || NodeDef.isCode(nodeDef) || NodeDef.isTaxon(nodeDef) + const NodeDefsSelectorAggregate = (props) => { const { dimensions, measures, nodeDefLabelType = NodeDef.NodeDefLabelTypes.label, nodeDefUuidEntity = null, + olap = false, onChangeEntity, onChangeMeasures, onChangeDimensions, @@ -95,12 +99,7 @@ const NodeDefsSelectorAggregate = (props) => { - NodeDef.isBoolean(nodeDef) || - NodeDef.isCode(nodeDef) || - NodeDef.isTaxon(nodeDef) || - NodeDef.isKey(nodeDef) - } + filterFunction={isNodeDefIncludedAsDimension} nodeDefLabelType={nodeDefLabelType} nodeDefUuidEntity={nodeDefUuidEntity} nodeDefUuidsAttributes={dimensions} @@ -114,8 +113,8 @@ const NodeDefsSelectorAggregate = (props) => { !NodeDef.isKey(nodeDef)} - includeEntityFrequencySelector + filterFunction={(nodeDef) => !NodeDef.isKey(nodeDef) && (!olap || NodeDef.isAnalysis(nodeDef))} + includeEntityFrequencySelector={!olap} nodeDefLabelType={nodeDefLabelType} nodeDefUuidEntity={nodeDefUuidEntity} nodeDefUuidsAttributes={measuresNodeDefUuids} @@ -155,6 +154,7 @@ NodeDefsSelectorAggregate.propTypes = { measures: PropTypes.object.isRequired, nodeDefLabelType: PropTypes.string, nodeDefUuidEntity: PropTypes.string, + olap: PropTypes.bool, onChangeEntity: PropTypes.func.isRequired, onChangeMeasures: PropTypes.func.isRequired, onChangeDimensions: PropTypes.func.isRequired,