Cube builder

Data Cube

To set up a data cube, you can override the ADataCubeConfigurer class.

Query cube

To set up a query cube, you can override the AQueryCubeConfigurer class.

Methods to override

ADataCubeConfigurer constructor

The constructor of the class gets the dimensions and the aggregate provider configuration of the cube.

    AggregateProviderConfig mdConfiguration = builder -> builder
            .withAggregateProvider()
            .bitmap()
            .withModuloPartitioning(StoreFieldConstants.RISK_FACTOR, NUM_HASH_PARTITIONS)
            .withAggregatesCache().withSize(aggregateCacheProperties.getSize());

    DimensionsAdder aDimension = builder -> builder
        .withDimension(CURRENCIES_DIM)
                    .withHierarchy(CURRENCY_HIERARCHY)
                            .withLevel(CCY_LVL);

    HierarchyBuilderConsumer aHierarchy = hierarchyBuilder ->
            hierarchyBuilder.toDimension(MARKET_DATA_DIMENSION, builder ->
                    builder
                            .withHierarchy(MARKET_DATA_SETS_HIERARCHY)
                            .slicing()
                            .fromStore(MARKET_DATA_SETS_STORE_NAME)
                            .withLevel(MARKET_DATA_SET_LEVEL)
                            .withFieldName(MARKET_DATA_SET));

    List<?> dimensions = List.of(aDimension, aHierarchy);

    var cube = new ADataCubeConfigurer(mdConfiguration, dimensions) {
        // ...    
    };

AQueryCubeConfigurer constructor

Parameters:

  • QueryCubeAggregateProviderConfig configuration The injection of the bean that contains the performance-related elements of the cube cache.
  • boolean appMeasureGroup Enables or disables the application measure groups. If enabled, all measures and hierarchies from a data node are grouped in a measure group (whose name is the application id of the DATA node they come from).
  • boolean appMeasureName Enables or disables the application measure names. If set to true, measures names coming from the data cubes are suffixed in the query cube with the application id of the DATA cube they come from.

copperMeasures

Used to define the Copper Measures of the cube:

@Override public void copperMeasures(ICopperContext context) {
    Copper.sum("v0").as("v0").publish(context);
}

getKpis

Sets the KPIs of the cube:

@Override public Collection<IKpiDescription> getKpis() {
    IKpiDescription kpi = StartBuilding.kpi()
            .withName("VAR 99% Monitoring")
            .withValue(new KpiValueDescription("[Measures].[Exception 99 Count]"))
            .withGoal(new KpiGoalDescription("12"))
            .withStatus(new KpiStatusDescription(
                    """
                            Case
                            When IsEmpty(KpiGoal("VAR 99% Monitoring"))
                            Then NULL
                            When KpiValue("VAR 99% Monitoring")  > KpiGoal("VAR 99% Monitoring")
                            Then -1
                            Else 1
                            End""",
                    "Shapes"))
            .build();
    return List.of(kpi);
}

schemaSelectionDescription

Defines the selection of the cube.

@Override public ISelectionDescription schemaSelectionDescription(IDatastoreSchemaDescription datastoreSchemaDescription) {
    return StartBuilding.selection(datastoreDescription.asDatabaseSchema())
            .fromBaseStore(BASE_STORE_NAME)
            .withAllFields()
            .build();
}

sharedContextValues

Defines the context values set by default in the cube.

@Override public <T> ICanBuildCommonCubeDescription<T> sharedContextValues(ICanBuildCommonCubeDescription<T> builder) {
    builder = builder.withSharedContextValue(new VaRConfidenceLevel(99));
    return builder;
}

measures

Defines measures in the old way. This is deprecated and should be replaced by Copper measures.

@Override
public IHasAtLeastOneMeasure measures(ICanStartBuildingMeasures builder) {
    for (IMeasureBuilder measureBuilder : measureBuilders) {
        for (IPostProcessorDescription ppd : measureBuilder.getPostProcessors()) {
            builder = builder.withPostProcessor(ppd);
        }
    }
    return (IHasAtLeastOneMeasure) builder;
}

mdxContext

Sets some specifics related to the MDX engine for this cube:

    @Override
    public IMdxContext mdxContext(ICanStartBuildingMdxContext.IMdxContextBuilder<IMdxContext> mdxBuilder) {
        return mdxBuilder
                .withDefaultMember()
                .onHierarchy("[PnL].[Types]")
                .withMemberPath("[Type].[Actual PL Attributed]")
                .build();
    }

cubeName

Sets the cube name

@Override
public String cubeName() {
    return "Sensitivity Cube";
}

schemaName

Sets the schema name

@Override
public String schemaName() {
    return "SensiSchema";
}

