blob: 1d85fbde881a88e0357c6eac20d81b19382a01f3 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.lens.cube.metadata;
import java.lang.reflect.Constructor;
import java.util.*;
import org.apache.commons.lang.StringUtils;
import org.apache.hadoop.hive.ql.metadata.Table;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Cube extends AbstractBaseTable implements CubeInterface {
private final Set<CubeMeasure> measures;
private final Set<CubeDimAttribute> dimensions;
private final Map<String, CubeMeasure> measureMap;
private final Map<String, CubeDimAttribute> dimMap;
public Cube(String name, Set<CubeMeasure> measures, Set<CubeDimAttribute> dimensions) {
this(name, measures, dimensions, new HashMap<String, String>());
}
public Cube(String name, Set<CubeMeasure> measures, Set<CubeDimAttribute> dimensions,
Map<String, String> properties) {
this(name, measures, dimensions, properties, 0L);
}
public Cube(String name, Set<CubeMeasure> measures, Set<CubeDimAttribute> dimensions, Map<String, String> properties,
double weight) {
this(name, measures, dimensions, null, null, properties, weight);
}
public Cube(String name, Set<CubeMeasure> measures, Set<CubeDimAttribute> dimensions, Set<ExprColumn> expressions,
Set<JoinChain> joinChains, Map<String, String> properties, double weight) {
super(name, expressions, joinChains, properties, weight);
this.measures = measures;
this.dimensions = dimensions;
measureMap = new HashMap<>();
for (CubeMeasure m : measures) {
measureMap.put(m.getName().toLowerCase(), m);
}
dimMap = new HashMap<>();
for (CubeDimAttribute dim : dimensions) {
dimMap.put(dim.getName().toLowerCase(), dim);
}
addProperties();
}
public Cube(Table tbl) {
super(tbl);
this.measures = getMeasures(getName(), getProperties());
this.dimensions = getDimensions(getName(), getProperties());
measureMap = new HashMap<>();
for (CubeMeasure m : measures) {
measureMap.put(m.getName().toLowerCase(), m);
}
dimMap = new HashMap<>();
for (CubeDimAttribute dim : dimensions) {
addAllDimsToMap(dim);
}
}
private void addAllDimsToMap(CubeDimAttribute dim) {
dimMap.put(dim.getName().toLowerCase(), dim);
if (dim instanceof HierarchicalDimAttribute) {
for (CubeDimAttribute d : ((HierarchicalDimAttribute) dim).getHierarchy()) {
addAllDimsToMap(d);
}
}
}
public Set<CubeMeasure> getMeasures() {
return measures;
}
public Optional<Date> getColumnStartTime(String column) {
return Optional.ofNullable(getColumnByName(column)).map(CubeColumn::getStartTime);
}
public Optional<Date> getColumnEndTime(String column) {
return Optional.ofNullable(getColumnByName(column)).map(CubeColumn::getEndTime);
}
public Set<CubeDimAttribute> getDimAttributes() {
return dimensions;
}
public Set<String> getTimedDimensions() {
String str = getProperties().get(MetastoreUtil.getCubeTimedDimensionListKey(getName()));
Set<String> timedDimensions = new HashSet<>();
if (str != null) {
timedDimensions.addAll(Arrays.asList(StringUtils.split(str, ',')));
}
return timedDimensions;
}
@Override
public CubeTableType getTableType() {
return CubeTableType.CUBE;
}
@Override
public void addProperties() {
super.addProperties();
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeMeasureListKey(getName()), measures);
setMeasureProperties(getProperties(), measures);
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeDimensionListKey(getName()), dimensions);
setDimensionProperties(getProperties(), dimensions);
}
private static void setMeasureProperties(Map<String, String> props, Set<CubeMeasure> measures) {
for (CubeMeasure measure : measures) {
measure.addProperties(props);
}
}
private static void setDimensionProperties(Map<String, String> props, Set<CubeDimAttribute> dimensions) {
for (CubeDimAttribute dimension : dimensions) {
dimension.addProperties(props);
}
}
public static Set<CubeMeasure> getMeasures(String name, Map<String, String> props) {
Set<CubeMeasure> measures = new HashSet<>();
String measureStr = MetastoreUtil.getNamedStringValue(props, MetastoreUtil.getCubeMeasureListKey(name));
String[] names = measureStr.split(",");
for (String measureName : names) {
String className = props.get(MetastoreUtil.getMeasureClassPropertyKey(measureName));
CubeMeasure measure;
try {
Class<?> clazz = Class.forName(className);
Constructor<?> constructor;
constructor = clazz.getConstructor(String.class, Map.class);
measure = (CubeMeasure) constructor.newInstance(new Object[]{measureName, props});
} catch (Exception e) {
throw new IllegalArgumentException("Invalid measure", e);
}
measures.add(measure);
}
return measures;
}
public static Set<CubeDimAttribute> getDimensions(String name, Map<String, String> props) {
Set<CubeDimAttribute> dimensions = new HashSet<>();
String dimStr = MetastoreUtil.getNamedStringValue(props, MetastoreUtil.getCubeDimensionListKey(name));
if (StringUtils.isNotBlank(dimStr)) {
String[] names = dimStr.split(",");
for (String dimName : names) {
String className = props.get(MetastoreUtil.getDimensionClassPropertyKey(dimName));
CubeDimAttribute dim;
try {
Class<?> clazz = Class.forName(className);
Constructor<?> constructor;
constructor = clazz.getConstructor(String.class, Map.class);
dim = (CubeDimAttribute) constructor.newInstance(new Object[]{dimName, props});
} catch (Exception e) {
throw new IllegalArgumentException("Invalid dimension", e);
}
dimensions.add(dim);
}
}
return dimensions;
}
@Override
public int hashCode() {
return super.hashCode();
}
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
Cube other = (Cube) obj;
if (this.getMeasures() == null) {
if (other.getMeasures() != null) {
return false;
}
} else if (!this.getMeasures().equals(other.getMeasures())) {
return false;
}
if (this.getDimAttributes() == null) {
if (other.getDimAttributes() != null) {
return false;
}
} else if (!this.getDimAttributes().equals(other.getDimAttributes())) {
return false;
}
return true;
}
public CubeDimAttribute getDimAttributeByName(String dimension) {
return dimMap.get(dimension == null ? null : dimension.toLowerCase());
}
public CubeMeasure getMeasureByName(String measure) {
return measureMap.get(measure == null ? null : measure.toLowerCase());
}
public CubeColumn getColumnByName(String column) {
CubeColumn cubeCol = super.getExpressionByName(column);
if (cubeCol == null) {
cubeCol = getMeasureByName(column);
if (cubeCol == null) {
cubeCol = getDimAttributeByName(column);
}
}
return cubeCol;
}
/**
* Alters the measure if already existing or just adds if it is new measure.
*
* @param measure new measure definition
*/
public void alterMeasure(@NonNull CubeMeasure measure) {
// Replace measure if already existing
if (measureMap.containsKey(measure.getName().toLowerCase())) {
measures.remove(getMeasureByName(measure.getName()));
log.info("Replacing measure {} with {}", getMeasureByName(measure.getName()), measure);
}
measures.add(measure);
measureMap.put(measure.getName().toLowerCase(), measure);
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeMeasureListKey(getName()), measures);
measure.addProperties(getProperties());
}
/**
* Remove the joinchain with name specified
*
* @param chainName chain name
*/
public boolean removeJoinChain(@NonNull String chainName) {
if (super.removeJoinChain(chainName)) {
log.info("Removing dimension {}", getDimAttributeByName(chainName));
return true;
}
return false;
}
/**
* Alters the dimension if already existing or just adds if it is new dimension
*
* @param dimension the dim attribute
*/
public void alterDimension(@NonNull CubeDimAttribute dimension) {
// Replace dimension if already existing
if (dimMap.containsKey(dimension.getName().toLowerCase())) {
dimensions.remove(getDimAttributeByName(dimension.getName()));
log.info("Replacing dimension {} with {}", getDimAttributeByName(dimension.getName()), dimension);
}
dimensions.add(dimension);
dimMap.put(dimension.getName().toLowerCase(), dimension);
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeDimensionListKey(getName()), dimensions);
dimension.addProperties(getProperties());
}
/**
* Remove the attribute with name specified
*
* @param dimName attribute name
*/
public void removeDimension(@NonNull String dimName) {
if (dimMap.containsKey(dimName.toLowerCase())) {
log.info("Removing dimension {}", getDimAttributeByName(dimName));
dimensions.remove(getDimAttributeByName(dimName));
dimMap.remove(dimName.toLowerCase());
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeDimensionListKey(getName()), dimensions);
}
}
/**
* Remove the measure with name specified
*
* @param msrName measure name
*/
public void removeMeasure(@NonNull String msrName) {
if (measureMap.containsKey(msrName.toLowerCase())) {
log.info("Removing measure {}", getMeasureByName(msrName));
measures.remove(getMeasureByName(msrName));
measureMap.remove(msrName.toLowerCase());
MetastoreUtil.addNameStrings(getProperties(), MetastoreUtil.getCubeMeasureListKey(getName()), measures);
}
}
/**
* Adds the timed dimension
*
* @param timedDimension time dimension
*/
public void addTimedDimension(@NonNull String timedDimension) {
timedDimension = timedDimension.toLowerCase();
Set<String> timeDims = getTimedDimensions();
if (timeDims == null) {
timeDims = new LinkedHashSet<>();
}
if (timeDims.contains(timedDimension)) {
log.info("Timed dimension {} is already present in cube {}", timedDimension, getName());
return;
}
timeDims.add(timedDimension);
getProperties().put(MetastoreUtil.getCubeTimedDimensionListKey(getName()), StringUtils.join(timeDims, ","));
}
/**
* Removes the timed dimension
*
* @param timedDimension time dimension
*/
public void removeTimedDimension(@NonNull String timedDimension) {
timedDimension = timedDimension.toLowerCase();
Set<String> timeDims = getTimedDimensions();
if (timeDims != null && timeDims.contains(timedDimension)) {
timeDims.remove(timedDimension);
getProperties().put(MetastoreUtil.getCubeTimedDimensionListKey(getName()), StringUtils.join(timeDims, ","));
}
}
@Override
public boolean isDerivedCube() {
return false;
}
@Override
public Set<String> getMeasureNames() {
Set<String> measureNames = new HashSet<>();
for (CubeMeasure f : getMeasures()) {
measureNames.add(f.getName().toLowerCase());
}
return measureNames;
}
@Override
public Set<String> getDimAttributeNames() {
Set<String> dimNames = new HashSet<>();
for (CubeDimAttribute f : getDimAttributes()) {
MetastoreUtil.addColumnNames(f, dimNames);
}
return dimNames;
}
@Override
public boolean allFieldsQueriable() {
String canBeQueried = getProperties().get(MetastoreConstants.CUBE_ALL_FIELDS_QUERIABLE);
return canBeQueried == null || Boolean.parseBoolean(canBeQueried.toLowerCase());
}
@Override
public Set<String> getAllFieldNames() {
Set<String> fieldNames = super.getAllFieldNames();
fieldNames.addAll(getMeasureNames());
fieldNames.addAll(getDimAttributeNames());
fieldNames.addAll(getTimedDimensions());
return fieldNames;
}
public Set<CubeColumn> getAllFields() {
Set<CubeColumn> columns = Sets.newHashSet();
columns.addAll(getMeasures());
columns.addAll(getDimAttributes());
columns.addAll(getExpressions());
return columns;
}
/**
* @see org.apache.lens.cube.metadata.AbstractBaseTable
*/
@Override
protected String getJoinChainListPropKey(@NonNull String tblname) {
return MetastoreUtil.getCubeJoinChainListKey(getName());
}
public String getPartitionColumnOfTimeDim(@NonNull String timeDimName) {
String partCol = getProperties().get(MetastoreConstants.TIMEDIM_TO_PART_MAPPING_PFX + timeDimName);
return StringUtils.isNotBlank(partCol) ? partCol : timeDimName;
}
public String getTimeDimOfPartitionColumn(@NonNull String partCol) {
Map<String, String> properties = getProperties();
for (Map.Entry<String, String> entry : properties.entrySet()) {
if (entry.getKey().startsWith(MetastoreConstants.TIMEDIM_TO_PART_MAPPING_PFX)
&& entry.getValue().equalsIgnoreCase(partCol)) {
return entry.getKey().replace(MetastoreConstants.TIMEDIM_TO_PART_MAPPING_PFX, "");
}
}
return partCol;
}
@Override
public Date getAbsoluteStartTime() {
return MetastoreUtil.getDateFromProperty(this.getProperties().get(MetastoreConstants.CUBE_ABSOLUTE_START_TIME),
false, true);
}
@Override
public Date getRelativeStartTime() {
return MetastoreUtil.getDateFromProperty(this.getProperties().get(MetastoreConstants.CUBE_RELATIVE_START_TIME),
true, true);
}
@Override
public Date getStartTime() {
return Collections.max(Lists.newArrayList(getRelativeStartTime(), getAbsoluteStartTime()));
}
@Override
public Date getAbsoluteEndTime() {
return MetastoreUtil.getDateFromProperty(this.getProperties().get(MetastoreConstants.CUBE_ABSOLUTE_END_TIME),
false, false);
}
@Override
public Date getRelativeEndTime() {
return MetastoreUtil.getDateFromProperty(this.getProperties().get(MetastoreConstants.CUBE_RELATIVE_END_TIME),
true, false);
}
@Override
public Date getEndTime() {
return Collections.min(Lists.newArrayList(getRelativeEndTime(), getAbsoluteEndTime()));
}
}