You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
507 lines
20 KiB
507 lines
20 KiB
package de.superx.bianalysis; |
|
|
|
import java.util.ArrayList; |
|
import java.util.HashMap; |
|
import java.util.List; |
|
import java.util.Map; |
|
import java.util.Optional; |
|
import java.util.stream.Collectors; |
|
|
|
import de.superx.bianalysis.metadata.Identifier; |
|
import de.superx.bianalysis.models.DimensionAttribute; |
|
import de.superx.bianalysis.models.FactTable; |
|
import de.superx.bianalysis.models.Filter; |
|
import de.superx.bianalysis.models.InfoItem; |
|
import de.superx.bianalysis.models.Measure; |
|
import de.superx.bianalysis.service.DbMetaAdapter; |
|
import de.superx.jdbc.entity.Sachgebiet; |
|
|
|
public class ReportMetadata { |
|
|
|
public final FactTable factTable; |
|
public final Sachgebiet sachgebiet; |
|
public final List<DimensionAttribute> leftDimensionAttributes; |
|
public final List<DimensionAttribute> topDimensionAttributes; |
|
public final List<Measure> measures; |
|
public final List<Filter> filters; |
|
public final List<SecondaryFacttable> secondaryFacttables; |
|
|
|
public String lastBiadUpdate; |
|
|
|
public DbMetaAdapter dbMetaAdapter; |
|
|
|
public boolean hideEmptyColumns; |
|
public boolean isOuter = false; // TODO ugly |
|
|
|
public ReportMetadata(ReportDefinition reportDefinition, Identifier factTableId, DbMetaAdapter dbAdapter) { |
|
this.dbMetaAdapter = dbAdapter; |
|
this.hideEmptyColumns = reportDefinition.hideEmptyColumns; |
|
this.factTable = dbAdapter.getFactTable(factTableId); |
|
this.sachgebiet = dbAdapter.getSachgebietById(this.factTable.getSachgebiettid()); |
|
List<DimensionAttribute> databaseOrderedLeftDimensionAttributes = dbAdapter.getDimensionAttributeMetadata(reportDefinition.leftDimensionAttributeIds, factTableId); |
|
this.leftDimensionAttributes = reorderDimensionAttributesToReportOrder(databaseOrderedLeftDimensionAttributes, reportDefinition, false); |
|
List<DimensionAttribute> databaseOrderedTopDimensionAttributes = dbAdapter.getDimensionAttributeMetadata(reportDefinition.topDimensionAttributeIds, factTableId); |
|
this.topDimensionAttributes = reorderDimensionAttributesToReportOrder(databaseOrderedTopDimensionAttributes, reportDefinition, true); |
|
List<Measure> databaseOrderedMeasures = dbAdapter.getMeasureMetadata(reportDefinition.measureIds); |
|
this.measures = reorderMeasuresToReportOrder(databaseOrderedMeasures, reportDefinition); |
|
if (reportDefinition.filters != null) { |
|
this.filters = dbAdapter.getFilterMetadata(reportDefinition.filters); |
|
} else { |
|
this.filters = new ArrayList<Filter>(); |
|
} |
|
this.setTopDimensionAttributeValues(dbAdapter, factTableId); |
|
this.lastBiadUpdate = dbAdapter.getLastUpdate(440); |
|
this.secondaryFacttables = dbAdapter.getSecondaryFactTableMetadata(reportDefinition.secondaryFacttables, reportDefinition.leftDimensionAttributeIds); |
|
|
|
// TODO Ugly workaround to add measures to all measures from other facttables |
|
// if there is a outer join facttable we handle this elsewhere |
|
for (SecondaryFacttable sf : secondaryFacttables) { |
|
if(!hasOuter()) { |
|
for (Measure measure : sf.measures) { |
|
this.measures.add(measure); |
|
} |
|
} |
|
} |
|
|
|
} |
|
|
|
/** |
|
* Determines which dimension attribute a secondary fact table should use. |
|
* Conformed attributes are mapped to the matching role-playing attribute of the |
|
* secondary dimension. If no mapping exists, or if the attribute is not conformed, |
|
* a null/grouping copy of the provided attribute is returned instead. |
|
*/ |
|
private DimensionAttribute resolveDimensionAttributeForSecondaryFacttable(DimensionAttribute attr, SecondaryFacttable secondaryFacttable) { |
|
|
|
String conformedDimensionId = attr.getDimConformedId(); |
|
if(conformedDimensionId == null) { |
|
DimensionAttribute attribute = this.dbMetaAdapter.getDimensionAttributeMetadata(attr.getId()); |
|
attribute.setNullAttribute(true); |
|
attribute.setGrouping(true); |
|
return attribute; |
|
} |
|
|
|
DimensionAttribute result = null; |
|
for (JoinCriteria joinCriteria : secondaryFacttable.joinCriterias) { |
|
|
|
String conformedDimensionIdOfSecondaryDimension = joinCriteria.secondaryDimension.getConformed(); |
|
if(conformedDimensionId.equals(conformedDimensionIdOfSecondaryDimension)) { |
|
|
|
Identifier conformedAttrId = new Identifier(attr.getAttrConformedId()); |
|
Identifier rolePlayingDimId = joinCriteria.secondaryDimension.getId(); |
|
|
|
Optional<Identifier> rolePlayingAttributeId = |
|
dbMetaAdapter.findRolePlayingAttributeId(conformedAttrId, rolePlayingDimId); |
|
|
|
if(rolePlayingAttributeId.isPresent()) { |
|
result = this.dbMetaAdapter.getDimensionAttributeMetadata(rolePlayingAttributeId.get()); |
|
result.setGrouping(true); |
|
} |
|
} |
|
} |
|
|
|
if(result == null) { |
|
result = this.dbMetaAdapter.getDimensionAttributeMetadata(new Identifier(attr.getId())); |
|
result.setNullAttribute(true); |
|
result.setGrouping(true); |
|
} |
|
|
|
return result; |
|
} |
|
|
|
public HashMap<String, ReportMetadata> buildReportMetadatasForDrillAcross(){ |
|
|
|
List<Identifier> allMeasures = new ArrayList<>(); |
|
for (Measure m : measures) { |
|
allMeasures.add(m.getId()); |
|
} |
|
|
|
for (SecondaryFacttable sf : secondaryFacttables) { |
|
allMeasures.addAll(sf.measureIds); |
|
} |
|
|
|
// all metadata objects |
|
HashMap<String, ReportMetadata> factMetadatas = new HashMap<String, ReportMetadata>(); |
|
|
|
ReportMetadata primaryReportMetadata = new ReportMetadata(this, this.secondaryFacttables, allMeasures); |
|
factMetadatas.put(factTable.getId().composedId, primaryReportMetadata); |
|
|
|
// Every secondary facttable gets its own ReportMetadata |
|
for (SecondaryFacttable secondaryFacttable : secondaryFacttables) { |
|
if(!secondaryFacttable.join.equals("union")) { |
|
continue; |
|
} |
|
|
|
List<DimensionAttribute> leftAttributes = new ArrayList<>(); |
|
List<DimensionAttribute> rightAttributes = new ArrayList<>(); |
|
List<Filter> filterAttributes = new ArrayList<>(); |
|
List<Measure> measures2 = dbMetaAdapter.getMeasureMetadata(allMeasures); |
|
|
|
for (Measure measure : measures2) { |
|
if(!measure.getFacttableId().equals(secondaryFacttable.factId)) { |
|
measure.isNullMeasure = true; |
|
} |
|
} |
|
|
|
boolean isNullAttribute = false; |
|
for (DimensionAttribute attr : leftDimensionAttributes) { |
|
DimensionAttribute rolePlayingAttribute = resolveDimensionAttributeForSecondaryFacttable(attr, secondaryFacttable); |
|
|
|
if(isNullAttribute) { |
|
rolePlayingAttribute.setNullAttribute(true); |
|
} |
|
|
|
leftAttributes.add(rolePlayingAttribute); |
|
|
|
// if first is a null attribute all other automatically become null |
|
// attributes, because no drill down possible |
|
if(rolePlayingAttribute.isNullAttribute()) { |
|
isNullAttribute = true; |
|
} |
|
|
|
} |
|
|
|
isNullAttribute = false; |
|
for (DimensionAttribute attr : topDimensionAttributes) { |
|
DimensionAttribute rolePlayingAttribute = resolveDimensionAttributeForSecondaryFacttable(attr, secondaryFacttable); |
|
|
|
if(isNullAttribute) { |
|
rolePlayingAttribute.setNullAttribute(true); |
|
} |
|
|
|
rightAttributes.add(rolePlayingAttribute); |
|
|
|
// if first is a null attribute all other automatically become null |
|
// attributes, because no drill down possible |
|
if(rolePlayingAttribute.isNullAttribute()) { |
|
isNullAttribute = true; |
|
} |
|
} |
|
|
|
|
|
for (Filter f : filters) { |
|
Filter filter = new Filter(f); |
|
DimensionAttribute attr = dbMetaAdapter.getDimensionAttributeMetadataById(filter.dimensionAttributeId); |
|
DimensionAttribute rolePlayingAttribute = resolveDimensionAttributeForSecondaryFacttable(attr, secondaryFacttable); |
|
if(!rolePlayingAttribute.isNullAttribute()) { |
|
filter.dimensionAttributeId = rolePlayingAttribute.getId(); |
|
dbMetaAdapter.setFilterMetadata(filter); |
|
filterAttributes.add(filter); |
|
} |
|
} |
|
|
|
ReportMetadata secondaryFacttableMetadata = new ReportMetadata(secondaryFacttable.factTable, leftAttributes, rightAttributes, measures2, filterAttributes, dbMetaAdapter); |
|
|
|
// TODO all values from all facttables |
|
secondaryFacttableMetadata.setTopDimensionAttributeValues(dbMetaAdapter, secondaryFacttableMetadata.factTable.getId()); |
|
|
|
secondaryFacttableMetadata.isOuter = isOuter; |
|
factMetadatas.put(secondaryFacttable.factId.composedId, secondaryFacttableMetadata); |
|
} |
|
|
|
|
|
// set top dimension attribute filter values |
|
for (DimensionAttribute attribute : topDimensionAttributes) { |
|
DimensionAttribute confAttr = |
|
dbMetaAdapter.getDimensionAttributeMetadata(new Identifier(attribute.getAttrConformedId())); |
|
List<String> values |
|
= dbMetaAdapter.getTopDimensionAttributeValuesForFactTables(confAttr, attribute, factTable, this.secondaryFacttables); |
|
for (ReportMetadata metadata : factMetadatas.values()) { |
|
for (DimensionAttribute attr : metadata.topDimensionAttributes) { |
|
if(attr.getAttrConformedId() == null) { |
|
continue; |
|
} |
|
if(attr.getAttrConformedId().equals(attribute.getAttrConformedId())) { |
|
attr.setDimensionAttributeValues(values); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return factMetadatas; |
|
} |
|
|
|
public ReportMetadata(ReportMetadata metadata, List<SecondaryFacttable> secondary, List<Identifier> measureIds) { |
|
this.dbMetaAdapter = metadata.dbMetaAdapter; |
|
this.factTable = metadata.factTable; |
|
this.sachgebiet = metadata.sachgebiet; |
|
this.topDimensionAttributes = metadata.topDimensionAttributes; |
|
|
|
List<SecondaryFacttable> secondaryFacttables = new ArrayList<>(); |
|
|
|
for (SecondaryFacttable sf : secondary) { |
|
if(!sf.join.equals("union")) { |
|
secondaryFacttables.add(sf); |
|
} |
|
} |
|
|
|
this.measures = dbMetaAdapter.getMeasureMetadata(measureIds); |
|
|
|
for (Measure measure : this.measures) { |
|
// own facttable OR secondaryFacttable where inner or left join |
|
boolean isLeftOrInnerJoinFactTable = false; |
|
for (SecondaryFacttable sf : secondaryFacttables) { |
|
if(sf.measureIds.contains(measure.getId())) { |
|
isLeftOrInnerJoinFactTable = true; |
|
} |
|
} |
|
if(!isLeftOrInnerJoinFactTable && !measure.getFacttableId().equals(factTable.getId())) { |
|
measure.isNullMeasure = true; |
|
} |
|
} |
|
|
|
this.filters = metadata.filters; |
|
this.leftDimensionAttributes = metadata.leftDimensionAttributes; |
|
for (DimensionAttribute attribute : this.leftDimensionAttributes) { |
|
attribute.setGrouping(true); |
|
} |
|
this.hideEmptyColumns = metadata.hideEmptyColumns; |
|
this.secondaryFacttables = secondaryFacttables; |
|
} |
|
|
|
public ReportMetadata() { |
|
this.factTable = new FactTable(); |
|
this.sachgebiet = new Sachgebiet(); |
|
this.leftDimensionAttributes = new ArrayList<>(); |
|
this.topDimensionAttributes = new ArrayList<>(); |
|
this.measures = new ArrayList<>(); |
|
this.filters = new ArrayList<>(); |
|
this.secondaryFacttables = new ArrayList<>(); |
|
} |
|
|
|
public ReportMetadata(FactTable factTable, List<DimensionAttribute> leftDimensionAttributes2, List<DimensionAttribute> topDimensionAttributes, List<Measure> measures2, List<Filter> filterAttributes, |
|
DbMetaAdapter dbAdapter) { |
|
this.dbMetaAdapter = dbAdapter; |
|
this.factTable = factTable; |
|
this.sachgebiet = new Sachgebiet(); |
|
this.topDimensionAttributes = topDimensionAttributes; |
|
this.measures = measures2; |
|
this.leftDimensionAttributes = leftDimensionAttributes2; |
|
this.hideEmptyColumns = false; |
|
this.filters = filterAttributes; |
|
this.secondaryFacttables = new ArrayList<>(); |
|
} |
|
|
|
public List<DimensionAttribute> getSortOrderLeftDimensionAttributes(){ |
|
return leftDimensionAttributes.stream().filter(d -> d.getSortOrderColumn() != null).collect(Collectors.toList()); |
|
} |
|
|
|
private void setTopDimensionAttributeValues(DbMetaAdapter dbAdapter, Identifier factId) { |
|
for(DimensionAttribute attr : this.topDimensionAttributes) { |
|
List<Filter> filtersInDimension = new ArrayList<Filter>(); |
|
for(Filter filter : this.filters) { |
|
if(filter.tablename.equals(attr.getTablename())) { |
|
filtersInDimension.add(filter); |
|
} |
|
} |
|
attr.setDimensionAttributeValues(dbAdapter.getValuesOfAttribute(attr, factId, filtersInDimension, Boolean.valueOf(hideEmptyColumns))); |
|
} |
|
} |
|
|
|
private Filter getFilterForDimensionAttribute(Identifier id) { |
|
return this.filters |
|
.stream() |
|
.filter(f -> f.dimensionAttributeId.equals(id)) |
|
.findFirst() |
|
.orElse(null); |
|
} |
|
|
|
private static List<Measure> reorderMeasuresToReportOrder(List<Measure> measures, ReportDefinition reportDefinition) { |
|
List<Measure> orderedMeasures = new ArrayList<Measure>(); |
|
reportDefinition.measureIds.forEach(measureId -> { |
|
Measure nextMeasure = measures |
|
.stream() |
|
.filter( measure -> measure.getId().equals( measureId ) ) |
|
.findFirst() |
|
.orElse(null); |
|
orderedMeasures.add(nextMeasure); |
|
}); |
|
return orderedMeasures; |
|
} |
|
|
|
public static List<DimensionAttribute> reorderDimensionAttributesToReportOrder(List<DimensionAttribute> dimensionAttributes, ReportDefinition reportDefinition, boolean isTopAttribute) { |
|
List<DimensionAttribute> orderedDimensionAttributes = new ArrayList<DimensionAttribute>(); |
|
List<Identifier> attributeIds; |
|
if (isTopAttribute) { |
|
attributeIds = reportDefinition.topDimensionAttributeIds; |
|
} else { |
|
attributeIds = reportDefinition.leftDimensionAttributeIds; |
|
} |
|
attributeIds.forEach(attributeId -> { |
|
DimensionAttribute nextAttribute = dimensionAttributes |
|
.stream() |
|
.filter( dimensionAttribute -> dimensionAttribute.getId().equals( attributeId )) |
|
.findFirst() |
|
.orElse(null); |
|
orderedDimensionAttributes.add(nextAttribute); |
|
}); |
|
return orderedDimensionAttributes; |
|
} |
|
|
|
|
|
public DimensionAttribute getDimById(Identifier id) { |
|
DimensionAttribute attr = topDimensionAttributes |
|
.stream() |
|
.filter(a -> a.getDimensionId().equals(id)) |
|
.findFirst() |
|
.orElse(null); |
|
|
|
if(attr != null) { |
|
return attr; |
|
} |
|
|
|
attr = leftDimensionAttributes |
|
.stream() |
|
.filter(a -> a.getDimensionId().equals(id)) |
|
.findFirst() |
|
.orElse(null); |
|
|
|
return attr; |
|
} |
|
|
|
public DimensionAttribute getDimAttrById(Identifier id) { |
|
DimensionAttribute attr = topDimensionAttributes |
|
.stream() |
|
.filter(a -> a.getId().equals(id)) |
|
.findFirst() |
|
.orElse(null); |
|
|
|
if(attr != null) { |
|
return attr; |
|
} |
|
|
|
attr = leftDimensionAttributes |
|
.stream() |
|
.filter(a -> a.getId().equals(id)) |
|
.findFirst() |
|
.orElse(null); |
|
|
|
return attr; |
|
} |
|
|
|
/** |
|
* We want to join dimension tables only once. There we need a list of unique ids of |
|
* the dimensions, otherwise we duplicate our joins. |
|
*/ |
|
public List<DimensionAttribute> getUniqueDimensionAttributes(){ |
|
|
|
Map<String, DimensionAttribute> joinTables = new HashMap<String, DimensionAttribute>(); |
|
|
|
for (DimensionAttribute attr : leftDimensionAttributes) { |
|
if(attr.isNullAttribute()) { |
|
continue; |
|
} |
|
if(!joinTables.containsKey(attr.getDimensionTableAlias())) { |
|
joinTables.put(attr.getDimensionTableAlias(), attr); |
|
} |
|
} |
|
|
|
for (DimensionAttribute attr : topDimensionAttributes) { |
|
if(attr.isNullAttribute()) { |
|
continue; |
|
} |
|
if(!joinTables.containsKey(attr.getDimensionTableAlias())) { |
|
joinTables.put(attr.getDimensionTableAlias(), attr); |
|
} |
|
} |
|
|
|
// join dimension if attribute occurs in filter |
|
for (Filter filter : filters) { |
|
Identifier attrId = filter.dimensionAttributeId; |
|
DimensionAttribute attr = dbMetaAdapter.getDimensionAttributeMetadataById(attrId); |
|
if(attr.isNullAttribute()) { |
|
continue; |
|
} |
|
if(!joinTables.containsKey(attr.getDimensionTableAlias())) { |
|
joinTables.put(attr.getDimensionTableAlias(), attr); |
|
} |
|
} |
|
|
|
// join dimension if measure has a filter for an attribute |
|
for (Measure measure : measures) { |
|
if(measure.filterAttributeId != null) { |
|
Identifier attrId = measure.filterAttributeId; |
|
DimensionAttribute attr = dbMetaAdapter.getDimensionAttributeMetadataById(attrId); |
|
if(!joinTables.containsKey(attr.getDimensionTableAlias())) { |
|
joinTables.put(attr.getDimensionTableAlias(), attr); |
|
} |
|
} |
|
} |
|
|
|
return new ArrayList<DimensionAttribute>(joinTables.values()); |
|
} |
|
|
|
public List<InfoItem> getMeasureInfo() { |
|
if(measures != null && !measures.isEmpty()) { |
|
return measures.stream().map(m-> |
|
new InfoItem(m.getId().composedId, m.getCaption(), m.getDescription())).collect(Collectors.toList()); |
|
} |
|
return null; |
|
} |
|
|
|
public List<InfoItem> getTopDimAttrAsInfo() { |
|
if(topDimensionAttributes != null && !topDimensionAttributes.isEmpty()) { |
|
return topDimensionAttributes.stream().map(m-> |
|
new InfoItem(m.getStringId(), m.getCaption(), m.getDescription())).collect(Collectors.toList()); |
|
} |
|
return null; |
|
} |
|
|
|
public List<InfoItem> getLeftDimAttrAsInfo() { |
|
if(leftDimensionAttributes != null && !leftDimensionAttributes.isEmpty()) { |
|
return leftDimensionAttributes.stream().map(m-> |
|
new InfoItem(m.getStringId(), m.getCaption(), m.getDescription())).collect(Collectors.toList()); |
|
} |
|
return null; |
|
} |
|
|
|
public List<String> getFilterAsInfo(){ |
|
ArrayList<String> filterList = new ArrayList<String>(); |
|
DimensionAttribute dimAttr; |
|
for (Filter filter: filters) { |
|
dimAttr = filter.getDimAttribute(this); |
|
if(dimAttr == null) { |
|
dimAttr = dbMetaAdapter.getDimensionAttributeById(filter.dimensionAttributeId); |
|
} |
|
filterList.add(" (" + dimAttr.getCaption() + ") " + filter.getValuesAsString()); |
|
} |
|
return filterList; |
|
} |
|
|
|
public List<String> getSacondaryFacttableAsInfo(){ |
|
ArrayList<String> secondaryFacttableList = new ArrayList<String>(); |
|
|
|
for (SecondaryFacttable secondaryFacttable: secondaryFacttables) { |
|
String label = ""; |
|
if(secondaryFacttable.join.equals("left")) { |
|
label+= "Inklusion "; |
|
} else if(secondaryFacttable.join.equals("inner")) { |
|
label+= "Schnittmenge "; |
|
} else if(secondaryFacttable.join.equals("union")) { |
|
label+= "Vereinigung "; |
|
} |
|
label+= "mit "; |
|
label+=(secondaryFacttable.factTable.getCaption()); |
|
label+= " über "; |
|
for( int i = 0; i < secondaryFacttable.joinCriterias.size(); i++) { |
|
label+=(secondaryFacttable.joinCriterias.get(i).secondaryDimension.getCaption()); |
|
if(i < secondaryFacttable.joinCriterias.size() -1) { |
|
label+= " und "; |
|
} |
|
} |
|
secondaryFacttableList.add(label); |
|
} |
|
return secondaryFacttableList; |
|
} |
|
|
|
public boolean hasOuter() { |
|
for (SecondaryFacttable factable : this.secondaryFacttables) { |
|
if(factable.join.equals("union")) { |
|
return true; |
|
} |
|
} |
|
return false; |
|
} |
|
|
|
|
|
} |