Skip to content

Commit

Permalink
Introuduce AnalyticsSqlBuilder to handle DB-specific queries
Browse files Browse the repository at this point in the history
  • Loading branch information
luciano-fiandesio committed Nov 26, 2024
1 parent 2738f17 commit c8afc2f
Show file tree
Hide file tree
Showing 8 changed files with 355 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -27,18 +27,31 @@
*/
package org.hisp.dhis.analytics.config;

import org.hisp.dhis.analytics.AnalyticsTableHookService;
import org.hisp.dhis.analytics.AnalyticsTableManager;
import org.hisp.dhis.analytics.AnalyticsTableService;
import org.hisp.dhis.analytics.partition.PartitionManager;
import org.hisp.dhis.analytics.table.DefaultAnalyticsTableService;
import org.hisp.dhis.analytics.table.JdbcTrackedEntityEventsAnalyticsTableManager;
import org.hisp.dhis.analytics.table.setting.AnalyticsTableSettings;
import org.hisp.dhis.category.CategoryService;
import org.hisp.dhis.common.IdentifiableObjectManager;
import org.hisp.dhis.dataapproval.DataApprovalLevelService;
import org.hisp.dhis.dataelement.DataElementService;
import org.hisp.dhis.db.sql.AnalyticsSqlBuilder;
import org.hisp.dhis.db.sql.AnalyticsSqlBuilderProvider;
import org.hisp.dhis.db.sql.SqlBuilder;
import org.hisp.dhis.db.sql.SqlBuilderProvider;
import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.period.PeriodDataProvider;
import org.hisp.dhis.resourcetable.ResourceTableService;
import org.hisp.dhis.setting.SystemSettingsProvider;
import org.hisp.dhis.system.database.DatabaseInfoProvider;
import org.hisp.dhis.trackedentity.TrackedEntityTypeService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

