> ## Documentation Index
> Fetch the complete documentation index at: https://docs.activeviam.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Cube Configuration

## Introduction

To organize and structure data, Atoti is using OLAP cubes with hierarchies and measures.

* The [hierarchies](./hierarchy_configuration) defined the structure of the data.
* The analysis is done
  accessing [measures for different locations](../concepts/concepts_in_a_nutshell#defining-a-cube).

If the application is complex and cover several use cases, it is possible to define several **cubes** in the same
application. We organize them using catalogs and schemas:

* A **catalog** is a collection of cubes.
* A **schema** is a collection of cubes that have the same selection on the underlying database. It means that the cubes
  inside a same schema are based on the same data.

The **manager** will orchestrate catalogs and schemas. This is the highest entity.

All these objects can be described using the `StartBuilding` fluent builder.

# Create selection, managers, catalogs and schemas

## Create a selection

To create a selection, use `StartBuilding.selection()`:

* `fromBaseStore("storeName")` defines the base store
* `withAllReachableFields()` adds all the fields reachable from the base store via the references in the database.
* `withField("fieldName")` adds one field to the selection
* `withAlias("fieldName", "alias")` defines a new name for a field

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final ISelectionDescription selection =
    StartBuilding.selection(schemaDescription)
        .fromBaseStore("store")
        .withAllReachableFields()
        .build();
```

Adding fields individually:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final ISelectionDescription fieldByFieldSelection =
    StartBuilding.selection()
        .fromBaseStore("store")
        .withField("field1")
        .withField("field2")
        .build();
```

## Create managers, catalogs and schemas

To create a selection, use `StartBuilding.manager()`:

* `withSchema("schemaName")` defines a schema. The name is optional if there is only one schema. The default name is "
  Schema".
* `withSelection(selection)` adds the selection created earlier
* `withCube(cubeDescription)` adds a cube description. How to create a cube description is described below.
* `withDistributedCube(cubeDescription)` adds a distributed cube description.
* `withCatalog("catalogName)` groups the cubes into catalogs. This is optional if there is only one catalog. The default
  name of catalog is "Catalog".

Here is a simple example:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final IActivePivotManagerDescription desc =
    StartBuilding.managerDescription()
        .withSchema()
        .withSelection(selection)
        .withCube("Cube", b -> b.withSingleLevelDimension("field"))
        .build();
```

Here is a more complex example:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final IActivePivotManagerDescription desc =
    StartBuilding.managerDescription()
        .withSchema("A")
        .withSelection(selection)
        .withCube("C", b -> b.withSingleLevelDimension("f"))
        .withSchema("B")
        .withSelection(StartBuilding.selection().fromBaseStore("a").withField("f").build())
        .withCube("C2", b -> b.withSingleLevelDimension("f"))
        .withCatalog("C")
        .containingCubes("C")
        .withCatalog("Main")
        .containingAllCubes()
        .build();
```

It is possible to give the manager a name to be able to create several. The default name is "Manager".

<Info>
  The catalogs names are displayed in the UI (as opposed to the managers names and schemas names).
</Info>

# Cube Configuration

To create a cube, use `StartBuilding.cube("cubeName")`.

## Create a minimal cube

The minimal cube only has a single level (so a single hierarchy and a single dimension) and only the native measures (
the contributors count and a timestamp).

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final IActivePivotInstanceDescription desc =
    StartBuilding.cube().withName("Cube").withSingleLevelDimension("level").build();
```

## Add measures and dimensions

To add dimensions, [hierarchies](hierarchy_configuration) and [levels](./level_configuration) please see the
dedicated articles.

To add measures, the preferred way is [Copper](../copper/copper_intro).

To add simple aggregated measures, use `withAggregatedMeasure()`:

* choose a common aggregation function with `sum("fieldName")`, `min("fieldName")`, `max("fieldName")`,
  `avg("fieldName")`
* call the aggregation function plugin key otherwise with `withAggregateFunctionFunction(pluginKey)`
* optional: choose a folder to visually organize the measure in with `withinFolder("folderName")`
* optional: choose a formatter with `withFormatter("formatter")`

Here is an example that defines several measures:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
final Function<ICanStartBuildingMeasures, IHasAtLeastOneMeasure> measures =
    b ->
        b.withContributorsCount()
            .withFormatter("INT[#,###]")
            .withAggregatedMeasure()
            .sum("pnl")
            .withinFolder("pnl")
            .withFormatter("DOUBLE[#,###.00;-#,###.00]")
            .withAggregatedMeasure()
            .sum("delta")
            .withinFolder("sensitivities")
            .withFormatter("DOUBLE[#,###.00;-#,###.00]");
StartBuilding.cube()
    .withName("MyCube")
    .withMeasures(measures)
    .withDimensions(dimensions)
    .build();
```

## Optional cube configurations

### Configure the aggregate provider

To add an [aggregate provider](hierarchy_configuration), please see the dedicated article.

### Configure the aggregate cache

Queries can be served from an [aggregate cache](aggregate_cache) to avoid recomputing
aggregates that have already been requested by previous queries. The cache is configured per cube,
with options to control its size, the measures it caches, and the branches it covers.

Use `withAggregatesCache` to configure the aggregate cache:

* `withSize()` defines the cache size (number of `(location, measure)` pairs)
* `cachingOnlyMeasures("measure1", "measure2")` lists the measures to keep in the cache

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
StartBuilding.cube()
    .withName("MyCube")
    .withMeasures(measures)
    .withDimensions(dimensions)
    .withAggregateProvider()
    .jit()
    .withAggregatesCache()
    .withSize(10_000)
    .cachingOnlyMeasures("contributors.COUNT", "pnl.SUM")
    .build();
```

See the [aggregate cache](aggregate_cache) page for the full set of options,
performance considerations, and best practices.

### Configure the shared context values

The cube description can also define context values for all queries of all users.

Use `withSharedContextValue` to define shared context value:

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
StartBuilding.cube()
    .withName("MyCube")
    .withMeasures(measures)
    .withDimensions(dimensions)
    .withSharedContextValue(
        new DrillthroughProperties(
            new CustomComparator<>(List.of("Desk", "Currency"), null),
            List.of("BookId", "City"),
            List.of(
                new CalculatedDrillthroughColumnDescription(
                    "DoubleAdder", "delta + gamma", "delta,gamma")),
            List.of(
                new CalculatedDrillthroughColumnSetDescription(
                    "PnlCurrencyColumnSet",
                    Immutable.map("prefix", "pnl in", "currencies", "EUR,USD,GBP,JPY,CHF,ZAR")
                        .toProperties()))))
    .withSharedContextValue(new MdxContext())
    .build();
```

### Add a global fact filter

The cube retrieves its data from the database using the selection. It is possible to filter some of the data using a
fact filter.

The filter is applied when feeding the cube, on the facts in the selection, before any queries happening.

The cube retrieves data from the database based on the defined selection. A fact filter can be applied to limit the facts available to the cube. This filter ensures that all queries and updates on the cube respect the specified condition. Any operation performed on the cube, including queries and updates, is constrained by the fact filter. To create a fact filter, use withFactFilter(condition) and define the filtering condition.

```java theme={"languages":{"custom":["/engine/python-sdk/0.9/languages/pycon.tmLanguage.json"]}}
StartBuilding.cube()
    .withName("MyCube")
    .withMeasures(measures)
    .withDimensions(dimensions)
    .withFactFilter(FactFilterConditions.eq("field", "value"))
    .build();
```
