From e77965e56d44aec8e7e5dc2a9d71c38eea299965 Mon Sep 17 00:00:00 2001 From: Luciano Fiandesio Date: Thu, 16 Jan 2025 14:10:35 +0100 Subject: [PATCH] Minor refactoring --- .../data/JdbcEnrollmentAnalyticsManager.java | 205 ++++++++++++------ 1 file changed, 135 insertions(+), 70 deletions(-) diff --git a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java index 233550e84b4..e9d8ed3356b 100644 --- a/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java +++ b/dhis-2/dhis-services/dhis-service-analytics/src/main/java/org/hisp/dhis/analytics/event/data/JdbcEnrollmentAnalyticsManager.java @@ -60,6 +60,7 @@ import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; import org.hisp.dhis.analytics.analyze.ExecutionPlanStore; import org.hisp.dhis.analytics.common.CteContext; import org.hisp.dhis.analytics.common.CteDefinition; @@ -1085,46 +1086,9 @@ private void buildProgramStageCte( // Quoted column name for the item (e.g. "ax"."my_column"). String colName = quote(item.getItemName()); - if (params.isAggregatedEnrollments()) { - CteDefinition baseAggregatedCte = cteContext.getBaseAggregatedCte(); - assert baseAggregatedCte != null; - - String cteSql = - """ - select - evt.enrollment, - evt.%s as value - from ( - select - evt.enrollment, - evt.%s, - row_number() over ( - partition by evt.enrollment - order by occurreddate desc, created desc - ) as rn - from %s evt - join %s eb ON eb.enrollment = evt.enrollment - where evt.eventstatus != 'SCHEDULE' - and evt.ps = '%s' and %s) evt - where evt.rn = 1 - """ - .formatted( - colName, - colName, - eventTableName, - ENROLLMENT_AGGR_BASE, - item.getProgramStage().getUid(), - baseAggregatedCte.getAggregateWhereClause().replaceAll("%s", "eb")); - - System.out.println(cteSql); - - cteContext.addCte( - item.getProgramStage(), - item, - cteSql, - computeRowNumberOffset(item.getProgramStageOffset()), - false); + if (params.isAggregatedEnrollments()) { + handleAggregatedEnrollments(cteContext, item, params, eventTableName, colName); return; } @@ -1132,52 +1096,153 @@ private void buildProgramStageCte( boolean hasRowContext = rowContextAllowedAndNeeded(params, item); // Build the main CTE SQL. - // If hasRowContext == true, we'll also include the eventstatus column. - String cteSql = + String cteSql = buildMainCteSql(eventTableName, colName, item, hasRowContext); + + // Register this CTE in the context. + cteContext.addCte( + item.getProgramStage(), + item, + cteSql, + computeRowNumberOffset(item.getProgramStageOffset()), + hasRowContext); + + // If row context is needed, we add an extra "exists" CTE for event checks. + if (hasRowContext) { + addExistsCte(cteContext, item, eventTableName); + } + } + + /** + * Builds the main CTE SQL. + * + * @param eventTableName the event table name + * @param colName the quoted column name for the item + * @param item the {@link QueryItem} containing program-stage details + * @param hasRowContext whether row context is needed + * @return the main CTE SQL + */ + private String buildMainCteSql( + String eventTableName, String colName, QueryItem item, boolean hasRowContext) { + String template = """ select enrollment, - %s as value,%s + ${colName} as value,${rowContext} row_number() over ( partition by enrollment order by occurreddate desc, created desc ) as rn - from %s + from ${eventTableName} where eventstatus != 'SCHEDULE' - and ps = '%s' - """ - .formatted( - colName, - hasRowContext ? " eventstatus," : "", - eventTableName, - item.getProgramStage().getUid()); + and ps = '${programStageUid}' + """; + + Map values = new HashMap<>(); + values.put("colName", colName); + values.put("rowContext", hasRowContext ? " eventstatus," : ""); + values.put("eventTableName", eventTableName); + values.put("programStageUid", item.getProgramStage().getUid()); + + return new StringSubstitutor(values).replace(template); + } + + /** + * Handles the case when aggregated enrollments are enabled. + * + * @param cteContext the {@link CteContext} to which the new CTE definition(s) will be added + * @param item the {@link QueryItem} containing program-stage details + * @param params the {@link EventQueryParams}, used for checking row-context eligibility, offsets, + * etc. + * @param eventTableName the event table name + * @param colName the quoted column name for the item + */ + private void handleAggregatedEnrollments( + CteContext cteContext, + QueryItem item, + EventQueryParams params, + String eventTableName, + String colName) { + CteDefinition baseAggregatedCte = cteContext.getBaseAggregatedCte(); + assert baseAggregatedCte != null; + + String cteSql = buildAggregatedCteSql(eventTableName, colName, item, baseAggregatedCte); - // Register this CTE in the context. - // The createOffset2(...) method calculates the row offset based on - // item.getProgramStageOffset(). cteContext.addCte( item.getProgramStage(), item, cteSql, computeRowNumberOffset(item.getProgramStageOffset()), - hasRowContext); + false); + } - // If row context is needed, we add an extra "exists" CTE for event checks. - if (hasRowContext) { - String existCte = - """ - select distinct - enrollment - from - %s - where - eventstatus != 'SCHEDULE' - and ps = '%s' - """ - .formatted(eventTableName, item.getProgramStage().getUid()); - - cteContext.addExistsCte(item.getProgramStage(), item, existCte); - } + /** + * Builds the aggregated CTE SQL. + * + * @param eventTableName the event table name + * @param colName the quoted column name for the item + * @param item the {@link QueryItem} containing program-stage details + * @param baseAggregatedCte the base aggregated CTE + * @return the aggregated CTE SQL + */ + private String buildAggregatedCteSql( + String eventTableName, String colName, QueryItem item, CteDefinition baseAggregatedCte) { + String template = + """ + select + evt.enrollment, + evt.${colName} as value + from ( + select + evt.enrollment, + evt.${colName}, + row_number() over ( + partition by evt.enrollment + order by occurreddate desc, created desc + ) as rn + from ${eventTableName} evt + join ${enrollmentAggrBase} eb ON eb.enrollment = evt.enrollment + where evt.eventstatus != 'SCHEDULE' + and evt.ps = '${programStageUid}' and ${aggregateWhereClause}) evt + where evt.rn = 1 + """; + + Map values = new HashMap<>(); + values.put("colName", colName); + values.put("eventTableName", eventTableName); + values.put("enrollmentAggrBase", ENROLLMENT_AGGR_BASE); + values.put("programStageUid", item.getProgramStage().getUid()); + values.put( + "aggregateWhereClause", baseAggregatedCte.getAggregateWhereClause().replaceAll("%s", "eb")); + + return new StringSubstitutor(values).replace(template); + } + + /** + * Adds an "exists" CTE for event checks. + * + * @param cteContext the {@link CteContext} to which the new CTE definition(s) will be added + * @param item the {@link QueryItem} containing program-stage details + * @param eventTableName the event table name + */ + private void addExistsCte(CteContext cteContext, QueryItem item, String eventTableName) { + String template = + """ + select distinct + enrollment + from + ${eventTableName} + where + eventstatus != 'SCHEDULE' + and ps = '${programStageUid}' + """; + + Map values = new HashMap<>(); + values.put("eventTableName", eventTableName); + values.put("programStageUid", item.getProgramStage().getUid()); + + String existCte = new StringSubstitutor(values).replace(template); + + cteContext.addExistsCte(item.getProgramStage(), item, existCte); } private void addCteJoins(SelectBuilder builder, CteContext cteContext) {