/**
* @author Luciano Fiandesio
Expand All @@ -50,6 +63,46 @@ public SqlBuilder sqlBuilder(SqlBuilderProvider provider) {
return provider.getSqlBuilder();
}

@Bean
public AnalyticsSqlBuilder analyticsSqlBuilder(AnalyticsSqlBuilderProvider provider) {
return provider.getAnalyticsSqlBuilder();
}

@Bean("org.hisp.dhis.analytics.TrackedEntityEventsAnalyticsTableManager")
public AnalyticsTableManager jdbcTrackedEntityEventsAnalyticsTableManager(
IdentifiableObjectManager idObjectManager,
OrganisationUnitService organisationUnitService,
CategoryService categoryService,
SystemSettingsProvider settingsProvider,
DataApprovalLevelService dataApprovalLevelService,
ResourceTableService resourceTableService,
AnalyticsTableHookService tableHookService,
PartitionManager partitionManager,
DatabaseInfoProvider databaseInfoProvider,
@Qualifier("analyticsJdbcTemplate") JdbcTemplate jdbcTemplate,
TrackedEntityTypeService trackedEntityTypeService,
AnalyticsTableSettings analyticsTableSettings,
PeriodDataProvider periodDataProvider,
SqlBuilder sqlBuilder,
AnalyticsSqlBuilder analyticsSqlBuilder) {
return new JdbcTrackedEntityEventsAnalyticsTableManager(
idObjectManager,
organisationUnitService,
categoryService,
settingsProvider,
dataApprovalLevelService,
resourceTableService,
tableHookService,
partitionManager,
databaseInfoProvider,
jdbcTemplate,
trackedEntityTypeService,
analyticsTableSettings,
periodDataProvider,
sqlBuilder,
analyticsSqlBuilder);
}

@Bean("org.hisp.dhis.analytics.TrackedEntityAnalyticsTableService")
public AnalyticsTableService trackedEntityAnalyticsTableManager(
@Qualifier("org.hisp.dhis.analytics.TrackedEntityAnalyticsTableManager")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@
import org.hisp.dhis.dataapproval.DataApprovalLevelService;
import org.hisp.dhis.db.model.IndexType;
import org.hisp.dhis.db.model.Logged;
import org.hisp.dhis.db.sql.AnalyticsSqlBuilder;
import org.hisp.dhis.db.sql.SqlBuilder;
import org.hisp.dhis.organisationunit.OrganisationUnitService;
import org.hisp.dhis.period.PeriodDataProvider;
Expand All @@ -82,10 +83,8 @@
import org.hisp.dhis.trackedentity.TrackedEntityTypeService;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component("org.hisp.dhis.analytics.TrackedEntityEventsAnalyticsTableManager")
public class JdbcTrackedEntityEventsAnalyticsTableManager extends AbstractJdbcTableManager {

private static final List<AnalyticsTableColumn> FIXED_COLS =
Expand Down Expand Up @@ -197,6 +196,7 @@ public class JdbcTrackedEntityEventsAnalyticsTableManager extends AbstractJdbcTa
private static final String AND = " and (";

private final TrackedEntityTypeService trackedEntityTypeService;
private final AnalyticsSqlBuilder analyticsSqlBuilder;

public JdbcTrackedEntityEventsAnalyticsTableManager(
IdentifiableObjectManager idObjectManager,
Expand All @@ -212,7 +212,8 @@ public JdbcTrackedEntityEventsAnalyticsTableManager(
TrackedEntityTypeService trackedEntityTypeService,
AnalyticsTableSettings analyticsTableSettings,
PeriodDataProvider periodDataProvider,
SqlBuilder sqlBuilder) {
SqlBuilder sqlBuilder,
AnalyticsSqlBuilder analyticsSqlBuilder) {
super(
idObjectManager,
organisationUnitService,
Expand All @@ -228,6 +229,7 @@ public JdbcTrackedEntityEventsAnalyticsTableManager(
periodDataProvider,
sqlBuilder);
this.trackedEntityTypeService = trackedEntityTypeService;
this.analyticsSqlBuilder = analyticsSqlBuilder;
}

/**
Expand Down Expand Up @@ -288,7 +290,7 @@ private AnalyticsTableColumn getEventDataValueColumn() {
return AnalyticsTableColumn.builder()
.name("eventdatavalues")
.dataType(JSONB)
.selectExpression("ev.eventdatavalues")
.selectExpression(analyticsSqlBuilder.getEventDataValues())
.skipIndex(Skip.SKIP)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Copyright (c) 2004-2024, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.db.sql;

/**
* Interface for resolving specific SQL queries for analytics, that requires custom logic that can't
* be resolved by the default <code>SqlBuilder</code> implementations.
*/
public interface AnalyticsSqlBuilder {
/**
* Returns the correct SQL based on the underlying database for fetching the event data values.
*
* @return a SQL snippet.
*/
String getEventDataValues();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* Copyright (c) 2004-2024, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.db.sql;

import java.util.Objects;
import org.hisp.dhis.analytics.table.setting.AnalyticsTableSettings;
import org.hisp.dhis.db.model.Database;
import org.hisp.dhis.external.conf.DhisConfigurationProvider;
import org.springframework.stereotype.Service;

/** Provider of {@link AnalyticsSqlBuilder} implementations. */
@Service
public class AnalyticsSqlBuilderProvider {
private final AnalyticsSqlBuilder analyticsSqlBuilder;

public AnalyticsSqlBuilderProvider(AnalyticsTableSettings config) {
Objects.requireNonNull(config);
this.analyticsSqlBuilder = getSqlBuilder(config);
}

/**
* Returns a {@link AnalyticsSqlBuilder} implementation based on the system configuration.
*
* @return a {@link AnalyticsSqlBuilder}.
*/
public AnalyticsSqlBuilder getAnalyticsSqlBuilder() {
return analyticsSqlBuilder;
}

/**
* Returns the appropriate {@link AnalyticsSqlBuilder} implementation based on the system
* configuration.
*
* @param config the {@link DhisConfigurationProvider}.
* @return a {@link AnalyticsSqlBuilder}.
*/
private AnalyticsSqlBuilder getSqlBuilder(AnalyticsTableSettings config) {
Database database = config.getAnalyticsDatabase();
String catalog = config.getAnalyticsDatabaseCatalog();
String driverFilename = config.getAnalyticsDatabaseDriverFilename();

Objects.requireNonNull(database);

return switch (database) {
case DORIS -> new DorisAnalyticsSqlBuilder();
case CLICKHOUSE -> new ClickhouseAnalyticsSqlBuilder();
default -> new PostgresAnalyticsSqlBuilder();
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2004-2024, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.db.sql;

public class ClickhouseAnalyticsSqlBuilder implements AnalyticsSqlBuilder {
@Override
public String getEventDataValues() {
return "ev.eventdatavalues";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright (c) 2004-2024, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.db.sql;

public class DorisAnalyticsSqlBuilder implements AnalyticsSqlBuilder {
@Override
public String getEventDataValues() {
return "ev.eventdatavalues";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright (c) 2004-2024, University of Oslo
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
* list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* Neither the name of the HISP project nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package org.hisp.dhis.db.sql;

public class PostgresAnalyticsSqlBuilder implements AnalyticsSqlBuilder {

/**
* Returns a subquery that expand the event datavalue jsonb with two additional fields:
*
* <ul>
* <li>value_name: the name of the organisation unit that the datavalue is associated with
* <li>value_code: the code of the organisation unit that the datavalue is associated with
* </ul>
*
* @return a SQL subquery.
*/
@Override
public String getEventDataValues() {
return """
(select json_object_agg(l2.keys, l2.datavalue) as value
from (
select l1.uid,
l1.keys,
json_strip_nulls(json_build_object(
'value', l1.eventdatavalues -> l1.keys ->> 'value',
'created', l1.eventdatavalues -> l1.keys ->> 'created',
'storedBy', l1.eventdatavalues -> l1.keys ->> 'storedBy',
'lastUpdated', l1.eventdatavalues -> l1.keys ->> 'lastUpdated',
'providedElsewhere', l1.eventdatavalues -> l1.keys -> 'providedElsewhere',
'value_name', (select ou.name
from organisationunit ou
where ou.uid = l1.eventdatavalues -> l1.keys ->> 'value'),
'value_code', (select ou.code
from organisationunit ou
where ou.uid = l1.eventdatavalues -> l1.keys ->> 'value'))) as datavalue
from (select inner_evt.*, jsonb_object_keys(inner_evt.eventdatavalues) keys
from event inner_evt) as l1) as l2
where l2.uid = ev.uid
group by l2.uid)::jsonb
""";
}
}
Loading

0 comments on commit c8afc2f

Please sign in to comment.