diff --git a/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java b/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java index 31a4eabb84..b05d4e2472 100644 --- a/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java +++ b/backend/src/main/java/com/bakdata/conquery/apiv1/query/concept/filter/FilterValue.java @@ -19,7 +19,7 @@ import com.bakdata.conquery.models.query.QueryResolveContext; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptConversionTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; @@ -61,15 +61,16 @@ public abstract class FilterValue { private VALUE value; - public void resolve(QueryResolveContext context) {}; + public void resolve(QueryResolveContext context) { + } public FilterNode createNode() { return getFilter().createFilterNode(getValue()); } - public SqlFilters convertToSqlFilter(SqlIdColumns ids, ConversionContext context, ConceptConversionTables tables) { + public SqlFilters convertToSqlFilter(SqlIdColumns ids, ConversionContext context, ConnectorSqlTables tables) { FilterContext filterContext = FilterContext.forConceptConversion(ids, value, context, tables); - SqlFilters sqlFilters = filter.convertToSqlFilter(filterContext); + SqlFilters sqlFilters = filter.createConverter().convertToSqlFilter(filter, filterContext); if (context.isNegation()) { return new SqlFilters(sqlFilters.getSelects(), sqlFilters.getWhereClauses().negated()); } @@ -78,7 +79,7 @@ public SqlFilters convertToSqlFilter(SqlIdColumns ids, ConversionContext context public Condition convertForTableExport(SqlIdColumns ids, ConversionContext context) { FilterContext filterContext = FilterContext.forTableExport(ids, value, context); - return filter.convertForTableExport(filterContext); + return filter.createConverter().convertForTableExport(filter, filterContext); } @NoArgsConstructor @@ -217,7 +218,11 @@ public GroupFilterValue deserialize(JsonParser p, DeserializationContext ctxt) t final Filter filter = nsIdDeserializer.deserialize(filterTraverse, ctxt); if (!(filter instanceof GroupFilter)) { - throw InvalidTypeIdException.from(filterNode.traverse(), GroupFilter.class, String.format("Expected filter of type %s but was: %s", GroupFilter.class, filter != null ? filter.getClass() : null)); + throw InvalidTypeIdException.from(filterNode.traverse(), GroupFilter.class, String.format("Expected filter of type %s but was: %s", GroupFilter.class, + filter != null + ? filter.getClass() + : null + )); } GroupFilter groupFilter = (GroupFilter) filter; diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/Filter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/Filter.java index 58ba6f947e..e56884744b 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/Filter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/Filter.java @@ -13,8 +13,7 @@ import com.bakdata.conquery.models.identifiable.ids.NamespacedIdentifiable; import com.bakdata.conquery.models.identifiable.ids.specific.FilterId; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import com.fasterxml.jackson.annotation.JsonAlias; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnore; @@ -25,7 +24,6 @@ import lombok.NoArgsConstructor; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.jooq.Condition; /** * This class is the abstract superclass for all filters. @@ -92,7 +90,8 @@ public boolean isForConnectorsTable() { continue; } - log.error("Filter[{}] of Table[{}] is not of Connector[{}]#Table[{}]", getId(), column.getTable().getId(), connector.getId(), connector.getTable().getId()); + log.error("Filter[{}] of Table[{}] is not of Connector[{}]#Table[{}]", getId(), column.getTable().getId(), connector.getId(), connector.getTable() + .getId()); valid = false; } @@ -101,13 +100,7 @@ public boolean isForConnectorsTable() { } @JsonIgnore - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - throw new UnsupportedOperationException("SQL conversion of filter %s not implemented yet.".formatted(getClass())); + public > FilterConverter createConverter() { + throw new UnsupportedOperationException("No converter implemented for Filter %s".formatted(getClass())); } - - @JsonIgnore - public Condition convertForTableExport(FilterContext filterContext) { - throw new UnsupportedOperationException("SQL conversion of filter %s not implemented yet".formatted(getClass())); - } - } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/BigMultiSelectFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/BigMultiSelectFilter.java index 864e939826..4187d6fdfa 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/BigMultiSelectFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/BigMultiSelectFilter.java @@ -5,18 +5,15 @@ import com.bakdata.conquery.models.datasets.concepts.filters.Filter; import com.bakdata.conquery.models.query.filter.event.MultiSelectFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.SelectFilterUtil; -import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectFilterConverter; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; import lombok.Setter; -import org.jooq.Condition; /** * This filter represents a select in the front end. This means that the user can select one or more values from a list of values. - * + *

* This Filter can use optional labels or a template for displaying, same as {@link MultiSelectFilter}. * However, the frontend will fetch and display data beyond the defined values for {@link BigMultiSelectFilter}/BIG_MULTI_SELECT. */ @@ -37,12 +34,7 @@ public FilterNode createFilterNode(String[] value) { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return SelectFilterUtil.convert(this, filterContext, filterContext.getValue()); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return MultiSelectCondition.onColumn(getColumn(), filterContext.getValue(), filterContext.getSqlDialect().getFunctionProvider()).condition(); + public FilterConverter createConverter() { + return new MultiSelectFilterConverter(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountFilter.java index fafaf8ddf3..98cef9923c 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountFilter.java @@ -17,14 +17,11 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.DistinctValuesWrapperAggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountAggregator; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.model.aggregator.CountSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.filter.CountCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.NoArgsConstructor; -import org.jooq.Condition; @CPSType(id = "COUNT", base = Filter.class) @NoArgsConstructor @@ -74,12 +71,7 @@ public List getRequiredColumns() { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return CountSqlAggregator.create(this, filterContext).getSqlFilters(); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return CountCondition.onColumn(column, filterContext.getValue()).condition(); + public FilterConverter createConverter() { + return new CountSqlAggregator(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountQuartersFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountQuartersFilter.java index 4c54a403cf..76d4b5c4e1 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountQuartersFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/CountQuartersFilter.java @@ -14,16 +14,16 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountQuartersOfDateRangeAggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountQuartersOfDatesAggregator; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; -import com.bakdata.conquery.sql.conversion.model.select.CountQuartersSqlAggregator; +import com.bakdata.conquery.sql.conversion.model.aggregator.CountQuartersSqlAggregator; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import lombok.Getter; import lombok.Setter; -@Setter @Getter -@CPSType(id="COUNT_QUARTERS", base=Filter.class) +@Setter +@Getter +@CPSType(id = "COUNT_QUARTERS", base = Filter.class) public class CountQuartersFilter extends SingleColumnFilter { - + @Override public EnumSet getAcceptedColumnTypes() { return EnumSet.of(MajorTypeId.DATE, MajorTypeId.DATE_RANGE); @@ -44,11 +44,7 @@ public FilterNode createFilterNode(Range.LongRange value) { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - if (getColumn().getType() == MajorTypeId.DATE_RANGE) { - throw new UnsupportedOperationException("COUNT_QUARTERS conversion on columns of type DATE_RANGE not implemented yet."); - } - return CountQuartersSqlAggregator.create(this, filterContext).getSqlFilters(); + public FilterConverter createConverter() { + return new CountQuartersSqlAggregator(); } - } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/DateDistanceFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/DateDistanceFilter.java index f9090ad6b4..0213d0ad73 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/DateDistanceFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/DateDistanceFilter.java @@ -14,26 +14,25 @@ import com.bakdata.conquery.models.exceptions.ConceptConfigurationException; import com.bakdata.conquery.models.query.filter.event.DateDistanceFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.model.aggregator.DateDistanceSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.filter.DateDistanceCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import jakarta.validation.constraints.NotNull; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.jooq.Condition; /** * This filter represents a select in the front end. This means that the user can select one or more values from a list of values. */ -@Getter @Setter @Slf4j -@CPSType(id="DATE_DISTANCE", base=Filter.class) +@Getter +@Setter +@Slf4j +@CPSType(id = "DATE_DISTANCE", base = Filter.class) public class DateDistanceFilter extends SingleColumnFilter { @NotNull private ChronoUnit timeUnit = ChronoUnit.YEARS; - + @Override public EnumSet getAcceptedColumnTypes() { return EnumSet.of(MajorTypeId.DATE); @@ -47,19 +46,14 @@ public void configureFrontend(FrontendFilterConfiguration.Top f, ConqueryConfig f.setType(FrontendFilterType.Fields.INTEGER_RANGE); } - + @Override public FilterNode createFilterNode(Range.LongRange value) { return new DateDistanceFilterNode(getColumn(), timeUnit, value); } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return DateDistanceSqlAggregator.create(this, filterContext).getSqlFilters(); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return DateDistanceCondition.onColumn(getColumn(), getTimeUnit(), filterContext).condition(); + public FilterConverter createConverter() { + return new DateDistanceSqlAggregator(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/FlagFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/FlagFilter.java index 84d779bd71..c06b99dadd 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/FlagFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/FlagFilter.java @@ -19,22 +19,19 @@ import com.bakdata.conquery.models.exceptions.ConceptConfigurationException; import com.bakdata.conquery.models.query.filter.event.FlagColumnsFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.model.aggregator.FlagSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.filter.FlagCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import io.dropwizard.validation.ValidationMethod; import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.ToString; -import org.jooq.Condition; /** * Implements a MultiSelect type filter, where an event can meet multiple criteria (as opposed to {@link MultiSelectFilter} which is restricted to one value per event). * This is achieved by using multiple {@link com.bakdata.conquery.models.types.ResultType.BooleanT} columns, each defining if one property is met or not. - * + *

* The selected flags are logically or-ed. */ @Getter @@ -76,7 +73,7 @@ public FilterNode createFilterNode(String[] labels) { columns[index] = column; } - if(!missing.isEmpty()){ + if (!missing.isEmpty()) { throw new ConqueryError.ExecutionCreationPlanMissingFlagsError(missing); } @@ -96,12 +93,7 @@ public boolean isAllColumnsBoolean() { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return FlagSqlAggregator.create(this, filterContext).getSqlFilters(); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return FlagCondition.onColumn(getFlags(), filterContext.getValue()).condition(); + public FilterConverter createConverter() { + return new FlagSqlAggregator(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/MultiSelectFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/MultiSelectFilter.java index e5451b0be3..2ccacd286d 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/MultiSelectFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/MultiSelectFilter.java @@ -5,12 +5,9 @@ import com.bakdata.conquery.models.datasets.concepts.filters.Filter; import com.bakdata.conquery.models.query.filter.event.MultiSelectFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.SelectFilterUtil; -import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectFilterConverter; import com.fasterxml.jackson.annotation.JsonIgnore; -import org.jooq.Condition; /** * This filter represents a select in the front end. This means that the user can select one or more values from a list of values. @@ -36,12 +33,7 @@ public FilterNode createFilterNode(String[] value) { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return SelectFilterUtil.convert(this, filterContext, filterContext.getValue()); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return MultiSelectCondition.onColumn(getColumn(), filterContext.getValue(), filterContext.getSqlDialect().getFunctionProvider()).condition(); + public FilterConverter createConverter() { + return new MultiSelectFilterConverter(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/NumberFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/NumberFilter.java index fdf746d7d1..97097425e7 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/NumberFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/NumberFilter.java @@ -16,14 +16,11 @@ import com.bakdata.conquery.models.query.filter.event.number.MoneyFilterNode; import com.bakdata.conquery.models.query.filter.event.number.RealFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.aggregator.NumberSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.filter.NumberCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.NumberFilterConverter; import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; -import org.jooq.Condition; /** * This filter represents a filter on an integer columnof each event. @@ -60,12 +57,7 @@ public FilterNode createFilterNode(RANGE value) { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return NumberSqlAggregator.create(this, filterContext).getSqlFilters(); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return NumberCondition.onColumn(getColumn(), filterContext.getValue()).condition(); + public FilterConverter, RANGE> createConverter() { + return new NumberFilterConverter<>(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SingleSelectFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SingleSelectFilter.java index 012be75080..ee4e859144 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SingleSelectFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SingleSelectFilter.java @@ -5,12 +5,9 @@ import com.bakdata.conquery.models.datasets.concepts.filters.Filter; import com.bakdata.conquery.models.query.filter.event.SelectFilterNode; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.SelectFilterUtil; -import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectCondition; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.SingleSelectFilterConverter; import net.minidev.json.annotate.JsonIgnore; -import org.jooq.Condition; /** * This filter represents a select in the front end. This means that the user can select one or more values from a list of values.", @@ -32,16 +29,7 @@ public String getFilterType() { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - return SelectFilterUtil.convert(this, filterContext, new String[]{filterContext.getValue()}); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return MultiSelectCondition.onColumn( - getColumn(), - new String[]{filterContext.getValue()}, - filterContext.getSqlDialect().getFunctionProvider() - ).condition(); + public FilterConverter createConverter() { + return new SingleSelectFilterConverter(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SumFilter.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SumFilter.java index 9749afae68..fee51ae11f 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SumFilter.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/filters/specific/SumFilter.java @@ -30,17 +30,13 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.sum.MoneySumAggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.sum.RealSumAggregator; import com.bakdata.conquery.models.query.queryplan.filter.FilterNode; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.aggregator.SumDistinctSqlAggregator; import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; -import com.bakdata.conquery.sql.conversion.model.filter.SumCondition; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import com.fasterxml.jackson.annotation.JsonIgnore; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.NoArgsConstructor; import lombok.extern.slf4j.Slf4j; -import org.jooq.Condition; /** * This filter represents a filter on the sum of one integer column. @@ -109,16 +105,8 @@ public FilterNode createFilterNode(RANGE value) { } @Override - public SqlFilters convertToSqlFilter(FilterContext filterContext) { - if (distinctByColumn != null && !distinctByColumn.isEmpty()) { - return SumDistinctSqlAggregator.create(this, filterContext).getSqlFilters(); - } - return SumSqlAggregator.create(this, filterContext).getSqlFilters(); - } - - @Override - public Condition convertForTableExport(FilterContext filterContext) { - return SumCondition.onColumn(getColumn(), getSubtractColumn(), filterContext.getValue()).condition(); + public FilterConverter, RANGE> createConverter() { + return new SumSqlAggregator<>(); } @JsonIgnore diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/Select.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/Select.java index 53306dba5c..0a5031840e 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/Select.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/Select.java @@ -17,8 +17,7 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.resultinfo.SelectResultInfo; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonBackReference; import com.fasterxml.jackson.annotation.JsonIgnore; import com.fasterxml.jackson.annotation.JsonProperty; @@ -55,7 +54,9 @@ public Dataset getDataset() { /** * When set, the Frontend will preselect the Select for the User. */ - @Setter @Getter @JsonProperty("default") + @Setter + @Getter + @JsonProperty("default") private boolean isDefault = false; @JsonIgnore @@ -100,7 +101,7 @@ public SelectResultInfo getResultInfo(CQConcept cqConcept) { public boolean isForConnectorsTable() { boolean valid = true; - if(holder instanceof Concept){ + if (holder instanceof Concept) { return getRequiredColumns().isEmpty(); } @@ -112,7 +113,8 @@ public boolean isForConnectorsTable() { continue; } - log.error("Select[{}] of Table[{}] is not of Connector[{}]#Table[{}]", getId(), column.getTable().getId(), connector.getId(), connector.getTable().getId()); + log.error("Select[{}] of Table[{}] is not of Connector[{}]#Table[{}]", getId(), column.getTable().getId(), connector.getId(), connector.getTable() + .getId()); valid = false; } @@ -121,8 +123,8 @@ public boolean isForConnectorsTable() { } @JsonIgnore - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - throw new UnsupportedOperationException("SQL conversion of select %s not implemented yet.".formatted(getClass())); + public SelectConverter createConverter() { + throw new UnsupportedOperationException("No converter implemented for Select %s".formatted(getClass())); } @JsonIgnore diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDateUnionSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDateUnionSelect.java index 681d7786c0..ae8259aba0 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDateUnionSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDateUnionSelect.java @@ -10,9 +10,8 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.EventDateUnionAggregator; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.aggregator.EventDateUnionSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.EventDateUnionSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; /** * Collects the event dates that are corresponding to an enclosing {@link Connector} or {@link Concept} provided in a query. @@ -31,13 +30,13 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return EventDateUnionSqlAggregator.create(this, selectContext).getSqlSelects(); + public boolean isEventDateSelect() { + return true; } @Override - public boolean isEventDateSelect() { - return true; + public SelectConverter createConverter() { + return new EventDateUnionSelectConverter(); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDurationSumSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDurationSumSelect.java index a0eba1315c..416138c5b8 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDurationSumSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/EventDurationSumSelect.java @@ -6,9 +6,8 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.EventDurationSumAggregator; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.aggregator.EventDurationSumSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.EventDurationSumSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.google.common.base.Preconditions; import lombok.AccessLevel; import lombok.NoArgsConstructor; @@ -32,13 +31,13 @@ public static EventDurationSumSelect create(String name) { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return EventDurationSumSqlAggregator.create(this, selectContext).getSqlSelects(); + public boolean isEventDateSelect() { + return true; } @Override - public boolean isEventDateSelect() { - return true; + public SelectConverter createConverter() { + return new EventDurationSumSelectConverter(); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/ExistsSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/ExistsSelect.java index 2c7ec831a1..7792dc664c 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/ExistsSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/concept/specific/ExistsSelect.java @@ -10,34 +10,34 @@ import com.bakdata.conquery.models.datasets.concepts.select.concept.UniversalSelect; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.ExistsAggregator; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.aggregator.ExistsSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ExistsSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Getter; @CPSType(id = "EXISTS", base = Select.class) public class ExistsSelect extends UniversalSelect { - @JsonIgnore @Getter(lazy=true) + @JsonIgnore + @Getter(lazy = true) private final Set requiredTables = collectRequiredTables(); - + @Override public ExistsAggregator createAggregator() { return new ExistsAggregator(getRequiredTables()); } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return ExistsSqlAggregator.create(this, selectContext).getSqlSelects(); - } - - private Set
collectRequiredTables() { - return this.getHolder().findConcept().getConnectors().stream().map(Connector::getTable).collect(Collectors.toSet()); + public SelectConverter createConverter() { + return new ExistsSelectConverter(); } @Override public ResultType getResultType() { return ResultType.BooleanT.INSTANCE; } + + private Set
collectRequiredTables() { + return this.getHolder().findConcept().getConnectors().stream().map(Connector::getTable).collect(Collectors.toSet()); + } } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/FirstValueSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/FirstValueSelect.java index 4729637d80..e4e4288478 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/FirstValueSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/FirstValueSelect.java @@ -8,9 +8,8 @@ import com.bakdata.conquery.models.index.InternToExternMapper; import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.value.FirstValueAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.aggregator.FirstValueSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.FirstValueSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; @CPSType(id = "FIRST", base = Select.class) @@ -28,8 +27,7 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return FirstValueSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new FirstValueSelectConverter(); } - } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/LastValueSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/LastValueSelect.java index aa48276681..1112b8bb9a 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/LastValueSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/LastValueSelect.java @@ -8,9 +8,8 @@ import com.bakdata.conquery.models.index.InternToExternMapper; import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.value.LastValueAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.aggregator.LastValueSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.LastValueSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; @CPSType(id = "LAST", base = Select.class) @@ -28,8 +27,7 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return LastValueSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new LastValueSelectConverter(); } - } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/RandomValueSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/RandomValueSelect.java index 79f9cab147..458330c893 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/RandomValueSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/RandomValueSelect.java @@ -8,9 +8,8 @@ import com.bakdata.conquery.models.index.InternToExternMapper; import com.bakdata.conquery.models.query.queryplan.aggregators.Aggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.value.RandomValueAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.aggregator.RandomValueSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.RandomValueSelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; @CPSType(id = "RANDOM", base = Select.class) @@ -27,8 +26,7 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return RandomValueSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new RandomValueSelectConverter(); } - } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountQuartersSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountQuartersSelect.java index 3813816a8e..29a36531db 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountQuartersSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountQuartersSelect.java @@ -12,9 +12,8 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountQuartersOfDateRangeAggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountQuartersOfDatesAggregator; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.select.CountQuartersSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.aggregator.CountQuartersSqlAggregator; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; /** @@ -45,11 +44,8 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - if (getColumn().getType() == MajorTypeId.DATE_RANGE) { - throw new UnsupportedOperationException("COUNT_QUARTERS conversion on columns of type DATE_RANGE not implemented yet."); - } - return CountQuartersSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new CountQuartersSqlAggregator(); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountSelect.java index 65a0754330..305c57c66e 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/CountSelect.java @@ -14,8 +14,7 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.CountAggregator; import com.bakdata.conquery.models.types.ResultType; import com.bakdata.conquery.sql.conversion.model.aggregator.CountSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import jakarta.validation.constraints.NotNull; import lombok.Data; import lombok.NoArgsConstructor; @@ -55,7 +54,7 @@ public List getRequiredColumns() { final List out = new ArrayList<>(); out.add(getColumn()); - if(distinctByColumn != null) { + if (distinctByColumn != null) { out.addAll(getDistinctByColumn()); } @@ -63,8 +62,8 @@ public List getRequiredColumns() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return CountSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new CountSqlAggregator(); } diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/DateDistanceSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/DateDistanceSelect.java index 0c91993cd3..7969f11d61 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/DateDistanceSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/DateDistanceSelect.java @@ -13,8 +13,7 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.DateDistanceAggregator; import com.bakdata.conquery.models.types.ResultType; import com.bakdata.conquery.sql.conversion.model.aggregator.DateDistanceSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; import jakarta.validation.constraints.NotNull; import lombok.Getter; @@ -44,8 +43,8 @@ public Aggregator createAggregator() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return DateDistanceSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new DateDistanceSqlAggregator(); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/FlagSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/FlagSelect.java index b0fd5ee72b..939e7f660c 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/FlagSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/FlagSelect.java @@ -14,8 +14,7 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.FlagsAggregator; import com.bakdata.conquery.models.types.ResultType; import com.bakdata.conquery.sql.conversion.model.aggregator.FlagSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import io.dropwizard.validation.ValidationMethod; @@ -26,7 +25,7 @@ /** * Implements a MultiSelect type filter, where an event can meet multiple criteria (as opposed to {@link MultiSelectFilter} which is restricted to one value per event). * This is achieved by using multiple {@link com.bakdata.conquery.models.types.ResultType.BooleanT} columns, each defining if one property is met or not. - * + *

* The selected flags are logically or-ed. */ @Getter @@ -63,8 +62,8 @@ public boolean isAllColumnsBoolean() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - return FlagSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new FlagSqlAggregator(); } @Override diff --git a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/SumSelect.java b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/SumSelect.java index a9ff3d3d93..ea03eddee7 100644 --- a/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/SumSelect.java +++ b/backend/src/main/java/com/bakdata/conquery/models/datasets/concepts/select/connector/specific/SumSelect.java @@ -23,10 +23,8 @@ import com.bakdata.conquery.models.query.queryplan.aggregators.specific.sum.MoneySumAggregator; import com.bakdata.conquery.models.query.queryplan.aggregators.specific.sum.RealSumAggregator; import com.bakdata.conquery.models.types.ResultType; -import com.bakdata.conquery.sql.conversion.model.aggregator.SumDistinctSqlAggregator; import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import com.fasterxml.jackson.annotation.JsonCreator; import com.fasterxml.jackson.annotation.JsonIgnore; import io.dropwizard.validation.ValidationMethod; @@ -118,7 +116,6 @@ public ResultType getResultType() { return ResultType.resolveResultType(getColumn().getType()); } - @ValidationMethod(message = "Column is not of Summable Type.") @JsonIgnore public boolean isSummableColumnType() { @@ -132,11 +129,7 @@ public boolean isColumnsOfSameType() { } @Override - public SqlSelects convertToSqlSelects(SelectContext selectContext) { - if (distinctByColumn != null && !distinctByColumn.isEmpty()) { - return SumDistinctSqlAggregator.create(this, selectContext).getSqlSelects(); - } - return SumSqlAggregator.create(this, selectContext).getSqlSelects(); + public SelectConverter createConverter() { + return new SumSqlAggregator<>(); } - } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java index e9f79f501f..27b626f93e 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQConceptConverter.java @@ -14,6 +14,7 @@ import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.tree.ConceptTreeChild; import com.bakdata.conquery.models.datasets.concepts.tree.ConceptTreeNode; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; import com.bakdata.conquery.models.query.queryplan.DateAggregationAction; import com.bakdata.conquery.sql.conversion.NodeConverter; import com.bakdata.conquery.sql.conversion.SharedAliases; @@ -21,7 +22,6 @@ import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; import com.bakdata.conquery.sql.conversion.model.ConqueryJoinType; -import com.bakdata.conquery.sql.conversion.model.NameGenerator; import com.bakdata.conquery.sql.conversion.model.QueryStep; import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner; import com.bakdata.conquery.sql.conversion.model.Selects; @@ -31,10 +31,11 @@ import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition; +import com.bakdata.conquery.sql.conversion.model.select.ConceptSqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SelectContext; import com.bakdata.conquery.sql.conversion.model.select.SqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; import com.bakdata.conquery.util.TablePrimaryColumnUtil; import com.google.common.base.Preconditions; import org.jooq.Condition; @@ -65,18 +66,18 @@ public Class getConversionClass() { @Override public ConversionContext convert(CQConcept cqConcept, ConversionContext context) { - TablePathGenerator pathGenerator = new TablePathGenerator(context); - List convertedConnectorTables = cqConcept.getTables().stream() - .flatMap(cqTable -> convertCqTable(pathGenerator, cqConcept, cqTable, context).stream()) - .toList(); + TablePath tablePath = new TablePath(cqConcept, context); + List convertedCQTables = cqConcept.getTables().stream() + .flatMap(cqTable -> convertCqTable(tablePath, cqConcept, cqTable, context).stream()) + .toList(); - QueryStep joinedStep = QueryStepJoiner.joinSteps(convertedConnectorTables, ConqueryJoinType.OUTER_JOIN, DateAggregationAction.MERGE, context); - QueryStep lastConceptStep = finishConceptConversion(joinedStep, cqConcept, pathGenerator, context); + QueryStep joinedStep = QueryStepJoiner.joinSteps(convertedCQTables, ConqueryJoinType.OUTER_JOIN, DateAggregationAction.MERGE, context); + QueryStep lastConceptStep = finishConceptConversion(joinedStep, cqConcept, tablePath, context); return context.withQueryStep(lastConceptStep); } - private Optional convertCqTable(TablePathGenerator pathGenerator, CQConcept cqConcept, CQTable cqTable, ConversionContext context) { - CQTableContext tableContext = createTableContext(pathGenerator, cqConcept, cqTable, context); + private Optional convertCqTable(TablePath tablePath, CQConcept cqConcept, CQTable cqTable, ConversionContext context) { + CQTableContext tableContext = createTableContext(tablePath, cqConcept, cqTable, context); Optional lastQueryStep = Optional.empty(); for (ConnectorCte queryStep : connectorCTEs) { Optional convertedStep = queryStep.convert(tableContext, lastQueryStep); @@ -89,23 +90,26 @@ private Optional convertCqTable(TablePathGenerator pathGenerator, CQC return lastQueryStep; } - private static QueryStep finishConceptConversion(QueryStep predecessor, CQConcept cqConcept, TablePathGenerator pathGenerator, ConversionContext context) { + private static QueryStep finishConceptConversion(QueryStep predecessor, CQConcept cqConcept, TablePath tablePath, ConversionContext context) { - ConceptConversionTables universalTables = pathGenerator.createUniversalTables(predecessor, cqConcept); + ConceptSqlTables universalTables = tablePath.createConceptTables(predecessor); Selects predecessorSelects = predecessor.getQualifiedSelects(); - SelectContext selectContext = new SelectContext(predecessorSelects.getIds(), predecessorSelects.getValidityDate(), universalTables, context); - List converted = cqConcept.getSelects().stream() - .map(select -> select.convertToSqlSelects(selectContext)) - .toList(); + Optional validityDate = predecessorSelects.getValidityDate(); + SqlIdColumns ids = predecessorSelects.getIds(); + + SelectContext selectContext = SelectContext.create(cqConcept, ids, validityDate, universalTables, context); + List converted = cqConcept.getSelects().stream() + .map(select -> select.createConverter().conceptSelect(select, selectContext)) + .toList(); + + List queriesToJoin = new ArrayList<>(); + queriesToJoin.add(predecessor); + converted.stream().map(ConceptSqlSelects::getAdditionalPredecessor).filter(Optional::isPresent).map(Optional::get).forEach(queriesToJoin::add); - List queriesToJoin; if (universalTables.isRequiredStep(ConceptCteStep.INTERVAL_PACKING_SELECTS)) { QueryStep eventDateSelectsStep = IntervalPackingSelectsCte.forConcept(predecessor, universalTables, converted, context); - queriesToJoin = List.of(predecessor, eventDateSelectsStep); - } - else { - queriesToJoin = List.of(predecessor); + queriesToJoin.add(eventDateSelectsStep); } // combine all universal selects and connector selects from preceding step @@ -116,9 +120,9 @@ private static QueryStep finishConceptConversion(QueryStep predecessor, CQConcep .toList(); Selects finalSelects = Selects.builder() - .ids(predecessorSelects.getIds()) + .ids(ids) .stratificationDate(predecessorSelects.getStratificationDate()) - .validityDate(predecessorSelects.getValidityDate()) + .validityDate(validityDate) .sqlSelects(allConceptSelects) .build(); @@ -132,19 +136,14 @@ private static QueryStep finishConceptConversion(QueryStep predecessor, CQConcep .build(); } - private CQTableContext createTableContext(TablePathGenerator pathGenerator, CQConcept cqConcept, CQTable cqTable, ConversionContext conversionContext) { - - NameGenerator nameGenerator = conversionContext.getNameGenerator(); - SqlFunctionProvider functionProvider = conversionContext.getSqlDialect().getFunctionProvider(); - - Connector connector = cqTable.getConnector(); - String conceptConnectorLabel = nameGenerator.conceptConnectorName(cqConcept, connector); + private CQTableContext createTableContext(TablePath tablePath, CQConcept cqConcept, CQTable cqTable, ConversionContext conversionContext) { SqlIdColumns ids = convertIds(cqConcept, cqTable, conversionContext); - Optional tablesValidityDate = convertValidityDate(cqTable, conceptConnectorLabel, conversionContext); - ConceptConversionTables connectorTables = pathGenerator.createConnectorTables(cqConcept, cqTable, conceptConnectorLabel); + ConnectorSqlTables connectorTables = tablePath.getConnectorTables(cqTable); + Optional tablesValidityDate = convertValidityDate(cqTable, connectorTables.getLabel(), conversionContext); // convert filters + SqlFunctionProvider functionProvider = conversionContext.getSqlDialect().getFunctionProvider(); List allSqlFiltersForTable = new ArrayList<>(); cqTable.getFilters().stream() .map(filterValue -> filterValue.convertToSqlFilter(ids, conversionContext, connectorTables)) @@ -153,10 +152,10 @@ private CQTableContext createTableContext(TablePathGenerator pathGenerator, CQCo getDateRestriction(conversionContext, tablesValidityDate).ifPresent(allSqlFiltersForTable::add); // convert selects - SelectContext selectContext = new SelectContext(ids, tablesValidityDate, connectorTables, conversionContext); - List allSelectsForTable = cqTable.getSelects().stream() - .map(select -> select.convertToSqlSelects(selectContext)) - .toList(); + SelectContext selectContext = SelectContext.create(cqTable, ids, tablesValidityDate, connectorTables, conversionContext); + List allSelectsForTable = cqTable.getSelects().stream() + .map(select -> select.createConverter().connectorSelect(select, selectContext)) + .toList(); return CQTableContext.builder() .ids(ids) @@ -217,7 +216,7 @@ private static Optional collectConditionFilters(List new SqlFilters( - SqlSelects.builder().build(), + ConnectorSqlSelects.none(), WhereClauses.builder().preprocessingCondition(whereCondition).build() )); } @@ -271,7 +270,7 @@ private static Optional getDateRestriction(ConversionContext context Condition dateRestrictionCondition = functionProvider.dateRestriction(dateRestriction, validityDate.get()); return Optional.of(new SqlFilters( - SqlSelects.builder().preprocessingSelects(dateRestrictionSelects).build(), + ConnectorSqlSelects.builder().preprocessingSelects(dateRestrictionSelects).build(), WhereClauses.builder().eventFilter(ConditionUtil.wrap(dateRestrictionCondition, ConditionType.EVENT)).build() )); } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java index f3ac238008..26f881a23f 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/CQTableContext.java @@ -12,7 +12,7 @@ import com.bakdata.conquery.sql.conversion.model.QueryStep; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import lombok.Builder; import lombok.Value; import lombok.With; @@ -23,17 +23,17 @@ class CQTableContext implements Context { SqlIdColumns ids; Optional validityDate; - List sqlSelects; + List sqlSelects; List sqlFilters; - ConceptConversionTables connectorTables; + ConnectorSqlTables connectorTables; ConversionContext conversionContext; @With QueryStep previous; /** - * @return All concepts {@link SqlSelects} that are either required for {@link Filter}'s or {@link Select}'s. + * @return All {@link ConnectorSqlSelects} that are either required for {@link Filter}'s or {@link Select}'s. */ - public List allSqlSelects() { + public List allSqlSelects() { return Stream.concat(sqlSelects.stream(), sqlFilters.stream().map(SqlFilters::getSelects)).toList(); } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptSqlTables.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptSqlTables.java new file mode 100644 index 0000000000..ce9c58994f --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptSqlTables.java @@ -0,0 +1,26 @@ +package com.bakdata.conquery.sql.conversion.cqelement.concept; + +import java.util.List; +import java.util.Map; + +import com.bakdata.conquery.sql.conversion.model.CteStep; +import lombok.Getter; + +@Getter +public class ConceptSqlTables extends ConnectorSqlTables { + + private final List connectorTables; + + public ConceptSqlTables( + String conceptConnectorLabel, + String rootTable, + Map cteNameMap, + Map predecessorMap, + boolean containsIntervalPacking, + List connectorTables + ) { + super(conceptConnectorLabel, rootTable, cteNameMap, predecessorMap, containsIntervalPacking); + this.connectorTables = connectorTables; + } + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptConversionTables.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConnectorSqlTables.java similarity index 72% rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptConversionTables.java rename to backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConnectorSqlTables.java index 6ba2cf8c88..6f226a4119 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConceptConversionTables.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/ConnectorSqlTables.java @@ -8,27 +8,27 @@ import lombok.Getter; @Getter -public class ConceptConversionTables extends SqlTables { +public class ConnectorSqlTables extends SqlTables { /** - * Stores the name of the predecessor of the last CTE these tables contain. + * A unique label for these tables to create unique SQL CTE/Select names. */ - private final String lastPredecessor; + private final String label; /** * True if these tables contain interval packing CTEs {@link IntervalPackingCteStep}. */ private final boolean withIntervalPacking; - public ConceptConversionTables( + public ConnectorSqlTables( + String conceptConnectorLabel, String rootTable, Map cteNameMap, Map predecessorMap, - String lastPredecessor, boolean containsIntervalPacking ) { super(rootTable, cteNameMap, predecessorMap); - this.lastPredecessor = lastPredecessor; + this.label = conceptConnectorLabel; this.withIntervalPacking = containsIntervalPacking; } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/EventFilterCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/EventFilterCte.java index ddc46dcd3c..bc13891a0c 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/EventFilterCte.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/EventFilterCte.java @@ -9,12 +9,12 @@ import com.bakdata.conquery.sql.conversion.model.QueryStep; import com.bakdata.conquery.sql.conversion.model.Selects; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; -import com.bakdata.conquery.sql.conversion.model.aggregator.SumDistinctSqlAggregator; +import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator; import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; import com.google.common.base.Preconditions; import org.jooq.Condition; @@ -22,7 +22,7 @@ class EventFilterCte extends ConnectorCte { @Override public QueryStep.QueryStepBuilder convertStep(CQTableContext tableContext) { - Selects eventFilterSelects = colltectSelects(tableContext); + Selects eventFilterSelects = collectSelects(tableContext); List eventFilterConditions = collectEventFilterConditions(tableContext); return QueryStep.builder() .selects(eventFilterSelects) @@ -34,7 +34,7 @@ public ConceptCteStep cteStep() { return ConceptCteStep.EVENT_FILTER; } - private Selects colltectSelects(CQTableContext tableContext) { + private Selects collectSelects(CQTableContext tableContext) { String predecessorTableName = tableContext.getPrevious().getCteName(); Selects predecessorSelects = tableContext.getPrevious().getQualifiedSelects(); @@ -59,11 +59,11 @@ private Selects colltectSelects(CQTableContext tableContext) { /** * Collects the columns required in {@link ConceptCteStep#AGGREGATION_SELECT}, but also columns additional tables require (like the ones created by the - * {@link SumDistinctSqlAggregator}). An additional predecessor can contain an N-ary tree of predecessors itself (like all {@link QueryStep}s), so we want to - * look for the deepest preceding QueryStep leafs and collect their {@link SqlSelects}, because they expect this CTE to contain all their - * {@link SqlSelect#requiredColumns()}. + * {@link SumSqlAggregator}) when distinct-by columns are present. An additional predecessor can contain an N-ary tree of predecessors itself + * (like all {@link QueryStep}s), so we want to look for the deepest preceding QueryStep leafs and collect their {@link ConnectorSqlSelects}, + * because they expect this CTE to contain all their {@link SqlSelect#requiredColumns()}. */ - private static List collectSelects(SqlSelects sqlSelects) { + private static List collectSelects(ConnectorSqlSelects sqlSelects) { return Stream.concat( sqlSelects.getAggregationSelects().stream(), sqlSelects.getAdditionalPredecessor().map(EventFilterCte::collectDeepestPredecessorsColumns).orElse(Stream.empty()) diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java index d93f92596b..c482280e91 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/FilterContext.java @@ -5,6 +5,7 @@ import com.bakdata.conquery.apiv1.query.concept.filter.FilterValue; import com.bakdata.conquery.sql.conversion.Context; import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; +import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; import lombok.AccessLevel; import lombok.AllArgsConstructor; @@ -27,9 +28,9 @@ public class FilterContext implements Context { * Not present if this context is for table export. */ @Nullable - ConceptConversionTables tables; + ConnectorSqlTables tables; - public static FilterContext forConceptConversion(SqlIdColumns ids, V value, ConversionContext conversionContext, ConceptConversionTables tables) { + public static FilterContext forConceptConversion(SqlIdColumns ids, V value, ConversionContext conversionContext, ConnectorSqlTables tables) { return new FilterContext<>(ids, value, conversionContext, tables); } @@ -37,4 +38,8 @@ public static FilterContext forTableExport(SqlIdColumns ids, V value, Con return new FilterContext<>(ids, value, conversionContext, null); } + public SqlFunctionProvider getFunctionProvider() { + return getSqlDialect().getFunctionProvider(); + } + } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java index d4b234c69b..973381cae3 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/IntervalPackingSelectsCte.java @@ -2,6 +2,7 @@ import java.util.List; +import com.bakdata.conquery.models.datasets.concepts.select.Select; import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDateUnionSelect; import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDurationSumSelect; import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; @@ -9,8 +10,8 @@ import com.bakdata.conquery.sql.conversion.model.QueryStep; import com.bakdata.conquery.sql.conversion.model.Selects; import com.bakdata.conquery.sql.conversion.model.SqlTables; +import com.bakdata.conquery.sql.conversion.model.select.ConceptSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.SqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; import com.google.common.base.Preconditions; class IntervalPackingSelectsCte { @@ -18,7 +19,7 @@ class IntervalPackingSelectsCte { public static QueryStep forConnector(QueryStep predecessor, CQTableContext cqTableContext) { return create( predecessor, - cqTableContext.getSqlSelects(), + cqTableContext.getSqlSelects().stream().flatMap(selects -> selects.getEventDateSelects().stream()).toList(), cqTableContext.getConnectorTables(), cqTableContext.getConversionContext().getSqlDialect().getFunctionProvider() ); @@ -27,12 +28,12 @@ public static QueryStep forConnector(QueryStep predecessor, CQTableContext cqTab public static QueryStep forConcept( QueryStep predecessor, SqlTables tables, - List sqlSelects, + List sqlSelects, ConversionContext conversionContext ) { return create( predecessor, - sqlSelects, + sqlSelects.stream().flatMap(selects -> selects.getEventDateSelects().stream()).toList(), tables, conversionContext.getSqlDialect().getFunctionProvider() ); @@ -40,17 +41,16 @@ public static QueryStep forConcept( /** * @param predecessor The preceding query step which must contain an aggregated validity date. - * @param sqlSelects {@link SqlSelects} who's interval packing selects will be part of the returned {@link QueryStep}. + * @param intervalPackingSelects {@link Select}s that require an interval-packed date. * @return A {@link QueryStep} containing converted interval packing selects, like {@link EventDurationSumSelect}, {@link EventDateUnionSelect}, etc. - * Returns the given predecessor as is if there are no interval packing selects in the given list of {@link SqlSelects}. + * Returns the given predecessor as is if the given list of interval packing selects is empty. */ private static QueryStep create( QueryStep predecessor, - List sqlSelects, + List intervalPackingSelects, SqlTables tables, SqlFunctionProvider functionProvider ) { - List intervalPackingSelects = sqlSelects.stream().flatMap(selects -> selects.getIntervalPackingSelects().stream()).toList(); if (intervalPackingSelects.isEmpty()) { return predecessor; } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java index 54d99cdabf..f1c91db84e 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/JoinBranchesCte.java @@ -12,7 +12,7 @@ import com.bakdata.conquery.sql.conversion.model.QueryStepJoiner; import com.bakdata.conquery.sql.conversion.model.Selects; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; -import com.bakdata.conquery.sql.conversion.model.aggregator.SumDistinctSqlAggregator; +import com.bakdata.conquery.sql.conversion.model.aggregator.SumSqlAggregator; import com.bakdata.conquery.sql.conversion.model.select.SqlSelect; import org.jooq.Record; import org.jooq.TableLike; @@ -22,7 +22,7 @@ * {@link IntervalPackingSelectsCte} as well as optional additional predecessors. *

* Joining is optional - if a validity date is not present, the node is excluded from time aggregation or if there is no additional predecessor, no join will - * take place. See {@link SumDistinctSqlAggregator} for an example of additional predecessors. + * take place. See {@link SumSqlAggregator} with distinct-by columns for an example of additional predecessors. * *

  *     {@code
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/SelectFilterUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/SelectFilterUtil.java
deleted file mode 100644
index 8b2862bc06..0000000000
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/SelectFilterUtil.java
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.bakdata.conquery.sql.conversion.cqelement.concept;
-
-import com.bakdata.conquery.models.datasets.concepts.filters.specific.SelectFilter;
-import com.bakdata.conquery.sql.conversion.model.filter.MultiSelectCondition;
-import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters;
-import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses;
-import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition;
-import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
-import com.bakdata.conquery.sql.conversion.model.select.SqlSelects;
-
-public class SelectFilterUtil {
-
-	public static  SqlFilters convert(SelectFilter selectFilter, FilterContext context, String[] values) {
-		ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(
-				context.getTables().getPredecessor(ConceptCteStep.PREPROCESSING),
-				selectFilter.getColumn().getName(),
-				String.class
-		);
-
-		WhereCondition condition = new MultiSelectCondition(
-				rootSelect.qualify(context.getTables().getPredecessor(ConceptCteStep.EVENT_FILTER)).select(),
-				values,
-				context.getConversionContext().getSqlDialect().getFunctionProvider()
-		);
-
-		return new SqlFilters(
-				SqlSelects.builder()
-						  .preprocessingSelect(rootSelect)
-						  .build(),
-				WhereClauses.builder()
-							.eventFilter(condition)
-							.build()
-		);
-	}
-
-}
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePathGenerator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
similarity index 65%
rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePathGenerator.java
rename to backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
index 54eb8923c2..452a0b147f 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePathGenerator.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/cqelement/concept/TablePath.java
@@ -2,13 +2,13 @@
 
 import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.EVENT_FILTER;
 import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.INTERVAL_PACKING_SELECTS;
-import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.JOIN_BRANCHES;
 import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.MANDATORY_STEPS;
 import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.UNIVERSAL_SELECTS;
 import static com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep.UNNEST_DATE;
 import static com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingCteStep.INTERVAL_COMPLETE;
 
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -17,55 +17,73 @@
 import com.bakdata.conquery.models.datasets.concepts.select.Select;
 import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext;
 import com.bakdata.conquery.sql.conversion.cqelement.intervalpacking.IntervalPackingCteStep;
-import com.bakdata.conquery.sql.conversion.dialect.SqlDialect;
 import com.bakdata.conquery.sql.conversion.model.CteStep;
-import com.bakdata.conquery.sql.conversion.model.NameGenerator;
 import com.bakdata.conquery.sql.conversion.model.QueryStep;
-import com.bakdata.conquery.sql.conversion.model.Selects;
 import com.google.common.base.Preconditions;
 import lombok.Data;
-import lombok.Value;
+import lombok.Getter;
 
-@Value
-class TablePathGenerator {
+/**
+ * Determines all table/CTE names and creates the respective required {@link ConnectorSqlTables} and {@link ConceptSqlTables} which will be created during the
+ * conversion of a {@link CQConcept}.
+ */
+class TablePath {
 
-	SqlDialect sqlDialect;
-	NameGenerator nameGenerator;
+	private final Map connectorTableMap = new HashMap<>();
 
-	public TablePathGenerator(ConversionContext context) {
-		this.sqlDialect = context.getSqlDialect();
-		this.nameGenerator = context.getNameGenerator();
-	}
+	@Getter
+	private final CQConcept cqConcept;
+
+	@Getter
+	private final ConversionContext context;
 
-	public ConceptConversionTables createConnectorTables(CQConcept cqConcept, CQTable cqTable, String label) {
-		TablePathInfo tableInfo = collectConnectorTables(cqConcept, cqTable);
-		return create(tableInfo, label);
+	public TablePath(CQConcept cqConcept, ConversionContext context) {
+		this.cqConcept = cqConcept;
+		this.context = context;
+		cqConcept.getTables().forEach(cqTable -> this.connectorTableMap.put(cqTable, createConnectorTables(cqConcept, cqTable, context)));
 	}
 
-	public ConceptConversionTables createUniversalTables(QueryStep predecessor, CQConcept cqConcept) {
-		TablePathInfo tableInfo = collectConceptTables(predecessor, cqConcept);
-		String conceptName = nameGenerator.conceptName(cqConcept);
-		return create(tableInfo, conceptName);
+	public ConnectorSqlTables getConnectorTables(CQTable cqTable) {
+		return connectorTableMap.get(cqTable);
 	}
 
-	private ConceptConversionTables create(TablePathInfo tableInfo, String label) {
-		Map cteNameMap = CteStep.createCteNameMap(tableInfo.getMappings().keySet(), label, nameGenerator);
-		String lastPredecessorName = cteNameMap.get(tableInfo.getLastPredecessor());
-		return new ConceptConversionTables(
+	private static ConnectorSqlTables createConnectorTables(CQConcept cqConcept, CQTable cqTable, ConversionContext context) {
+
+		String conceptConnectorLabel = context.getNameGenerator().conceptConnectorName(cqConcept, cqTable.getConnector());
+		TablePathInfo tableInfo = collectConnectorTables(cqConcept, cqTable, context);
+		Map cteNameMap = CteStep.createCteNameMap(tableInfo.getMappings().keySet(), conceptConnectorLabel, context.getNameGenerator());
+
+		return new ConnectorSqlTables(
+				conceptConnectorLabel,
 				tableInfo.getRootTable(),
 				cteNameMap,
 				tableInfo.getMappings(),
-				lastPredecessorName,
 				tableInfo.isContainsIntervalPacking()
 		);
 	}
 
-	private TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable cqTable) {
+	public ConceptSqlTables createConceptTables(QueryStep predecessor) {
+
+		TablePathInfo tableInfo = collectConceptTables(predecessor);
+		String conceptName = context.getNameGenerator().conceptName(cqConcept);
+		Map cteNameMap = CteStep.createCteNameMap(tableInfo.getMappings().keySet(), conceptName, context.getNameGenerator());
+		List connectorSqlTables = this.connectorTableMap.values().stream().toList();
+
+		return new ConceptSqlTables(
+				conceptName,
+				tableInfo.getRootTable(),
+				cteNameMap,
+				tableInfo.getMappings(),
+				tableInfo.isContainsIntervalPacking(),
+				connectorSqlTables
+		);
+	}
+
+	private static TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable cqTable, ConversionContext context) {
 
 		TablePathInfo tableInfo = new TablePathInfo();
 		tableInfo.setRootTable(cqTable.getConnector().getTable().getName());
 		tableInfo.addWithDefaultMapping(MANDATORY_STEPS);
-		tableInfo.setLastPredecessor(JOIN_BRANCHES);
 
 		boolean eventDateSelectsPresent = cqTable.getSelects().stream().anyMatch(Select::isEventDateSelect);
 		// no validity date aggregation possible nor necessary
@@ -75,14 +93,14 @@ private TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable cqTabl
 
 		// interval packing required
 		tableInfo.setContainsIntervalPacking(true);
-		tableInfo.addMappings(IntervalPackingCteStep.getMappings(EVENT_FILTER, sqlDialect));
+		tableInfo.addMappings(IntervalPackingCteStep.getMappings(EVENT_FILTER, context.getSqlDialect()));
 
 		if (!eventDateSelectsPresent) {
 			return tableInfo;
 		}
 
 		// interval packing selects required with optional unnest step
-		if (sqlDialect.supportsSingleColumnRanges()) {
+		if (context.getSqlDialect().supportsSingleColumnRanges()) {
 			tableInfo.addMappings(Map.of(
 					UNNEST_DATE, INTERVAL_COMPLETE,
 					INTERVAL_PACKING_SELECTS, UNNEST_DATE
@@ -97,7 +115,7 @@ private TablePathInfo collectConnectorTables(CQConcept cqConcept, CQTable cqTabl
 		return tableInfo;
 	}
 
-	private TablePathInfo collectConceptTables(QueryStep predecessor, CQConcept cqConcept) {
+	private TablePathInfo collectConceptTables(QueryStep predecessor) {
 
 		TablePathInfo tableInfo = new TablePathInfo();
 		tableInfo.setRootTable(predecessor.getCteName()); // last table of a single connector or merged and aggregated table of multiple connectors
@@ -110,11 +128,11 @@ private TablePathInfo collectConceptTables(QueryStep predecessor, CQConcept cqCo
 
 		Preconditions.checkArgument(
 				predecessor.getSelects().getValidityDate().isPresent(),
-				"Can not convert Selects that require interval packing without a validity date present in QueryStep %s".formatted(predecessor)
+				"Can not convert Selects that require interval packing without a validity date present after converting (a) connector(s)"
 		);
 
 		// universal event date selects required with optional additional unnest step
-		if (sqlDialect.supportsSingleColumnRanges()) {
+		if (context.getSqlDialect().supportsSingleColumnRanges()) {
 			tableInfo.addRootTableMapping(UNNEST_DATE);
 			tableInfo.addMappings(Map.of(INTERVAL_PACKING_SELECTS, UNNEST_DATE));
 		}
@@ -122,6 +140,8 @@ private TablePathInfo collectConceptTables(QueryStep predecessor, CQConcept cqCo
 			tableInfo.addRootTableMapping(INTERVAL_PACKING_SELECTS);
 		}
 
+		tableInfo.addMappings(Map.of(UNIVERSAL_SELECTS, INTERVAL_PACKING_SELECTS));
+
 		return tableInfo;
 	}
 
@@ -138,12 +158,6 @@ private static class TablePathInfo {
 		 */
 		private String rootTable;
 
-		/**
-		 * When converting {@link Selects}, we need to qualify the final references onto the predecessor of the last CTE that is part of the conversion.
-		 * It varies depending on the given {@link CQConcept}, thus we need to set it explicitly.
-		 */
-		private CteStep lastPredecessor;
-
 		/**
 		 * True if this path info contains CTEs from {@link IntervalPackingCteStep}.
 		 */
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberMapUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/NumberMapUtil.java
similarity index 80%
rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberMapUtil.java
rename to backend/src/main/java/com/bakdata/conquery/sql/conversion/model/NumberMapUtil.java
index d50bcbf75e..1523e36515 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberMapUtil.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/NumberMapUtil.java
@@ -1,11 +1,11 @@
-package com.bakdata.conquery.sql.conversion.model.aggregator;
+package com.bakdata.conquery.sql.conversion.model;
 
 import java.math.BigDecimal;
 import java.util.Map;
 
 import com.bakdata.conquery.models.events.MajorTypeId;
 
-class NumberMapUtil {
+public class NumberMapUtil {
 
 	public static final Map> NUMBER_MAP = Map.of(
 			MajorTypeId.MONEY, BigDecimal.class,
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
index 6c643eeca0..0a542da464 100644
--- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/QueryStepJoiner.java
@@ -50,14 +50,14 @@ public static QueryStep joinSteps(
 			return queriesToJoin.get(0);
 		}
 
-		String joinedCteName = context.getNameGenerator().joinedNodeName(logicalOperation);
+		String joinedNodeName = context.getNameGenerator().joinedNodeName(logicalOperation);
 		SqlIdColumns ids = coalesceIds(queriesToJoin);
 		List mergedSelects = mergeSelects(queriesToJoin);
 		TableLike joinedTable = constructJoinedTable(queriesToJoin, logicalOperation, context);
 
 		QueryStep joinedStep;
 		QueryStep.QueryStepBuilder joinedStepBuilder = QueryStep.builder()
-																.cteName(joinedCteName)
+																.cteName(joinedNodeName)
 																.fromTable(joinedTable)
 																.predecessors(queriesToJoin);
 
diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
new file mode 100644
index 0000000000..ab79e7c7b4
--- /dev/null
+++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CommonAggregationSelect.java
@@ -0,0 +1,40 @@
+package com.bakdata.conquery.sql.conversion.model.aggregator;
+
+import java.util.List;
+import java.util.Optional;
+
+import com.bakdata.conquery.models.datasets.concepts.filters.Filter;
+import com.bakdata.conquery.models.datasets.concepts.select.Select;
+import com.bakdata.conquery.sql.conversion.model.QueryStep;
+import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects;
+import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect;
+import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper;
+import com.bakdata.conquery.sql.conversion.model.select.SqlSelect;
+import lombok.Builder;
+import lombok.Singular;
+import lombok.Value;
+
+/**
+ * Container object for parts of the {@link ConnectorSqlSelects}.
+ * 

+ * {@link Select}s and {@link Filter}s like COUNT and SUM share the majority of their {@link ConnectorSqlSelects} when being converted. This container makes + * the respective shared {@link SqlSelect}s and the optional additional preceding {@link QueryStep} accessible for building {@link ConnectorSqlSelects} on + * demand. + * + * @param The type parameter of the aggregation select. + */ +@Value +@Builder +class CommonAggregationSelect { + + @Singular + List> rootSelects; + + FieldWrapper groupBy; + + QueryStep additionalPredecessor; + + public Optional getAdditionalPredecessor() { + return Optional.ofNullable(additionalPredecessor); + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java index e9195c10e2..79d667bcf8 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountQuartersSqlAggregator.java @@ -1,82 +1,99 @@ -package com.bakdata.conquery.sql.conversion.model.select; +package com.bakdata.conquery.sql.conversion.model.aggregator; import java.sql.Date; -import com.bakdata.conquery.models.common.IRange; import com.bakdata.conquery.models.common.Range; import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.filters.specific.CountQuartersFilter; import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.CountQuartersSelect; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.aggregator.SqlAggregator; import com.bakdata.conquery.sql.conversion.model.filter.CountCondition; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import lombok.Value; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; +import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; +import com.bakdata.conquery.sql.conversion.model.select.SelectContext; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; +import org.jooq.Condition; import org.jooq.Field; +import org.jooq.Param; import org.jooq.impl.DSL; -@Value -public class CountQuartersSqlAggregator implements SqlAggregator { +public class CountQuartersSqlAggregator implements SelectConverter, FilterConverter { - SqlSelects sqlSelects; - WhereClauses whereClauses; + @Override + public ConnectorSqlSelects connectorSelect(CountQuartersSelect countQuartersSelect, SelectContext selectContext) { - private CountQuartersSqlAggregator( - Column column, - String alias, - SqlTables connectorTables, - SqlFunctionProvider functionProvider, - IRange filterValue - ) { - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), column.getName(), Date.class); + Column countColumn = countQuartersSelect.getColumn(); + String alias = selectContext.getNameGenerator().selectName(countQuartersSelect); + ConnectorSqlTables tables = selectContext.getTables(); + SqlFunctionProvider functionProvider = selectContext.getFunctionProvider(); - Field qualifiedRootSelect = rootSelect.qualify(connectorTables.cteName(ConceptCteStep.EVENT_FILTER)).select(); - FieldWrapper countQuartersField = new FieldWrapper<>( - DSL.countDistinct(functionProvider.yearQuarter(qualifiedRootSelect)).as(alias), - column.getName() - ); + CommonAggregationSelect countAggregationSelect = createCountQuartersAggregationSelect(countColumn, alias, tables, functionProvider); + + String finalPredecessor = tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); + ExtractingSqlSelect finalSelect = countAggregationSelect.getGroupBy().qualify(finalPredecessor); - SqlSelects.SqlSelectsBuilder builder = SqlSelects.builder() - .preprocessingSelect(rootSelect) - .aggregationSelect(countQuartersField); - - String aggregationFilterPredecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); - if (filterValue == null) { - ExtractingSqlSelect finalSelect = countQuartersField.qualify(aggregationFilterPredecessor); - this.sqlSelects = builder.finalSelect(finalSelect).build(); - this.whereClauses = WhereClauses.empty(); - } - else { - this.sqlSelects = builder.build(); - Field qualified = countQuartersField.qualify(aggregationFilterPredecessor).select(); - CountCondition countCondition = new CountCondition(qualified, filterValue); - this.whereClauses = WhereClauses.builder() - .groupFilter(countCondition) - .build(); - } + return ConnectorSqlSelects.builder() + .preprocessingSelects(countAggregationSelect.getRootSelects()) + .aggregationSelect(countAggregationSelect.getGroupBy()) + .finalSelect(finalSelect) + .build(); } - public static CountQuartersSqlAggregator create(CountQuartersSelect countQuartersSelect, SelectContext selectContext) { - return new CountQuartersSqlAggregator( - countQuartersSelect.getColumn(), - selectContext.getNameGenerator().selectName(countQuartersSelect), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider(), - null - ); + @Override + public SqlFilters convertToSqlFilter(CountQuartersFilter countQuartersFilter, FilterContext filterContext) { + + Column countColumn = countQuartersFilter.getColumn(); + String alias = filterContext.getNameGenerator().selectName(countQuartersFilter); + ConnectorSqlTables tables = filterContext.getTables(); + SqlFunctionProvider functionProvider = filterContext.getSqlDialect().getFunctionProvider(); + + CommonAggregationSelect countAggregationSelect = createCountQuartersAggregationSelect(countColumn, alias, tables, functionProvider); + ConnectorSqlSelects selects = ConnectorSqlSelects.builder() + .preprocessingSelects(countAggregationSelect.getRootSelects()) + .aggregationSelect(countAggregationSelect.getGroupBy()) + .build(); + + Field qualifiedCountSelect = countAggregationSelect.getGroupBy().qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)).select(); + CountCondition countCondition = new CountCondition(qualifiedCountSelect, filterContext.getValue()); + WhereClauses whereClauses = WhereClauses.builder() + .groupFilter(countCondition) + .build(); + + return new SqlFilters(selects, whereClauses); + } + + @Override + public Condition convertForTableExport(CountQuartersFilter filter, FilterContext filterContext) { + Param field = DSL.val(1); // no grouping, count is always 1 per row + return new CountCondition(field, filterContext.getValue()).condition(); } - public static CountQuartersSqlAggregator create(CountQuartersFilter countQuartersFilter, FilterContext filterContext) { - return new CountQuartersSqlAggregator( - countQuartersFilter.getColumn(), - filterContext.getNameGenerator().selectName(countQuartersFilter), - filterContext.getTables(), - filterContext.getConversionContext().getSqlDialect().getFunctionProvider(), - filterContext.getValue() + private CommonAggregationSelect createCountQuartersAggregationSelect( + Column countColumn, + String alias, + ConnectorSqlTables tables, + SqlFunctionProvider functionProvider + ) { + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(tables.getRootTable(), countColumn.getName(), Date.class); + + Field qualifiedRootSelect = rootSelect.qualify(tables.cteName(ConceptCteStep.EVENT_FILTER)).select(); + FieldWrapper countQuartersAggregation = new FieldWrapper<>( + DSL.countDistinct(functionProvider.yearQuarter(qualifiedRootSelect)).as(alias), + countColumn.getName() ); + + return CommonAggregationSelect.builder() + .rootSelect(rootSelect) + .groupBy(countQuartersAggregation) + .build(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java index 346a436c47..8d41ce66a7 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/CountSqlAggregator.java @@ -1,98 +1,103 @@ package com.bakdata.conquery.sql.conversion.model.aggregator; -import com.bakdata.conquery.models.common.IRange; import com.bakdata.conquery.models.common.Range; import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.filters.specific.CountFilter; import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.CountSelect; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.SqlTables; import com.bakdata.conquery.sql.conversion.model.filter.CountCondition; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; +import lombok.NoArgsConstructor; +import org.jooq.Condition; import org.jooq.Field; +import org.jooq.Param; import org.jooq.impl.DSL; +@NoArgsConstructor +public class CountSqlAggregator implements SelectConverter, FilterConverter, SqlAggregator { -@Value -public class CountSqlAggregator implements SqlAggregator { + public enum CountType { + DEFAULT, + DISTINCT; + + public static CountType fromBoolean(boolean value) { + return value ? DISTINCT : DEFAULT; + } + } - SqlSelects sqlSelects; - WhereClauses whereClauses; + @Override + public ConnectorSqlSelects connectorSelect(CountSelect countSelect, SelectContext selectContext) { - private CountSqlAggregator( - Column countColumn, - CountType countType, - String alias, - SqlTables connectorTables, - IRange filterValue - ) { - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), countColumn.getName(), Object.class); + ConnectorSqlTables tables = selectContext.getTables(); + CountType countType = CountType.fromBoolean(countSelect.isDistinct()); + Column countColumn = countSelect.getColumn(); + String alias = selectContext.getNameGenerator().selectName(countSelect); - Field qualifiedRootSelect = rootSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); - Field countField = countType == CountType.DISTINCT - ? DSL.countDistinct(qualifiedRootSelect) - : DSL.count(qualifiedRootSelect); - FieldWrapper countGroupBy = new FieldWrapper<>(countField.as(alias), countColumn.getName()); + CommonAggregationSelect countAggregationSelect = createCountAggregationSelect(countColumn, countType, alias, tables); - SqlSelects.SqlSelectsBuilder builder = SqlSelects.builder() - .preprocessingSelect(rootSelect) - .aggregationSelect(countGroupBy); + String finalPredecessor = tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); + ExtractingSqlSelect finalSelect = countAggregationSelect.getGroupBy().qualify(finalPredecessor); - String finalPredecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); - if (filterValue == null) { - ExtractingSqlSelect finalSelect = countGroupBy.qualify(finalPredecessor); - this.sqlSelects = builder.finalSelect(finalSelect).build(); - this.whereClauses = WhereClauses.empty(); - } - else { - this.sqlSelects = builder.build(); - Field qualifiedCountSelect = countGroupBy.qualify(finalPredecessor).select(); - CountCondition countCondition = new CountCondition(qualifiedCountSelect, filterValue); - this.whereClauses = WhereClauses.builder() - .groupFilter(countCondition) - .build(); - } + return ConnectorSqlSelects.builder() + .preprocessingSelects(countAggregationSelect.getRootSelects()) + .aggregationSelect(countAggregationSelect.getGroupBy()) + .finalSelect(finalSelect) + .build(); } - public static CountSqlAggregator create(CountSelect countSelect, SelectContext selectContext) { - return new CountSqlAggregator( - countSelect.getColumn(), - CountType.fromBoolean(countSelect.isDistinct()), - selectContext.getNameGenerator().selectName(countSelect), - selectContext.getTables(), - null - ); - } + @Override + public SqlFilters convertToSqlFilter(CountFilter countFilter, FilterContext filterContext) { + + ConnectorSqlTables tables = filterContext.getTables(); + CountType countType = CountType.fromBoolean(countFilter.isDistinct()); + Column countColumn = countFilter.getColumn(); + String alias = filterContext.getNameGenerator().selectName(countFilter); + + CommonAggregationSelect countAggregationSelect = createCountAggregationSelect(countColumn, countType, alias, tables); + ConnectorSqlSelects selects = ConnectorSqlSelects.builder() + .preprocessingSelects(countAggregationSelect.getRootSelects()) + .aggregationSelect(countAggregationSelect.getGroupBy()) + .build(); + + Field qualifiedCountSelect = countAggregationSelect.getGroupBy().qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)).select(); + CountCondition countCondition = new CountCondition(qualifiedCountSelect, filterContext.getValue()); + WhereClauses whereClauses = WhereClauses.builder() + .groupFilter(countCondition) + .build(); - public static CountSqlAggregator create(CountFilter countFilter, FilterContext filterContext) { - return new CountSqlAggregator( - countFilter.getColumn(), - CountType.fromBoolean(countFilter.isDistinct()), - filterContext.getNameGenerator().selectName(countFilter), - filterContext.getTables(), - filterContext.getValue() - ); + return new SqlFilters(selects, whereClauses); } @Override - public SqlFilters getSqlFilters() { - return new SqlFilters(this.sqlSelects, this.whereClauses); + public Condition convertForTableExport(CountFilter countFilter, FilterContext filterContext) { + Param field = DSL.val(1); // no grouping, count is always 1 per row + return new CountCondition(field, filterContext.getValue()).condition(); } - public enum CountType { - DEFAULT, - DISTINCT; + private CommonAggregationSelect createCountAggregationSelect(Column countColumn, CountType countType, String alias, ConnectorSqlTables tables) { - public static CountType fromBoolean(boolean value) { - return value ? DISTINCT : DEFAULT; - } + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(tables.getRootTable(), countColumn.getName(), Object.class); + + Field qualifiedRootSelect = rootSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); + Field countField = countType == CountType.DISTINCT + ? DSL.countDistinct(qualifiedRootSelect) + : DSL.count(qualifiedRootSelect); + FieldWrapper countGroupBy = new FieldWrapper<>(countField.as(alias), countColumn.getName()); + + return CommonAggregationSelect.builder() + .rootSelect(rootSelect) + .groupBy(countGroupBy) + .build(); } } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java index 5ee5c1f21e..e2b9cbcc7a 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/DateDistanceSqlAggregator.java @@ -7,38 +7,95 @@ import com.bakdata.conquery.models.common.Range; import com.bakdata.conquery.models.common.daterange.CDateRange; import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.filters.specific.DateDistanceFilter; import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.DateDistanceSelect; import com.bakdata.conquery.models.events.MajorTypeId; import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; import com.bakdata.conquery.sql.conversion.model.SqlTables; import com.bakdata.conquery.sql.conversion.model.filter.DateDistanceCondition; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; import com.bakdata.conquery.sql.conversion.model.filter.WhereCondition; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; +import org.jooq.Condition; import org.jooq.Field; import org.jooq.impl.DSL; -@Value -public class DateDistanceSqlAggregator implements SqlAggregator { +public class DateDistanceSqlAggregator implements SelectConverter, FilterConverter { - SqlSelects sqlSelects; - WhereClauses whereClauses; + @Override + public ConnectorSqlSelects connectorSelect(DateDistanceSelect select, SelectContext selectContext) { - public DateDistanceSqlAggregator( + Column column = select.getColumn(); + String alias = selectContext.getNameGenerator().selectName(select); + ConnectorSqlTables tables = selectContext.getTables(); + ConversionContext conversionContext = selectContext.getConversionContext(); + + FieldWrapper dateDistanceSelect = createDateDistanceSelect(column, alias, select.getTimeUnit(), tables, conversionContext); + + Field qualifiedDateDistance = dateDistanceSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); + FieldWrapper minDateDistance = new FieldWrapper<>(DSL.min(qualifiedDateDistance).as(alias)); + + String finalPredecessor = tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); + ExtractingSqlSelect finalSelect = minDateDistance.qualify(finalPredecessor); + + return ConnectorSqlSelects.builder() + .preprocessingSelect(dateDistanceSelect) + .aggregationSelect(minDateDistance) + .finalSelect(finalSelect) + .build(); + } + + @Override + public SqlFilters convertToSqlFilter(DateDistanceFilter filter, FilterContext filterContext) { + + Column column = filter.getColumn(); + String alias = filterContext.getNameGenerator().selectName(filter); + ConnectorSqlTables tables = filterContext.getTables(); + ConversionContext conversionContext = filterContext.getConversionContext(); + + FieldWrapper dateDistanceSelect = createDateDistanceSelect(column, alias, filter.getTimeUnit(), tables, conversionContext); + ConnectorSqlSelects selects = ConnectorSqlSelects.builder().preprocessingSelect(dateDistanceSelect).build(); + + String eventFilterCteName = tables.getPredecessor(ConceptCteStep.EVENT_FILTER); + Field qualifiedDateDistanceSelect = dateDistanceSelect.qualify(eventFilterCteName).select(); + WhereCondition dateDistanceCondition = new DateDistanceCondition(qualifiedDateDistanceSelect, filterContext.getValue()); + + WhereClauses whereClauses = WhereClauses.builder().eventFilter(dateDistanceCondition).build(); + + return new SqlFilters(selects, whereClauses); + } + + @Override + public Condition convertForTableExport(DateDistanceFilter filter, FilterContext filterContext) { + + Column column = filter.getColumn(); + String tableName = column.getTable().getName(); + String columnName = column.getName(); + + Field startDateField = DSL.field(DSL.name(tableName, columnName), Date.class); + Field endDate = getEndDate(filterContext.getConversionContext()); + + Field dateDistance = filterContext.getFunctionProvider().dateDistance(filter.getTimeUnit(), startDateField, endDate); + return new DateDistanceCondition(dateDistance, filterContext.getValue()).condition(); + } + + private FieldWrapper createDateDistanceSelect( Column column, String alias, ChronoUnit timeUnit, SqlTables tables, - Range.LongRange filterValue, ConversionContext conversionContext ) { if (column.getType() != MajorTypeId.DATE) { @@ -49,60 +106,7 @@ public DateDistanceSqlAggregator( Field endDate = getEndDate(conversionContext); SqlFunctionProvider functionProvider = conversionContext.getSqlDialect().getFunctionProvider(); - FieldWrapper dateDistanceSelect = new FieldWrapper<>(functionProvider.dateDistance(timeUnit, startDate, endDate).as(alias)); - - SqlSelects.SqlSelectsBuilder builder = SqlSelects.builder().preprocessingSelect(dateDistanceSelect); - - if (filterValue == null) { - Field qualifiedDateDistance = dateDistanceSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)) - .select(); - FieldWrapper minDateDistance = new FieldWrapper<>(DSL.min(qualifiedDateDistance).as(alias)); - - String finalPredecessor = tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); - ExtractingSqlSelect finalSelect = minDateDistance.qualify(finalPredecessor); - - this.sqlSelects = builder.aggregationSelect(minDateDistance) - .finalSelect(finalSelect) - .build(); - this.whereClauses = WhereClauses.empty(); - } - else { - this.sqlSelects = builder.build(); - String predecessorCte = tables.getPredecessor(ConceptCteStep.EVENT_FILTER); - Field qualifiedDateDistanceSelect = dateDistanceSelect.qualify(predecessorCte).select(); - WhereCondition dateDistanceCondition = new DateDistanceCondition(qualifiedDateDistanceSelect, filterValue); - this.whereClauses = WhereClauses.builder() - .eventFilter(dateDistanceCondition) - .build(); - } - } - - public static DateDistanceSqlAggregator create( - DateDistanceSelect dateDistanceSelect, - SelectContext selectContext - ) { - return new DateDistanceSqlAggregator( - dateDistanceSelect.getColumn(), - selectContext.getNameGenerator().selectName(dateDistanceSelect), - dateDistanceSelect.getTimeUnit(), - selectContext.getTables(), - null, - selectContext.getConversionContext() - ); - } - - public static DateDistanceSqlAggregator create( - DateDistanceFilter dateDistanceFilter, - FilterContext filterContext - ) { - return new DateDistanceSqlAggregator( - dateDistanceFilter.getColumn(), - filterContext.getNameGenerator().selectName(dateDistanceFilter), - dateDistanceFilter.getTimeUnit(), - filterContext.getTables(), - filterContext.getValue(), - filterContext.getConversionContext() - ); + return new FieldWrapper<>(functionProvider.dateDistance(timeUnit, startDate, endDate).as(alias)); } private Field getEndDate(ConversionContext conversionContext) { @@ -131,4 +135,5 @@ private Field getEndDate(ConversionContext conversionContext) { return functionProvider.toDateField(Date.valueOf(endDate).toString()); } + } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDateUnionSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDateUnionSqlAggregator.java deleted file mode 100644 index 631dade583..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDateUnionSqlAggregator.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDateUnionSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptConversionTables; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; - -@Value -public class EventDateUnionSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - public EventDateUnionSqlAggregator( - String alias, - ColumnDateRange validityDate, - ConceptConversionTables tables, - SqlFunctionProvider functionProvider - ) { - ColumnDateRange qualified = validityDate.qualify(tables.getPredecessor(ConceptCteStep.INTERVAL_PACKING_SELECTS)); - FieldWrapper stringAggregation = new FieldWrapper<>(functionProvider.daterangeStringAggregation(qualified).as(alias)); - - ExtractingSqlSelect finalSelect = stringAggregation.qualify(tables.getLastPredecessor()); - - this.sqlSelects = SqlSelects.builder() - .intervalPackingSelect(stringAggregation) - .finalSelect(finalSelect) - .build(); - this.whereClauses = WhereClauses.builder().build(); - } - - public static EventDateUnionSqlAggregator create(EventDateUnionSelect select, SelectContext selectContext) { - return new EventDateUnionSqlAggregator( - selectContext.getNameGenerator().selectName(select), - selectContext.getValidityDate().orElseThrow(() -> new IllegalStateException("Can't convert a EventDateUnion select without a validity date")), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider() - ); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDurationSumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDurationSumSqlAggregator.java deleted file mode 100644 index 62409f4850..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/EventDurationSumSqlAggregator.java +++ /dev/null @@ -1,75 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import java.math.BigDecimal; -import java.sql.Date; -import java.time.temporal.ChronoUnit; - -import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDurationSumSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptConversionTables; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; -import org.jooq.Condition; -import org.jooq.Field; -import org.jooq.impl.DSL; - -@Value -public class EventDurationSumSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - private EventDurationSumSqlAggregator( - String alias, - ColumnDateRange validityDate, - ConceptConversionTables tables, - SqlFunctionProvider functionProvider - ) { - Field durationSum = DSL.sum( - DSL.when(containsInfinityDate(validityDate, functionProvider), DSL.val(null, Integer.class)) - .otherwise(functionProvider.dateDistance(ChronoUnit.DAYS, validityDate.getStart(), validityDate.getEnd())) - ) - .as(alias); - - FieldWrapper durationSumWrapper = new FieldWrapper<>(durationSum); - ExtractingSqlSelect finalSelect = durationSumWrapper.qualify(tables.getLastPredecessor()); - - this.sqlSelects = SqlSelects.builder() - .intervalPackingSelect(durationSumWrapper) - .finalSelect(finalSelect) - .build(); - this.whereClauses = WhereClauses.builder().build(); - } - - public static EventDurationSumSqlAggregator create(EventDurationSumSelect eventDurationSumSelect, SelectContext selectContext) { - - ColumnDateRange validityDate = selectContext.getValidityDate().orElseThrow( - () -> new IllegalStateException("Can't convert a EventDurationSum select without a validity date") - ); - - return new EventDurationSumSqlAggregator( - selectContext.getNameGenerator().selectName(eventDurationSumSelect), - prepareValidityDate(validityDate, selectContext), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider() - ); - } - - private static ColumnDateRange prepareValidityDate(ColumnDateRange validityDate, SelectContext selectContext) { - ColumnDateRange qualified = validityDate.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.INTERVAL_PACKING_SELECTS)); - return selectContext.getSqlDialect().getFunctionProvider().toDualColumn(qualified); - } - - private static Condition containsInfinityDate(ColumnDateRange validityDate, SqlFunctionProvider functionProvider) { - Field negativeInfinity = functionProvider.toDateField(functionProvider.getMinDateExpression()); - Field positiveInfinity = functionProvider.toDateField(functionProvider.getMaxDateExpression()); - return validityDate.getStart().eq(negativeInfinity).or(validityDate.getEnd().eq(positiveInfinity)); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/ExistsSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/ExistsSqlAggregator.java deleted file mode 100644 index d377fb7ecf..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/ExistsSqlAggregator.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.ExistsSelect; -import com.bakdata.conquery.sql.conversion.model.select.ExistsSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import com.bakdata.conquery.sql.conversion.model.select.UniversalSqlSelect; -import lombok.Value; -import org.jooq.impl.DSL; - -@Value -public class ExistsSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - private ExistsSqlAggregator(String alias) { - ExistsSqlSelect existsSqlSelect = new ExistsSqlSelect(alias); - this.sqlSelects = SqlSelects.builder() - .finalSelect(existsSqlSelect) - .build(); - this.whereClauses = WhereClauses.empty(); - } - - public static ExistsSqlAggregator create(ExistsSelect existsSelect, SelectContext selectContext) { - String alias = selectContext.getNameGenerator().selectName(existsSelect); - return new ExistsSqlAggregator(alias); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FirstValueSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FirstValueSqlAggregator.java deleted file mode 100644 index 04cf59bdb9..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FirstValueSqlAggregator.java +++ /dev/null @@ -1,66 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import com.bakdata.conquery.models.datasets.Column; -import com.bakdata.conquery.models.datasets.concepts.select.connector.FirstValueSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; -import org.jooq.Field; - -@Value -public class FirstValueSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - private FirstValueSqlAggregator( - Column column, - String alias, - Optional validityDate, - SqlTables connectorTables, - SqlFunctionProvider functionProvider - ) { - String rootTableName = connectorTables.getRootTable(); - String columnName = column.getName(); - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(rootTableName, columnName, Object.class); - - List> validityDateFields = - validityDate.map(_validityDate -> _validityDate.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT))) - .map(ColumnDateRange::toFields) - .orElse(Collections.emptyList()); - Field qualifiedRootSelect = rootSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); - FieldWrapper firstGroupBy = new FieldWrapper<>(functionProvider.first(qualifiedRootSelect, validityDateFields).as(alias), columnName); - - ExtractingSqlSelect finalSelect = firstGroupBy.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - - this.sqlSelects = SqlSelects.builder() - .preprocessingSelect(rootSelect) - .aggregationSelect(firstGroupBy) - .finalSelect(finalSelect) - .build(); - - this.whereClauses = WhereClauses.empty(); - } - - public static FirstValueSqlAggregator create(FirstValueSelect firstValueSelect, SelectContext selectContext) { - return new FirstValueSqlAggregator( - firstValueSelect.getColumn(), - selectContext.getNameGenerator().selectName(firstValueSelect), - selectContext.getValidityDate(), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider() - ); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java index 1964e59afb..54aebeb216 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/FlagSqlAggregator.java @@ -1,24 +1,29 @@ package com.bakdata.conquery.sql.conversion.model.aggregator; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.stream.Collectors; import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.filters.specific.FlagFilter; import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.FlagSelect; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.bakdata.conquery.sql.conversion.model.SqlTables; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; import com.bakdata.conquery.sql.conversion.model.filter.FlagCondition; +import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; import org.jooq.Condition; import org.jooq.Field; import org.jooq.Param; @@ -63,15 +68,12 @@ * } *

*/ -@Value -public class FlagSqlAggregator implements SqlAggregator { +public class FlagSqlAggregator implements SelectConverter, FilterConverter, SqlAggregator { private static final Param NUMERIC_TRUE_VAL = DSL.val(1); - SqlSelects sqlSelects; - WhereClauses whereClauses; - - public static FlagSqlAggregator create(FlagSelect flagSelect, SelectContext selectContext) { + @Override + public ConnectorSqlSelects connectorSelect(FlagSelect flagSelect, SelectContext selectContext) { SqlFunctionProvider functionProvider = selectContext.getConversionContext().getSqlDialect().getFunctionProvider(); SqlTables connectorTables = selectContext.getTables(); @@ -83,25 +85,28 @@ public static FlagSqlAggregator create(FlagSelect flagSelect, SelectContext sele ExtractingSqlSelect finalSelect = flagAggregation.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - SqlSelects sqlSelects = SqlSelects.builder().preprocessingSelects(rootSelects.values()) - .aggregationSelect(flagAggregation) - .finalSelect(finalSelect) - .build(); - - return new FlagSqlAggregator(sqlSelects, WhereClauses.builder().build()); + return ConnectorSqlSelects.builder() + .preprocessingSelects(rootSelects.values()) + .aggregationSelect(flagAggregation) + .finalSelect(finalSelect) + .build(); } - public static FlagSqlAggregator create(FlagFilter flagFilter, FilterContext filterContext) { + @Override + public SqlFilters convertToSqlFilter(FlagFilter flagFilter, FilterContext filterContext) { + SqlTables connectorTables = filterContext.getTables(); String rootTable = connectorTables.getPredecessor(ConceptCteStep.PREPROCESSING); - List> rootSelects = FlagCondition.getRequiredColumns(flagFilter.getFlags(), filterContext.getValue()).stream() - .map(Column::getName) - .map(columnName -> new ExtractingSqlSelect<>(rootTable, columnName, Boolean.class)) - .collect(Collectors.toList()); - SqlSelects selects = SqlSelects.builder() - .preprocessingSelects(rootSelects) - .build(); + List> rootSelects = getRequiredColumns(flagFilter.getFlags(), filterContext.getValue()) + .stream() + .map(Column::getName) + .map(columnName -> new ExtractingSqlSelect<>(rootTable, columnName, Boolean.class)) + .collect(Collectors.toList()); + + ConnectorSqlSelects selects = ConnectorSqlSelects.builder() + .preprocessingSelects(rootSelects) + .build(); List> flagFields = rootSelects.stream() .map(sqlSelect -> sqlSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.EVENT_FILTER)).select()) @@ -111,7 +116,27 @@ public static FlagSqlAggregator create(FlagFilter flagFilter, FilterContext filterContext) { + + List> flagFields = getRequiredColumns(filter.getFlags(), filterContext.getValue()) + .stream() + .map(column -> DSL.field(DSL.name(column.getTable().getName(), column.getName()), Boolean.class)) + .toList(); + + return new FlagCondition(flagFields).condition(); + } + + /** + * @return Columns names of a given flags map that match the selected flags of the filter value. + */ + private static List getRequiredColumns(Map flags, String[] selectedFlags) { + return Arrays.stream(selectedFlags) + .map(flags::get) + .toList(); } /** diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/LastValueSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/LastValueSqlAggregator.java deleted file mode 100644 index 6d59271d71..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/LastValueSqlAggregator.java +++ /dev/null @@ -1,65 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import java.util.Collections; -import java.util.List; -import java.util.Optional; - -import com.bakdata.conquery.models.datasets.Column; -import com.bakdata.conquery.models.datasets.concepts.select.connector.LastValueSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; -import org.jooq.Field; - -@Value -public class LastValueSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - private LastValueSqlAggregator( - Column column, - String alias, - Optional validityDate, - SqlTables connectorTables, - SqlFunctionProvider functionProvider - ) { - String columnName = column.getName(); - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), columnName, Object.class); - - List> validityDateFields = - validityDate.map(_validityDate -> _validityDate.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT))) - .map(ColumnDateRange::toFields) - .orElse(Collections.emptyList()); - Field qualifiedRootSelect = rootSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); - FieldWrapper lastGroupBy = new FieldWrapper<>(functionProvider.last(qualifiedRootSelect, validityDateFields).as(alias), columnName); - - ExtractingSqlSelect finalSelect = lastGroupBy.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - - this.sqlSelects = SqlSelects.builder() - .preprocessingSelect(rootSelect) - .aggregationSelect(lastGroupBy) - .finalSelect(finalSelect) - .build(); - - this.whereClauses = WhereClauses.empty(); - } - - public static LastValueSqlAggregator create(LastValueSelect lastValueSelect, SelectContext selectContext) { - return new LastValueSqlAggregator( - lastValueSelect.getColumn(), - selectContext.getNameGenerator().selectName(lastValueSelect), - selectContext.getValidityDate(), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider() - ); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/RandomValueSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/RandomValueSqlAggregator.java deleted file mode 100644 index 0aea7f0380..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/RandomValueSqlAggregator.java +++ /dev/null @@ -1,53 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import com.bakdata.conquery.models.datasets.Column; -import com.bakdata.conquery.models.datasets.concepts.select.connector.RandomValueSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; -import org.jooq.Field; - -@Value -public class RandomValueSqlAggregator implements SqlAggregator { - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - private RandomValueSqlAggregator( - Column column, - String alias, - SqlTables connectorTables, - SqlFunctionProvider functionProvider - ) { - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), column.getName(), Object.class); - - Field qualifiedRootSelect = rootSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); - FieldWrapper randomGroupBy = new FieldWrapper<>(functionProvider.random(qualifiedRootSelect).as(alias), column.getName()); - - ExtractingSqlSelect finalSelect = randomGroupBy.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - - this.sqlSelects = SqlSelects.builder() - .preprocessingSelect(rootSelect) - .aggregationSelect(randomGroupBy) - .finalSelect(finalSelect) - .build(); - - this.whereClauses = WhereClauses.empty(); - } - - public static RandomValueSqlAggregator create(RandomValueSelect randomValueSelect, SelectContext selectContext) { - return new RandomValueSqlAggregator( - randomValueSelect.getColumn(), - selectContext.getNameGenerator().selectName(randomValueSelect), - selectContext.getTables(), - selectContext.getConversionContext().getSqlDialect().getFunctionProvider() - ); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SqlAggregator.java index 81fb9d6c67..bb36b22447 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SqlAggregator.java @@ -1,19 +1,13 @@ package com.bakdata.conquery.sql.conversion.model.aggregator; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; -import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; -import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; - -public interface SqlAggregator { - - SqlSelects getSqlSelects(); - - WhereClauses getWhereClauses(); - - default SqlFilters getSqlFilters() { - return new SqlFilters(getSqlSelects(), getWhereClauses()); - } - +import com.bakdata.conquery.models.datasets.concepts.filters.Filter; +import com.bakdata.conquery.models.datasets.concepts.select.Select; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; + +/** + * Marker interface. SQL aggregators extend {@link SelectConverter} and {@link FilterConverter} and share common code for {@link Select} and + * {@link Filter} conversion. + */ +interface SqlAggregator { } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumDistinctSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumDistinctSqlAggregator.java deleted file mode 100644 index 0fad8b5e46..0000000000 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumDistinctSqlAggregator.java +++ /dev/null @@ -1,227 +0,0 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; - -import java.math.BigDecimal; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.bakdata.conquery.models.common.IRange; -import com.bakdata.conquery.models.datasets.Column; -import com.bakdata.conquery.models.datasets.concepts.filters.specific.SumFilter; -import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.SumSelect; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.CteStep; -import com.bakdata.conquery.sql.conversion.model.NameGenerator; -import com.bakdata.conquery.sql.conversion.model.QueryStep; -import com.bakdata.conquery.sql.conversion.model.Selects; -import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.filter.SumCondition; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; -import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; -import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Getter; -import lombok.RequiredArgsConstructor; -import lombok.Value; -import org.jooq.Condition; -import org.jooq.Field; -import org.jooq.impl.DSL; - -/** - * Conversion of a {@link SumSelect} with {@link SumSelect#getDistinctByColumn()}. Sum's the values of a column for each row which is distinct by the - * distinct-by columns by creating 2 additional CTEs. We can't use our usual {@link ConceptCteStep#PREPROCESSING} CTE for achieving distinctness, because - * it's used for the conversion of other selects where distinctness by distinct-by columns is not required and would cause wrong results. - *

- * - *

- *  The two additional CTEs this aggregator creates
- * 	
    - *
  1. - * Assign a row number to each row partitioned by the distinct by columns to ensure distinctness. - * {@code - * "row_number_assigned" as ( - * select - * "pid", - * "value", - * row_number() over (partition by "pid", "k1", "k2") "row_number" - * from "event_filter" - * ) - * } - *
  2. - *
  3. - * Sum all entries of a subject where the row number = 1, thus only summing distinct entries. - * {@code - * "sum_distinct_select-1-row_number_filtered" as ( - * select - * "pid", - * sum("value") "sum_distinct_select-1" - * from "row_number_assigned" - * where "row_number" = 1 - * group by "pid" - * ), - * } - *
  4. - *
- *
- */ -@Value -public class SumDistinctSqlAggregator implements SqlAggregator { - - @Getter - @RequiredArgsConstructor - private enum SumDistinctCteStep implements CteStep { - - ROW_NUMBER_ASSIGNED("row_number_assigned", null), - ROW_NUMBER_FILTERED("row_number_filtered", ROW_NUMBER_ASSIGNED); - - private final String suffix; - private final SumDistinctCteStep predecessor; - } - - private static final String ROW_NUMBER_ALIAS = "row_number"; - private static final String SUM_DISTINCT_SUFFIX = "sum_distinct"; - - SqlSelects sqlSelects; - WhereClauses whereClauses; - - public SumDistinctSqlAggregator( - Column sumColumn, - List distinctByColumns, - String alias, - IRange filterValue, - SqlIdColumns ids, - SqlTables connectorTables, - NameGenerator nameGenerator - ) { - // preprocessing - Class numberClass = NumberMapUtil.NUMBER_MAP.get(sumColumn.getType()); - ExtractingSqlSelect sumColumnRootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), sumColumn.getName(), numberClass); - List> distinctByRootSelects = distinctByColumns.stream() - .map(column -> new ExtractingSqlSelect<>( - connectorTables.getRootTable(), column.getName(), Object.class) - ) - .toList(); - - // additional predecessors - QueryStep rowNumberCte = createRowNumberCte(ids, sumColumnRootSelect, distinctByRootSelects, alias, connectorTables, nameGenerator); - Field rootSelectQualified = sumColumnRootSelect.qualify(rowNumberCte.getCteName()).select(); - FieldWrapper distinctSum = new FieldWrapper<>(DSL.sum(rootSelectQualified).as(alias)); - QueryStep rowNumberFilteredCte = createRowNumberFilteredCte(rowNumberCte, distinctSum, alias, nameGenerator); - - SqlSelects.SqlSelectsBuilder builder = SqlSelects.builder() - .preprocessingSelect(sumColumnRootSelect) - .preprocessingSelects(distinctByRootSelects) - .additionalPredecessor(Optional.of(rowNumberFilteredCte)); - - if (filterValue != null) { - this.sqlSelects = builder.build(); - String groupFilterPredecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); - Field qualifiedSumSelect = distinctSum.qualify(groupFilterPredecessor).select(); - SumCondition sumCondition = new SumCondition(qualifiedSumSelect, filterValue); - this.whereClauses = WhereClauses.builder() - .groupFilter(sumCondition) - .build(); - } - else { - ExtractingSqlSelect finalSelect = distinctSum.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - this.sqlSelects = builder.finalSelect(finalSelect).build(); - this.whereClauses = WhereClauses.empty(); - } - } - - public static SumDistinctSqlAggregator create(SumSelect sumSelect, SelectContext selectContext) { - return new SumDistinctSqlAggregator( - sumSelect.getColumn(), - sumSelect.getDistinctByColumn(), - selectContext.getNameGenerator().selectName(sumSelect), - null, - selectContext.getIds(), - selectContext.getTables(), - selectContext.getNameGenerator() - ); - } - - public static > SumDistinctSqlAggregator create(SumFilter sumFilter, FilterContext filterContext) { - return new SumDistinctSqlAggregator( - sumFilter.getColumn(), - sumFilter.getDistinctByColumn(), - filterContext.getNameGenerator().selectName(sumFilter), - filterContext.getValue(), - filterContext.getIds(), - filterContext.getTables(), - filterContext.getNameGenerator() - ); - } - - /** - * Assigns row numbers for each partition over the pid and the distinct by columns. If the values per pid in the distinct by columns are duplicated, - * the row number will be incremented for each duplicated entry. - */ - private static QueryStep createRowNumberCte( - SqlIdColumns ids, - ExtractingSqlSelect sumColumnRootSelect, - List> distinctByRootSelects, - String alias, - SqlTables connectorTables, - NameGenerator nameGenerator - ) { - String predecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT); - SqlIdColumns qualifiedIds = ids.qualify(predecessor); - ExtractingSqlSelect qualifiedSumRootSelect = sumColumnRootSelect.qualify(predecessor); - - List> partitioningFields = Stream.concat( - qualifiedIds.toFields().stream(), - distinctByRootSelects.stream().map(sqlSelect -> sqlSelect.qualify(predecessor).select()) - ) - .collect(Collectors.toList()); - FieldWrapper rowNumber = new FieldWrapper<>( - DSL.rowNumber().over(DSL.partitionBy(partitioningFields)).as(ROW_NUMBER_ALIAS), - partitioningFields.stream().map(Field::getName).toArray(String[]::new) - ); - - Selects rowNumberAssignedSelects = Selects.builder() - .ids(qualifiedIds) - .sqlSelects(List.of(qualifiedSumRootSelect, rowNumber)) - .build(); - - return QueryStep.builder() - .cteName(nameGenerator.cteStepName(SumDistinctCteStep.ROW_NUMBER_ASSIGNED, alias)) - .selects(rowNumberAssignedSelects) - .fromTable(QueryStep.toTableLike(predecessor)) - .build(); - } - - /** - * Sums up the sum column values but only those whose row number is 1. Thus, only unique entries will be summed up. - */ - private static QueryStep createRowNumberFilteredCte( - QueryStep rowNumberCte, - FieldWrapper sumSelect, - String alias, - NameGenerator nameGenerator - ) { - SqlIdColumns ids = rowNumberCte.getQualifiedSelects().getIds(); - - Selects rowNumberFilteredSelects = Selects.builder() - .ids(ids) - .sqlSelects(List.of(sumSelect)) - .build(); - - Condition firstOccurrence = DSL.field(DSL.name(rowNumberCte.getCteName(), ROW_NUMBER_ALIAS)) - .eq(DSL.val(1)); - - return QueryStep.builder() - .cteName(nameGenerator.cteStepName(SumDistinctCteStep.ROW_NUMBER_FILTERED, alias)) - .selects(rowNumberFilteredSelects) - .fromTable(QueryStep.toTableLike(rowNumberCte.getCteName())) - .conditions(List.of(firstOccurrence)) - .predecessors(List.of(rowNumberCte)) - .groupBy(ids.toFields()) - .build(); - } - -} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java index 0a0703fd4f..5ea0d55527 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/SumSqlAggregator.java @@ -3,49 +3,204 @@ import java.math.BigDecimal; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; +import java.util.stream.Stream; import com.bakdata.conquery.models.common.IRange; import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; import com.bakdata.conquery.models.datasets.concepts.filters.specific.SumFilter; import com.bakdata.conquery.models.datasets.concepts.select.connector.specific.SumSelect; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; +import com.bakdata.conquery.sql.conversion.model.CteStep; +import com.bakdata.conquery.sql.conversion.model.NameGenerator; +import com.bakdata.conquery.sql.conversion.model.NumberMapUtil; +import com.bakdata.conquery.sql.conversion.model.QueryStep; +import com.bakdata.conquery.sql.conversion.model.Selects; +import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; import com.bakdata.conquery.sql.conversion.model.SqlTables; +import com.bakdata.conquery.sql.conversion.model.filter.FilterConverter; +import com.bakdata.conquery.sql.conversion.model.filter.SqlFilters; import com.bakdata.conquery.sql.conversion.model.filter.SumCondition; import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; import com.bakdata.conquery.sql.conversion.model.select.FieldWrapper; import com.bakdata.conquery.sql.conversion.model.select.SelectContext; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; +import com.bakdata.conquery.sql.conversion.model.select.SelectConverter; +import com.bakdata.conquery.sql.conversion.model.select.SingleColumnSqlSelect; +import lombok.Getter; +import lombok.RequiredArgsConstructor; +import org.jooq.Condition; import org.jooq.Field; import org.jooq.impl.DSL; -@Value -public class SumSqlAggregator implements SqlAggregator { +/** + * Conversion of a {@link SumSelect} by summing the {@link SumSelect#getColumn()} or, if present, the {@link SumSelect#getColumn()} minus the + * {@link SumSelect#getSubtractColumn()}. + *

+ * Conversion of a {@link SumSelect} with {@link SumSelect#getDistinctByColumn()} is a special case: Sum's the values of a column for each row which is distinct + * by the distinct-by columns by creating 2 additional CTEs. We can't use our usual {@link ConceptCteStep#PREPROCESSING} CTE for achieving distinctness, because + * it's used for the conversion of other selects where distinctness by distinct-by columns is not required and would cause wrong results. + * + *

+ *  The two additional CTEs this aggregator creates
+ * 	
    + *
  1. + * Assign a row number to each row partitioned by the distinct by columns to ensure distinctness. + * {@code + * "row_number_assigned" as ( + * select + * "pid", + * "value", + * row_number() over (partition by "pid", "k1", "k2") "row_number" + * from "event_filter" + * ) + * } + *
  2. + *
  3. + * Sum all entries of a subject where the row number = 1, thus only summing distinct entries. + * {@code + * "sum_distinct_select-1-row_number_filtered" as ( + * select + * "pid", + * sum("value") "sum_distinct_select-1" + * from "row_number_assigned" + * where "row_number" = 1 + * group by "pid" + * ), + * } + *
  4. + *
+ *
+ */ +public class SumSqlAggregator> implements + SelectConverter, + FilterConverter, RANGE>, + SqlAggregator { - SqlSelects sqlSelects; - WhereClauses whereClauses; + @Getter + @RequiredArgsConstructor + private enum SumDistinctCteStep implements CteStep { + + ROW_NUMBER_ASSIGNED("row_number_assigned", null), + ROW_NUMBER_FILTERED("row_number_filtered", ROW_NUMBER_ASSIGNED); + + private final String suffix; + private final SumDistinctCteStep predecessor; + } + + private static final String ROW_NUMBER_ALIAS = "row_number"; + private static final String SUM_DISTINCT_SUFFIX = "sum_distinct"; + + @Override + public ConnectorSqlSelects connectorSelect(SumSelect sumSelect, SelectContext selectContext) { + + Column sumColumn = sumSelect.getColumn(); + Column subtractColumn = sumSelect.getSubtractColumn(); + List distinctByColumns = sumSelect.getDistinctByColumn(); + NameGenerator nameGenerator = selectContext.getNameGenerator(); + String alias = nameGenerator.selectName(sumSelect); + ConnectorSqlTables tables = selectContext.getTables(); + + CommonAggregationSelect sumAggregationSelect; + if (distinctByColumns != null && !distinctByColumns.isEmpty()) { + SqlIdColumns ids = selectContext.getIds(); + sumAggregationSelect = createDistinctSumAggregationSelect(sumColumn, distinctByColumns, alias, ids, tables, nameGenerator); + ExtractingSqlSelect finalSelect = createFinalSelect(sumAggregationSelect, tables); + return ConnectorSqlSelects.builder() + .preprocessingSelects(sumAggregationSelect.getRootSelects()) + .additionalPredecessor(sumAggregationSelect.getAdditionalPredecessor()) + .finalSelect(finalSelect) + .build(); + } + else { + sumAggregationSelect = createSumAggregationSelect(sumColumn, subtractColumn, alias, tables); + ExtractingSqlSelect finalSelect = createFinalSelect(sumAggregationSelect, tables); + return ConnectorSqlSelects.builder() + .preprocessingSelects(sumAggregationSelect.getRootSelects()) + .aggregationSelect(sumAggregationSelect.getGroupBy()) + .finalSelect(finalSelect) + .build(); + } + } + + @Override + public SqlFilters convertToSqlFilter(SumFilter sumFilter, FilterContext filterContext) { + + Column sumColumn = sumFilter.getColumn(); + Column subtractColumn = sumFilter.getSubtractColumn(); + List distinctByColumns = sumFilter.getDistinctByColumn(); + String alias = filterContext.getNameGenerator().selectName(sumFilter); + ConnectorSqlTables tables = filterContext.getTables(); + + CommonAggregationSelect sumAggregationSelect; + ConnectorSqlSelects selects; + + if (distinctByColumns != null && !distinctByColumns.isEmpty()) { + sumAggregationSelect = + createDistinctSumAggregationSelect(sumColumn, distinctByColumns, alias, filterContext.getIds(), tables, filterContext.getNameGenerator()); + selects = ConnectorSqlSelects.builder() + .preprocessingSelects(sumAggregationSelect.getRootSelects()) + .additionalPredecessor(sumAggregationSelect.getAdditionalPredecessor()) + .build(); + } + else { + sumAggregationSelect = createSumAggregationSelect(sumColumn, subtractColumn, alias, tables); + selects = ConnectorSqlSelects.builder() + .preprocessingSelects(sumAggregationSelect.getRootSelects()) + .additionalPredecessor(sumAggregationSelect.getAdditionalPredecessor()) + .aggregationSelect(sumAggregationSelect.getGroupBy()) + .build(); + } + + Field qualifiedSumSelect = sumAggregationSelect.getGroupBy().qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)).select(); + SumCondition sumCondition = new SumCondition(qualifiedSumSelect, filterContext.getValue()); + WhereClauses whereClauses = WhereClauses.builder() + .groupFilter(sumCondition) + .build(); + + return new SqlFilters(selects, whereClauses); + + } + + @Override + public Condition convertForTableExport(SumFilter filter, FilterContext filterContext) { + + Column column = filter.getColumn(); + String tableName = column.getTable().getName(); + String columnName = column.getName(); + Field field = DSL.field(DSL.name(tableName, columnName), Number.class); + + Column subtractColumn = filter.getSubtractColumn(); + if (subtractColumn == null) { + return new SumCondition(field, filterContext.getValue()).condition(); + } + + String subtractColumnName = subtractColumn.getName(); + String subtractTableName = subtractColumn.getTable().getName(); + Field subtractField = DSL.field(DSL.name(subtractTableName, subtractColumnName), Number.class); + return new SumCondition(field.minus(subtractField), filterContext.getValue()).condition(); + } + + private CommonAggregationSelect createSumAggregationSelect(Column sumColumn, Column subtractColumn, String alias, ConnectorSqlTables tables) { - private SumSqlAggregator( - Column sumColumn, - Column subtractColumn, - String alias, - SqlTables connectorTables, - IRange filterValue - ) { Class numberClass = NumberMapUtil.NUMBER_MAP.get(sumColumn.getType()); - List> preprocessingSelects = new ArrayList<>(); + List> preprocessingSelects = new ArrayList<>(); - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), sumColumn.getName(), numberClass); + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(tables.getRootTable(), sumColumn.getName(), numberClass); preprocessingSelects.add(rootSelect); - String eventFilterCte = connectorTables.cteName(ConceptCteStep.EVENT_FILTER); + String eventFilterCte = tables.cteName(ConceptCteStep.EVENT_FILTER); Field sumField = rootSelect.qualify(eventFilterCte).select(); + FieldWrapper sumGroupBy; + if (subtractColumn != null) { ExtractingSqlSelect subtractColumnRootSelect = new ExtractingSqlSelect<>( - connectorTables.getRootTable(), + tables.getRootTable(), subtractColumn.getName(), numberClass ); @@ -58,44 +213,114 @@ private SumSqlAggregator( sumGroupBy = new FieldWrapper<>(DSL.sum(sumField).as(alias), sumColumn.getName()); } - SqlSelects.SqlSelectsBuilder builder = SqlSelects.builder() - .preprocessingSelects(preprocessingSelects) - .aggregationSelect(sumGroupBy); + return CommonAggregationSelect.builder() + .rootSelects(preprocessingSelects) + .groupBy(sumGroupBy) + .build(); + } - if (filterValue == null) { - ExtractingSqlSelect finalSelect = sumGroupBy.qualify(connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); - this.sqlSelects = builder.finalSelect(finalSelect).build(); - this.whereClauses = WhereClauses.empty(); - } - else { - this.sqlSelects = builder.build(); - String predecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); - Field qualifiedSumGroupBy = sumGroupBy.qualify(predecessor).select(); - SumCondition sumCondition = new SumCondition(qualifiedSumGroupBy, filterValue); - this.whereClauses = WhereClauses.builder() - .groupFilter(sumCondition) - .build(); - } + private CommonAggregationSelect createDistinctSumAggregationSelect( + Column sumColumn, + List distinctByColumns, + String alias, + SqlIdColumns ids, + ConnectorSqlTables tables, + NameGenerator nameGenerator + ) { + List> preprocessingSelects = new ArrayList<>(); + + Class numberClass = NumberMapUtil.NUMBER_MAP.get(sumColumn.getType()); + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(tables.getRootTable(), sumColumn.getName(), numberClass); + preprocessingSelects.add(rootSelect); + + List> distinctByRootSelects = + distinctByColumns.stream() + .map(column -> new ExtractingSqlSelect<>(tables.getRootTable(), column.getName(), Object.class)) + .collect(Collectors.toList()); + preprocessingSelects.addAll(distinctByRootSelects); + + QueryStep rowNumberCte = createRowNumberCte(ids, rootSelect, distinctByRootSelects, alias, tables, nameGenerator); + Field rootSelectQualified = rootSelect.qualify(rowNumberCte.getCteName()).select(); + FieldWrapper sumGroupBy = new FieldWrapper<>(DSL.sum(rootSelectQualified).as(alias)); + QueryStep rowNumberFilteredCte = createRowNumberFilteredCte(rowNumberCte, sumGroupBy, alias, nameGenerator); + + return CommonAggregationSelect.builder() + .rootSelects(preprocessingSelects) + .additionalPredecessor(rowNumberFilteredCte) + .groupBy(sumGroupBy) + .build(); } - public static SumSqlAggregator create(SumSelect sumSelect, SelectContext selectContext) { - return new SumSqlAggregator( - sumSelect.getColumn(), - sumSelect.getSubtractColumn(), - selectContext.getNameGenerator().selectName(sumSelect), - selectContext.getTables(), - null + /** + * Assigns row numbers for each partition over the pid and the distinct by columns. If the values per pid in the distinct by columns are duplicated, + * the row number will be incremented for each duplicated entry. + */ + private static QueryStep createRowNumberCte( + SqlIdColumns ids, + SingleColumnSqlSelect sumColumnRootSelect, + List> distinctByRootSelects, + String alias, + SqlTables connectorTables, + NameGenerator nameGenerator + ) { + String predecessor = connectorTables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT); + SqlIdColumns qualifiedIds = ids.qualify(predecessor); + SingleColumnSqlSelect qualifiedSumRootSelect = sumColumnRootSelect.qualify(predecessor); + + List> partitioningFields = Stream.concat( + qualifiedIds.toFields().stream(), + distinctByRootSelects.stream().map(sqlSelect -> sqlSelect.qualify(predecessor).select()) + ) + .collect(Collectors.toList()); + FieldWrapper rowNumber = new FieldWrapper<>( + DSL.rowNumber().over(DSL.partitionBy(partitioningFields)).as(ROW_NUMBER_ALIAS), + partitioningFields.stream().map(Field::getName).toArray(String[]::new) ); + + Selects rowNumberAssignedSelects = Selects.builder() + .ids(qualifiedIds) + .sqlSelects(List.of(qualifiedSumRootSelect, rowNumber)) + .build(); + + return QueryStep.builder() + .cteName(nameGenerator.cteStepName(SumDistinctCteStep.ROW_NUMBER_ASSIGNED, alias)) + .selects(rowNumberAssignedSelects) + .fromTable(QueryStep.toTableLike(predecessor)) + .build(); } - public static > SqlAggregator create(SumFilter sumFilter, FilterContext filterContext) { - return new SumSqlAggregator( - sumFilter.getColumn(), - sumFilter.getSubtractColumn(), - filterContext.getNameGenerator().selectName(sumFilter), - filterContext.getTables(), - filterContext.getValue() - ); + /** + * Sums up the sum column values but only those whose row number is 1. Thus, only unique entries will be summed up. + */ + private static QueryStep createRowNumberFilteredCte( + QueryStep rowNumberCte, + FieldWrapper sumSelect, + String alias, + NameGenerator nameGenerator + ) { + SqlIdColumns ids = rowNumberCte.getQualifiedSelects().getIds(); + + Selects rowNumberFilteredSelects = Selects.builder() + .ids(ids) + .sqlSelects(List.of(sumSelect)) + .build(); + + Condition firstOccurrence = DSL.field(DSL.name(rowNumberCte.getCteName(), ROW_NUMBER_ALIAS)) + .eq(DSL.val(1)); + + return QueryStep.builder() + .cteName(nameGenerator.cteStepName(SumDistinctCteStep.ROW_NUMBER_FILTERED, alias)) + .selects(rowNumberFilteredSelects) + .fromTable(QueryStep.toTableLike(rowNumberCte.getCteName())) + .conditions(List.of(firstOccurrence)) + .predecessors(List.of(rowNumberCte)) + .groupBy(ids.toFields()) + .build(); + } + + private static ExtractingSqlSelect createFinalSelect(CommonAggregationSelect sumAggregationSelect, ConnectorSqlTables tables) { + String finalPredecessor = tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER); + return sumAggregationSelect.getGroupBy().qualify(finalPredecessor); } } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java new file mode 100644 index 0000000000..abfa39614e --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/AbstractSelectFilterConverter.java @@ -0,0 +1,50 @@ +package com.bakdata.conquery.sql.conversion.model.filter; + +import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.filters.specific.SelectFilter; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; +import org.jooq.Condition; +import org.jooq.Field; +import org.jooq.impl.DSL; + +abstract class AbstractSelectFilterConverter, T> implements FilterConverter { + + @Override + public SqlFilters convertToSqlFilter(F filter, FilterContext filterContext) { + + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>( + filterContext.getTables().getPredecessor(ConceptCteStep.PREPROCESSING), + filter.getColumn().getName(), + String.class + ); + + WhereCondition condition = new MultiSelectCondition( + rootSelect.qualify(filterContext.getTables().getPredecessor(ConceptCteStep.EVENT_FILTER)).select(), + getValues(filterContext), + filterContext.getFunctionProvider() + ); + + return new SqlFilters( + ConnectorSqlSelects.builder() + .preprocessingSelect(rootSelect) + .build(), + WhereClauses.builder() + .eventFilter(condition) + .build() + ); + } + + @Override + public Condition convertForTableExport(F filter, FilterContext filterContext) { + Column column = filter.getColumn(); + String tableName = column.getTable().getName(); + String columnName = column.getName(); + Field field = DSL.field(DSL.name(tableName, columnName), String.class); + return new MultiSelectCondition(field, getValues(filterContext), filterContext.getFunctionProvider()).condition(); + } + + protected abstract String[] getValues(FilterContext filterContext); +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/CountCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/CountCondition.java index 1eea111b58..728aafee2b 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/CountCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/CountCondition.java @@ -1,9 +1,7 @@ package com.bakdata.conquery.sql.conversion.model.filter; import com.bakdata.conquery.models.common.IRange; -import com.bakdata.conquery.models.datasets.Column; import org.jooq.Field; -import org.jooq.impl.DSL; public class CountCondition extends RangeCondition { @@ -11,13 +9,6 @@ public CountCondition(Field column, IRange range) { super(column, range); } - public static CountCondition onColumn(Column column, IRange range) { - String tableName = column.getTable().getName(); - String columnName = column.getName(); - Field field = DSL.field(DSL.name(tableName, columnName), Number.class); - return new CountCondition(field, range); - } - @Override public ConditionType type() { return ConditionType.GROUP; diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateDistanceCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateDistanceCondition.java index 6b3d4d096d..5344330c16 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateDistanceCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/DateDistanceCondition.java @@ -1,16 +1,7 @@ package com.bakdata.conquery.sql.conversion.model.filter; -import java.sql.Date; -import java.time.LocalDate; -import java.time.temporal.ChronoUnit; - import com.bakdata.conquery.models.common.Range; -import com.bakdata.conquery.models.datasets.Column; -import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import org.jooq.Field; -import org.jooq.impl.DSL; public class DateDistanceCondition extends RangeCondition { @@ -18,21 +9,6 @@ public DateDistanceCondition(Field column, Range.LongRange range) { super(column, range); } - public static DateDistanceCondition onColumn(Column column, ChronoUnit timeUnit, FilterContext filterContext) { - - String tableName = column.getTable().getName(); - String columnName = column.getName(); - Field startDateField = DSL.field(DSL.name(tableName, columnName), Date.class); - - ConversionContext conversionContext = filterContext.getConversionContext(); - SqlFunctionProvider functionProvider = filterContext.getSqlDialect().getFunctionProvider(); - LocalDate endDate = conversionContext.getSqlDialect().getDateNowSupplier().getLocalDateNow(); - Field endDateField = functionProvider.toDateField(Date.valueOf(endDate).toString()); - - Field dateDistance = functionProvider.dateDistance(timeUnit, startDateField, endDateField); - return new DateDistanceCondition(dateDistance, filterContext.getValue()); - } - @Override public ConditionType type() { return ConditionType.EVENT; diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FilterConverter.java new file mode 100644 index 0000000000..6ed05d1527 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FilterConverter.java @@ -0,0 +1,13 @@ +package com.bakdata.conquery.sql.conversion.model.filter; + +import com.bakdata.conquery.models.datasets.concepts.filters.Filter; +import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; +import org.jooq.Condition; + +public interface FilterConverter, V> { + + SqlFilters convertToSqlFilter(F filter, FilterContext filterContext); + + Condition convertForTableExport(F filter, FilterContext filterContext); + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FlagCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FlagCondition.java index f5b3e68dd3..01ebb4aeba 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FlagCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/FlagCondition.java @@ -1,10 +1,7 @@ package com.bakdata.conquery.sql.conversion.model.filter; -import java.util.Arrays; import java.util.List; -import java.util.Map; -import com.bakdata.conquery.models.datasets.Column; import lombok.RequiredArgsConstructor; import org.jooq.Condition; import org.jooq.Field; @@ -15,23 +12,6 @@ public class FlagCondition implements WhereCondition { private final List> flagFields; - public static FlagCondition onColumn(Map flags, String[] selectedFlags) { - List> flagFields = getRequiredColumns(flags, selectedFlags) - .stream() - .map(column -> DSL.field(DSL.name(column.getTable().getName(), column.getName()), Boolean.class)) - .toList(); - return new FlagCondition(flagFields); - } - - /** - * @return Columns names of a given flags map that match the selected flags of the filter value. - */ - public static List getRequiredColumns(Map flags, String[] selectedFlags) { - return Arrays.stream(selectedFlags) - .map(flags::get) - .toList(); - } - @Override public Condition condition() { return flagFields.stream() diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectCondition.java index e37449f024..576b068d8e 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectCondition.java @@ -2,7 +2,6 @@ import java.util.Arrays; -import com.bakdata.conquery.models.datasets.Column; import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.google.common.base.Strings; import lombok.RequiredArgsConstructor; @@ -17,13 +16,6 @@ public class MultiSelectCondition implements WhereCondition { private final String[] values; private final SqlFunctionProvider functionProvider; - public static MultiSelectCondition onColumn(Column column, String[] values, SqlFunctionProvider functionProvider) { - String tableName = column.getTable().getName(); - String columnName = column.getName(); - Field field = DSL.field(DSL.name(tableName, columnName), String.class); - return new MultiSelectCondition(field, values, functionProvider); - } - @Override public WhereCondition negate() { // we want all entries that don't satisfy a condition - because in SQL a comparison with NULL equals UNKNOWN and not FALSE, diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectFilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectFilterConverter.java new file mode 100644 index 0000000000..ca1556bcb3 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/MultiSelectFilterConverter.java @@ -0,0 +1,12 @@ +package com.bakdata.conquery.sql.conversion.model.filter; + +import com.bakdata.conquery.models.datasets.concepts.filters.specific.MultiSelectFilter; +import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; + +public class MultiSelectFilterConverter extends AbstractSelectFilterConverter { + + @Override + protected String[] getValues(FilterContext filterContext) { + return filterContext.getValue(); + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberCondition.java index dddd91a079..169ead8af7 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberCondition.java @@ -1,9 +1,7 @@ package com.bakdata.conquery.sql.conversion.model.filter; import com.bakdata.conquery.models.common.IRange; -import com.bakdata.conquery.models.datasets.Column; import org.jooq.Field; -import org.jooq.impl.DSL; public class NumberCondition extends RangeCondition { @@ -11,13 +9,6 @@ public NumberCondition(Field column, IRange range) { - String tableName = column.getTable().getName(); - String columnName = column.getName(); - Field field = DSL.field(DSL.name(tableName, columnName), Number.class); - return new NumberCondition(field, range); - } - @Override public ConditionType type() { return ConditionType.EVENT; diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberSqlAggregator.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java similarity index 50% rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberSqlAggregator.java rename to backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java index 7b93747f81..bbfb1dce5f 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/aggregator/NumberSqlAggregator.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/NumberFilterConverter.java @@ -1,4 +1,4 @@ -package com.bakdata.conquery.sql.conversion.model.aggregator; +package com.bakdata.conquery.sql.conversion.model.filter; import java.math.BigDecimal; import java.util.List; @@ -9,49 +9,48 @@ import com.bakdata.conquery.models.datasets.concepts.filters.specific.NumberFilter; import com.bakdata.conquery.models.events.MajorTypeId; import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; -import com.bakdata.conquery.sql.conversion.model.SqlTables; -import com.bakdata.conquery.sql.conversion.model.filter.NumberCondition; -import com.bakdata.conquery.sql.conversion.model.filter.WhereClauses; +import com.bakdata.conquery.sql.conversion.model.NumberMapUtil; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import com.bakdata.conquery.sql.conversion.model.select.ExtractingSqlSelect; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; -import lombok.Value; +import org.jooq.Condition; import org.jooq.Field; +import org.jooq.impl.DSL; -@Value -public class NumberSqlAggregator implements SqlAggregator { +public class NumberFilterConverter> implements FilterConverter, RANGE> { - SqlSelects sqlSelects; - WhereClauses whereClauses; + @Override + public SqlFilters convertToSqlFilter(NumberFilter filter, FilterContext filterContext) { + + Column column = filter.getColumn(); + ConnectorSqlTables tables = filterContext.getTables(); - public NumberSqlAggregator( - Column column, - SqlTables connectorTables, - IRange filterValue - ) { Class numberClass = NumberMapUtil.NUMBER_MAP.get(column.getType()); - ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(connectorTables.getRootTable(), column.getName(), numberClass); + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(tables.getRootTable(), column.getName(), numberClass); - Field eventFilterCtePredecessor = rootSelect.qualify(connectorTables.getPredecessor(ConceptCteStep.EVENT_FILTER)).select(); + Field eventFilterCtePredecessor = rootSelect.qualify(tables.getPredecessor(ConceptCteStep.EVENT_FILTER)).select(); + IRange filterValue = prepareFilterValue(column, filterContext.getValue()); NumberCondition condition = new NumberCondition(eventFilterCtePredecessor, filterValue); - this.sqlSelects = SqlSelects.builder() - .preprocessingSelects(List.of(rootSelect)) - .build(); - this.whereClauses = WhereClauses.builder() - .eventFilter(condition) - .build(); + ConnectorSqlSelects selects = ConnectorSqlSelects.builder() + .preprocessingSelects(List.of(rootSelect)) + .build(); + + WhereClauses whereClauses = WhereClauses.builder() + .eventFilter(condition) + .build(); + + return new SqlFilters(selects, whereClauses); } - public static NumberSqlAggregator create( - NumberFilter> numberFilter, - FilterContext> filterContext - ) { - return new NumberSqlAggregator( - numberFilter.getColumn(), - filterContext.getTables(), - prepareFilterValue(numberFilter.getColumn(), filterContext.getValue()) - ); + @Override + public Condition convertForTableExport(NumberFilter filter, FilterContext filterContext) { + Column column = filter.getColumn(); + String tableName = column.getTable().getName(); + String columnName = column.getName(); + Field field = DSL.field(DSL.name(tableName, columnName), Number.class); + return new NumberCondition(field, filterContext.getValue()).condition(); } /** diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SingleSelectFilterConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SingleSelectFilterConverter.java new file mode 100644 index 0000000000..eb3f2b0159 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SingleSelectFilterConverter.java @@ -0,0 +1,12 @@ +package com.bakdata.conquery.sql.conversion.model.filter; + +import com.bakdata.conquery.models.datasets.concepts.filters.specific.SingleSelectFilter; +import com.bakdata.conquery.sql.conversion.cqelement.concept.FilterContext; + +public class SingleSelectFilterConverter extends AbstractSelectFilterConverter { + + @Override + protected String[] getValues(FilterContext filterContext) { + return new String[]{filterContext.getValue()}; + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SqlFilters.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SqlFilters.java index 99d1b402c1..5f8a1e6a6b 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SqlFilters.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SqlFilters.java @@ -1,10 +1,10 @@ package com.bakdata.conquery.sql.conversion.model.filter; -import com.bakdata.conquery.sql.conversion.model.select.SqlSelects; +import com.bakdata.conquery.sql.conversion.model.select.ConnectorSqlSelects; import lombok.Value; @Value public class SqlFilters { - SqlSelects selects; + ConnectorSqlSelects selects; WhereClauses whereClauses; } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SumCondition.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SumCondition.java index ee19b8ee40..1159b87343 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SumCondition.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/filter/SumCondition.java @@ -1,11 +1,7 @@ package com.bakdata.conquery.sql.conversion.model.filter; -import javax.annotation.Nullable; - import com.bakdata.conquery.models.common.IRange; -import com.bakdata.conquery.models.datasets.Column; import org.jooq.Field; -import org.jooq.impl.DSL; public class SumCondition extends RangeCondition { @@ -13,22 +9,6 @@ public SumCondition(Field column, IRange super(column, range); } - public static SumCondition onColumn(Column column, @Nullable Column subtractColumn, IRange range) { - - String tableName = column.getTable().getName(); - String columnName = column.getName(); - Field field = DSL.field(DSL.name(tableName, columnName), Number.class); - - if (subtractColumn == null) { - return new SumCondition(field, range); - } - - String subtractColumnName = subtractColumn.getName(); - String subtractTableName = subtractColumn.getTable().getName(); - Field subtractField = DSL.field(DSL.name(subtractTableName, subtractColumnName), Number.class); - return new SumCondition(field.minus(subtractField), range); - } - @Override public ConditionType type() { return ConditionType.GROUP; diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java new file mode 100644 index 0000000000..df250611e7 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConceptSqlSelects.java @@ -0,0 +1,24 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import java.util.List; +import java.util.Optional; + +import com.bakdata.conquery.sql.conversion.model.QueryStep; +import lombok.Builder; +import lombok.Singular; +import lombok.Value; + +@Value +@Builder +public class ConceptSqlSelects { + + @Builder.Default + Optional additionalPredecessor = Optional.empty(); + + @Singular + List eventDateSelects; + + @Singular + List finalSelects; + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelects.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java similarity index 72% rename from backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelects.java rename to backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java index 47a1b06f65..4eaed8a4fc 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SqlSelects.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ConnectorSqlSelects.java @@ -10,19 +10,29 @@ @Value @Builder -public class SqlSelects { +public class ConnectorSqlSelects { + @Singular List preprocessingSelects; + // Empty if only used in event filter @Singular List aggregationSelects; + + // Selects that are applied on the aggregated validity date. + @Singular + List eventDateSelects; + // Empty if only used in aggregation select @Singular List finalSelects; - // Selects that require an interval-packed date - @Singular - List intervalPackingSelects; + // An additional predecessor these SqlSelects require @Builder.Default Optional additionalPredecessor = Optional.empty(); + + public static ConnectorSqlSelects none() { + return ConnectorSqlSelects.builder().build(); + } + } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java new file mode 100644 index 0000000000..0558140279 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDateUnionSelectConverter.java @@ -0,0 +1,51 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDateUnionSelect; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; +import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; +import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; +import com.google.common.base.Preconditions; + +public class EventDateUnionSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(EventDateUnionSelect select, SelectContext selectContext) { + + FieldWrapper stringAggregation = createEventDateUnionAggregation(select, selectContext); + ExtractingSqlSelect finalSelect = stringAggregation.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); + + return ConnectorSqlSelects.builder() + .eventDateSelect(stringAggregation) + .finalSelect(finalSelect) + .build(); + } + + @Override + public ConceptSqlSelects conceptSelect(EventDateUnionSelect select, SelectContext selectContext) { + + FieldWrapper stringAggregation = createEventDateUnionAggregation(select, selectContext); + ExtractingSqlSelect finalSelect = stringAggregation.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.UNIVERSAL_SELECTS)); + + return ConceptSqlSelects.builder() + .eventDateSelect(stringAggregation) + .finalSelect(finalSelect) + .build(); + } + + private static FieldWrapper createEventDateUnionAggregation(EventDateUnionSelect select, SelectContext selectContext) { + + Preconditions.checkArgument(selectContext.getValidityDate().isPresent(), "Can't convert an EventDateUnionSelect without a validity date being present"); + ColumnDateRange validityDate = selectContext.getValidityDate().get(); + + SqlFunctionProvider functionProvider = selectContext.getFunctionProvider(); + String alias = selectContext.getNameGenerator().selectName(select); + + ColumnDateRange qualified = validityDate.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.INTERVAL_PACKING_SELECTS)); + return new FieldWrapper<>(functionProvider.daterangeStringAggregation(qualified).as(alias)); + } + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java new file mode 100644 index 0000000000..87ab0900b8 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/EventDurationSumSelectConverter.java @@ -0,0 +1,70 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import java.math.BigDecimal; +import java.sql.Date; +import java.time.temporal.ChronoUnit; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.EventDurationSumSelect; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; +import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; +import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; +import com.google.common.base.Preconditions; +import org.jooq.Condition; +import org.jooq.Field; +import org.jooq.impl.DSL; + +public class EventDurationSumSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(EventDurationSumSelect select, SelectContext selectContext) { + + FieldWrapper stringAggregation = createEventDurationSumAggregation(select, selectContext); + ExtractingSqlSelect finalSelect = stringAggregation.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); + + return ConnectorSqlSelects.builder() + .eventDateSelect(stringAggregation) + .finalSelect(finalSelect) + .build(); + } + + @Override + public ConceptSqlSelects conceptSelect(EventDurationSumSelect select, SelectContext selectContext) { + + FieldWrapper stringAggregation = createEventDurationSumAggregation(select, selectContext); + ExtractingSqlSelect finalSelect = stringAggregation.qualify(selectContext.getTables().getPredecessor(ConceptCteStep.UNIVERSAL_SELECTS)); + + return ConceptSqlSelects.builder() + .eventDateSelect(stringAggregation) + .finalSelect(finalSelect) + .build(); + } + + private FieldWrapper createEventDurationSumAggregation(EventDurationSumSelect select, SelectContext selectContext) { + + Preconditions.checkArgument(selectContext.getValidityDate().isPresent(), "Can't convert an EventDateUnionSelect without a validity date being present"); + String predecessorCteName = selectContext.getTables().getPredecessor(ConceptCteStep.INTERVAL_PACKING_SELECTS); + ColumnDateRange qualified = selectContext.getValidityDate().get().qualify(predecessorCteName); + ColumnDateRange asDualColumn = selectContext.getFunctionProvider().toDualColumn(qualified); + + SqlFunctionProvider functionProvider = selectContext.getFunctionProvider(); + String alias = selectContext.getNameGenerator().selectName(select); + + Field durationSum = DSL.sum( + DSL.when(containsInfinityDate(asDualColumn, functionProvider), DSL.val(null, Integer.class)) + .otherwise(functionProvider.dateDistance(ChronoUnit.DAYS, asDualColumn.getStart(), asDualColumn.getEnd())) + ) + .as(alias); + return new FieldWrapper<>(durationSum); + } + + private static Condition containsInfinityDate(ColumnDateRange validityDate, SqlFunctionProvider functionProvider) { + Field negativeInfinity = functionProvider.toDateField(functionProvider.getMinDateExpression()); + Field positiveInfinity = functionProvider.toDateField(functionProvider.getMaxDateExpression()); + return validityDate.getStart().eq(negativeInfinity).or(validityDate.getEnd().eq(positiveInfinity)); + } + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java new file mode 100644 index 0000000000..b0c8561aa9 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ExistsSelectConverter.java @@ -0,0 +1,31 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.concept.specific.ExistsSelect; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; + +public class ExistsSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(ExistsSelect select, SelectContext selectContext) { + ExistsSqlSelect existsSqlSelect = createExistsSelect(select, selectContext); + return ConnectorSqlSelects.builder() + .finalSelect(existsSqlSelect) + .build(); + } + + @Override + public ConceptSqlSelects conceptSelect(ExistsSelect select, SelectContext selectContext) { + ExistsSqlSelect existsSqlSelect = createExistsSelect(select, selectContext); + return ConceptSqlSelects.builder() + .finalSelect(existsSqlSelect) + .build(); + } + + private static ExistsSqlSelect createExistsSelect(ExistsSelect select, SelectContext selectContext) { + String alias = selectContext.getNameGenerator().selectName(select); + return new ExistsSqlSelect(alias); + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FirstValueSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FirstValueSelectConverter.java new file mode 100644 index 0000000000..5a81632ba2 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/FirstValueSelectConverter.java @@ -0,0 +1,19 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.connector.FirstValueSelect; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; + +public class FirstValueSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(FirstValueSelect select, SelectContext selectContext) { + return ValueSelectUtil.createValueSelect( + select.getColumn(), + selectContext.getNameGenerator().selectName(select), + (valueField, orderByFields) -> selectContext.getFunctionProvider().first(valueField, orderByFields), + selectContext + ); + } + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/LastValueSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/LastValueSelectConverter.java new file mode 100644 index 0000000000..2ad575be52 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/LastValueSelectConverter.java @@ -0,0 +1,18 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.connector.LastValueSelect; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; + +public class LastValueSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(LastValueSelect select, SelectContext selectContext) { + return ValueSelectUtil.createValueSelect( + select.getColumn(), + selectContext.getNameGenerator().selectName(select), + (valueField, orderByFields) -> selectContext.getFunctionProvider().last(valueField, orderByFields), + selectContext + ); + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java new file mode 100644 index 0000000000..8e1fbf5eef --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/RandomValueSelectConverter.java @@ -0,0 +1,33 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.connector.RandomValueSelect; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; +import org.jooq.Field; + +public class RandomValueSelectConverter implements SelectConverter { + + @Override + public ConnectorSqlSelects connectorSelect(RandomValueSelect select, SelectContext selectContext) { + + ConnectorSqlTables tables = selectContext.getTables(); + + String rootTableName = tables.getRootTable(); + String columnName = select.getColumn().getName(); + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(rootTableName, columnName, Object.class); + + String alias = selectContext.getNameGenerator().selectName(select); + Field qualifiedRootSelect = rootSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); + Field firstAggregation = selectContext.getFunctionProvider().random(qualifiedRootSelect).as(alias); + FieldWrapper firstAggregationSqlSelect = new FieldWrapper<>(firstAggregation, columnName); + + ExtractingSqlSelect finalSelect = firstAggregationSqlSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); + + return ConnectorSqlSelects.builder() + .preprocessingSelect(rootSelect) + .aggregationSelect(firstAggregationSqlSelect) + .finalSelect(finalSelect) + .build(); + } +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java index d4c1069827..1f4ff5aee0 100644 --- a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectContext.java @@ -2,19 +2,58 @@ import java.util.Optional; +import com.bakdata.conquery.apiv1.query.concept.filter.CQTable; +import com.bakdata.conquery.apiv1.query.concept.specific.CQConcept; +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.SelectHolder; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; import com.bakdata.conquery.sql.conversion.Context; import com.bakdata.conquery.sql.conversion.cqelement.ConversionContext; -import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptConversionTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; +import com.bakdata.conquery.sql.conversion.dialect.SqlFunctionProvider; import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; import com.bakdata.conquery.sql.conversion.model.SqlIdColumns; +import com.bakdata.conquery.sql.conversion.model.SqlTables; +import lombok.AccessLevel; +import lombok.AllArgsConstructor; import lombok.Value; @Value -public class SelectContext implements Context { +@AllArgsConstructor(access = AccessLevel.PRIVATE) +public class SelectContext, T extends SqlTables> implements Context { + S selectHolder; SqlIdColumns ids; Optional validityDate; - ConceptConversionTables tables; + T tables; ConversionContext conversionContext; + public static SelectContext create( + CQTable cqTable, + SqlIdColumns ids, + Optional validityDate, + ConnectorSqlTables tables, + ConversionContext conversionContext + ) { + return new SelectContext<>(cqTable.getConnector(), ids, validityDate, tables, conversionContext); + } + + public static SelectContext create( + CQConcept cqConcept, + SqlIdColumns ids, + Optional validityDate, + ConceptSqlTables tables, + ConversionContext conversionContext + ) { + if (!(cqConcept.getConcept() instanceof TreeConcept treeConcept)) { + throw new IllegalArgumentException("Cannot create a select context for a concept that is not a TreeConcept"); + } + return new SelectContext<>(treeConcept, ids, validityDate, tables, conversionContext); + } + + public SqlFunctionProvider getFunctionProvider() { + return getSqlDialect().getFunctionProvider(); + } + } diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectConverter.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectConverter.java new file mode 100644 index 0000000000..fc043a84b6 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/SelectConverter.java @@ -0,0 +1,19 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.models.datasets.concepts.select.Select; +import com.bakdata.conquery.models.datasets.concepts.tree.TreeConcept; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptSqlTables; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; + +public interface SelectConverter { + + default ConnectorSqlSelects connectorSelect(S select, SelectContext selectContext) { + throw new UnsupportedOperationException("Conversion of Select %s not implemented on Connector-level".formatted(select.getClass())); + } + + default ConceptSqlSelects conceptSelect(S select, SelectContext selectContext) { + throw new UnsupportedOperationException("Conversion of Select %s not implemented or not possible on Concept-level".formatted(select.getClass())); + } + +} diff --git a/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java new file mode 100644 index 0000000000..633a520df1 --- /dev/null +++ b/backend/src/main/java/com/bakdata/conquery/sql/conversion/model/select/ValueSelectUtil.java @@ -0,0 +1,46 @@ +package com.bakdata.conquery.sql.conversion.model.select; + +import java.util.Collections; +import java.util.List; +import java.util.function.BiFunction; + +import com.bakdata.conquery.models.datasets.Column; +import com.bakdata.conquery.models.datasets.concepts.Connector; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConceptCteStep; +import com.bakdata.conquery.sql.conversion.cqelement.concept.ConnectorSqlTables; +import com.bakdata.conquery.sql.conversion.model.ColumnDateRange; +import org.jooq.Field; + +class ValueSelectUtil { + + public static ConnectorSqlSelects createValueSelect( + Column column, + String alias, + BiFunction, List>, Field> aggregationFunction, + SelectContext selectContext + ) { + ConnectorSqlTables tables = selectContext.getTables(); + + String rootTableName = tables.getRootTable(); + String columnName = column.getName(); + ExtractingSqlSelect rootSelect = new ExtractingSqlSelect<>(rootTableName, columnName, Object.class); + + List> validityDateFields = + selectContext.getValidityDate().map(dateRange -> dateRange.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT))) + .map(ColumnDateRange::toFields) + .orElse(Collections.emptyList()); + + Field qualifiedRootSelect = rootSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_SELECT)).select(); + Field firstAggregation = aggregationFunction.apply(qualifiedRootSelect, validityDateFields).as(alias); + FieldWrapper firstAggregationSqlSelect = new FieldWrapper<>(firstAggregation, columnName); + + ExtractingSqlSelect finalSelect = firstAggregationSqlSelect.qualify(tables.getPredecessor(ConceptCteStep.AGGREGATION_FILTER)); + + return ConnectorSqlSelects.builder() + .preprocessingSelect(rootSelect) + .aggregationSelect(firstAggregationSqlSelect) + .finalSelect(finalSelect) + .build(); + } + +}