SuperX-Kernmodul
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

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;
}
}