applicationId

Sets the application id, used to declare a data cube type on the cluster:

@Override public String applicationId() {
    return "Sensi";
}

getDistributedApps

On the query cube, you need to register all the cube application ids with their distribution key:

@Override private Map<String, String[]> getDistributedApps() {
    String[] DISTRIBUTION_FIELD = new String[] { AS_OF_DATE };
    Map<String, String[]> applications = new HashMap<>();
    applications.put("Sensi", DISTRIBUTION_FIELD);
    applications.put("VaR", DISTRIBUTION_FIELD);
    return applications;
}

Methods to expose

To grab all cube setups and create an Atoti application, you need to expose some beans from the cube configurer.

cubeName

To create the cubes catalog, you must expose all the cube names as beans:

@Bean("sensiCubeName")
@Qualifier("Risk_CubeName")
@Order(20)
@Override
public String cubeName() {
    return CUBE_NAME;
}

buildCube

And finally the most important bean to expose, the CubeBuilderFunction that provides the lambda used to construct the cube itself:

@Bean("buildSensiCube")
@Qualifier("Cube")
@Override
public CubeBuilderFunction buildCube(@Qualifier(SP_QUALIFIER__DATA_CUBE_MESSENGER_DEFINITION) IMessengerDefinition dataCubeMessengerDefinition) {
    return super.buildCube(dataCubeMessengerDefinition);
}

Additional Spring beans needed to build an application

The Catalog

The catalog contains all the cube names, it will grab all the cubeName beans to be constructed:

@Bean
@Qualifier("Catalog")
@Order(10)
public Function<IActivePivotManagerDescriptionBuilder, IActivePivotManagerDescriptionBuilder> buildRiskCatalog(@Qualifier("Risk_CubeName") List<String> cubeNames) {
    return builder -> builder.withCatalog("MarketRisk").containingCubes(cubeNames.toArray(String[]::new));
}

The application builder

Here is the configuration bean that will grab all the cubes and set up an application:

@Configuration
public class MarketRiskManagerConfig implements IDatastoreSchemaDescriptionConfig, IActivePivotManagerDescriptionConfig {

    protected final List<Function<IActivePivotManagerDescriptionBuilder, IActivePivotManagerDescriptionBuilder>> catalogs;

    protected final List<CubeBuilderFunction> cubes;

    protected final IDatastoreConfigurator datastoreConfigurator;

    protected final Collection<Collection<Set<StoreField>>> extraSameDictionaryDescriptions;

    public static final String MANAGER_NAME = "MarketRiskManager";

    public MarketRiskManagerConfig(
            @Qualifier("Catalog") List<Function<IActivePivotManagerDescriptionBuilder, IActivePivotManagerDescriptionBuilder>> catalogs,
            @Qualifier("Cube") List<CubeBuilderFunction> cubes,
            IDatastoreConfigurator datastoreConfigurator,
            @Qualifier("sameDictionaryDescription") @Autowired(required = false) Collection<Collection<Set<StoreField>>> extraSameDictionaryDescriptions) {
        this.catalogs = catalogs;
        this.cubes = cubes;
        this.datastoreConfigurator = datastoreConfigurator;
        this.extraSameDictionaryDescriptions = extraSameDictionaryDescriptions;
    }

    @Override
    @Bean
    public IActivePivotManagerDescription managerDescription() {
        IActivePivotManagerDescriptionBuilder builder = StartBuilding.managerDescription(MANAGER_NAME);

        builder = catalogs.stream().reduce((a, b) -> b1 -> b.apply(a.apply(b1))).orElse(o -> o).apply(builder);

        BuildableActivePivotSchemaDescriptionBuilder builder2 = cubes
                .stream()
                .reduce((a, b) -> (b1, d1) -> a.apply(b.apply(b1, d1), d1))
                .orElseThrow(() -> new ActiveViamRuntimeException("No cube"))
                .apply(builder, datastoreSchemaDescription());

        return builder2.build();
    }

    @Override
    @Bean
    public IDatastoreSchemaDescription datastoreSchemaDescription() {
        var datastoreSchemaDescription = datastoreConfigurator.buildSchemaDescription();
        if(extraSameDictionaryDescriptions != null) {
            var sameDictionaryDescriptions = new ArrayList<>(datastoreSchemaDescription.getDictionaryGroups());
            extraSameDictionaryDescriptions.forEach(sameDictionaryDescriptions::addAll);
            return new DatastoreSchemaDescription(datastoreSchemaDescription.getStoreDescriptions(), datastoreSchemaDescription.getReferenceDescriptions(), sameDictionaryDescriptions);
        } else {
            return datastoreSchemaDescription;
        }
    }
}