Skip to main content

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.

Getting started with DirectQuery

This tutorial requires access to the DirectQuery modules.
This tutorial will walk you through the basics of DirectQuery by creating an application on a external database. We’ll see how to:
  • Use the DirectQuery module in a project
  • Connect to the external database
  • Create a data model based on the external database
  • Use this data model in a Cube
This tutorial requires:
  • A external database
  • Familiarity with this database
  • Familiarity with the basic concepts of Atoti.

Building the external content

Let’s start by generating the content in the external database
Let’s create a new dataset for this tutorial
CREATE SCHEMA tutorial;
Then create the sales table and feed it with 20 facts
CREATE TABLE IF NOT EXISTS `tutorial`.SALES (
  SALE_ID STRING NOT NULL,
  DATE DATE NOT NULL,
  SHOP STRING NOT NULL,
  PRODUCT STRING NOT NULL,
  QUANTITY INT64 NOT NULL,
  UNIT_PRICE FLOAT64 NOT NULL
);
INSERT INTO `tutorial`.SALES VALUES
  ('S0010','2022-01-31','shop_2','BED_2',3,150),
  ('S0009','2022-01-31','shop_3','BED_2',1,150),
  ('S0008','2022-01-31','shop_4','BED_2',1,150),
  ('S0007','2022-02-01','shop_5','BED_2',1,150),
  ('S0019','2022-02-02','shop_3','HOO_5',1,48),
  ('S0018','2022-02-03','shop_3','HOO_5',1,48),
  ('S0017','2022-02-04','shop_3','HOO_5',1,48),
  ('S0000','2022-02-04','shop_0','TAB_0',1,210),
  ('S0001','2022-02-03','shop_1','TAB_0',1,210),
  ('S0002','2022-02-02','shop_2','TAB_0',1,210),
  ('S0003','2022-02-01','shop_3','TAB_0',1,210),
  ('S0004','2022-02-03','shop_1','TAB_1',1,300),
  ('S0005','2022-02-02','shop_2','TAB_1',1,300),
  ('S0006','2022-02-01','shop_4','TAB_1',2,300),
  ('S0013','2022-02-01','shop_4','TSH_3',1,22),
  ('S0012','2022-02-02','shop_5','TSH_3',1,22),
  ('S0011','2022-02-03','shop_5','TSH_3',1,22),
  ('S0016','2022-01-31','shop_1','TSH_4',2,24),
  ('S0015','2022-02-01','shop_2','TSH_4',2,24),
  ('S0014','2022-02-02','shop_4','TSH_4',1,24);
Let’s create and feed a PRODUCTS table containing information about the products
CREATE TABLE IF NOT EXISTS `tutorial`.PRODUCTS (
  PRODUCT_ID STRING NOT NULL,
  CATEGORY STRING NOT NULL,
  SUB_CATEGORY STRING NOT NULL,
  SIZE STRING NOT NULL,
  PURCHASE_PRICE FLOAT64 NOT NULL,
  COLOR STRING NOT NULL,
  BRAND STRING NOT NULL
);
INSERT INTO `tutorial`.PRODUCTS VALUES
  ('TAB_0','Furniture','Table','1m80',190,'black','Basic'),
  ('TAB_1','Furniture','Table','2m40',280,'white','Mega'),
  ('BED_2','Furniture','Bed','Single',127,'red','Mega'),
  ('TSH_3','Cloth','Tshirt','M',19,'brown','Over'),
  ('TSH_4','Cloth','Tshirt','L',20,'black','Over'),
  ('HOO_5','Cloth','Hoodie','M',38,'red','Mega');

Importing DirectQuery modules

Additionally to classic Atoti modules you will need to import two modules:
  • One generic module to interact with external databases
<dependency>
    <groupId>com.activeviam.activepivot</groupId>
    <artifactId>activepivot-external-database</artifactId>
    <version>${project.version}</version>
</dependency>
<dependency>
    <groupId>com.activeviam.directquery</groupId>
    <artifactId>api-application</artifactId>
    <version>${project.version}</version>
</dependency>
  • One to connect to your specific database
<dependency>
    <groupId>com.activeviam.directquery</groupId>
    <artifactId>api-bigquery</artifactId>
    <version>${project.version}</version>
</dependency>

Using the external database in DirectQuery

Create the database connector

Let’s start by creating a connection to BigQuery.
In this example we use a service account and have set an environment variable.
For more details and options about authentication on BigQuery we recommend to follow the official documentation and read the Connect to BigQuery page.
final BigQuery bigquery =
    BigQueryOptions.newBuilder().setProjectId(PROJECT_ID).build().getService();
This connection can then be used to create a DirectQuery connector.
final DirectQueryConnector<BigqueryDatabaseSettings> connector =
    BigqueryConnectorFactory.INSTANCE.createConnector(
        BigqueryClientSettings.builder().bigQuery(bigquery).build());

Add external tables into DirectQuery

The second step is to create the external database schema description.
We start by building the table definition. This can be done either with the help of an external database discoverer or by manually building the description of the tables.
Let’s first create a discoverer from the connector. Note that there are settings for the discoverer to customize the behavior but in this example we will use the default ones.
final IDirectQueryTableDiscoverer discoverer = connector.getDiscoverer();
Now we want to create the tables for our application, by discovering their structure on the external database.
final TableDescription salesTable =
    discoverer.discoverTable(new SqlTableId(PROJECT_ID, "tutorial", "SALES"));
final TableDescription productsTable =
    discoverer.discoverTable(new SqlTableId(PROJECT_ID, "tutorial", "PRODUCTS"));
For more information about creating table manually without discovery read the Create tables page.

Create a join between tables

Then we create a join between the tables of our example.
Sales has a many-to-one relationship with Products on the field product_id.
final JoinDescription join =
    JoinDescription.builder()
        .name("SALES_TO_PRODUCTS")
        .sourceTableName("SALES")
        .targetTableName("PRODUCTS")
        .fieldMappings(Set.of(new FieldMapping("PRODUCT", "PRODUCT_ID")))
        .build();

Relationship optionality

When defining a join you can set the Relation Optionality.
The default relationship is Optional, it means that some rows in Sales table could have a product which is missing from the Products table.
You can set this relationship to Mandatory, if you are sure that all products used in Sales table are in Products table.
Mandatory relationship could lead to better performance in some connector features like Incremental refresh.

Create the schema

Combining tables and join definitions, we can now build our schema description.
final SchemaDescription schema =
    SchemaDescription.builder()
        .externalTables(List.of(salesTable, productsTable))
        .externalJoins(List.of(join))
        .build();

Building an app on the external database

We can now build an Atoti cube manager description as usual.
final ISelectionDescription selection =
    StartBuilding.selection(schema).fromBaseStore("SALES").withAllReachableFields().build();
final IActivePivotInstanceDescription cubeDescription =
    StartBuilding.cube("MyCube")
        .withMeasures(
            superMeasureBuilder ->
                superMeasureBuilder.withCalculations(
                    c -> {
                      // You can use the database fields to define aggregations
                      Copper.sum("QUANTITY").as("QUANTITY.SUM").publish(c);
                      Copper.max("UNIT_PRICE").as("Max price").publish(c);
                    }))
        .withDimensions(
            builder ->
                builder
                    .withSingleLevelDimensions(
                        List.of("DATE", "SHOP", "PRODUCT", "SIZE", "BRAND"))
                    .withDimension("CATEGORY")
                    .withHierarchyOfSameName()
                    .withLevels("CATEGORY", "SUB_CATEGORY"))
        .build();
final IActivePivotManagerDescription managerDescription =
    StartBuilding.managerDescription()
        .withName("MyManager")
        .withSchema()
        .withSelection(selection)
        .withCube(cubeDescription)
        .build();
Then create the DirectQuery application.
final Application app =
    Application.builder(connector)
        .schema(schema)
        .managerDescription(managerDescription)
        .build();
app.start();
That’s it, the cube is built and you can run queries on it.
final IActivePivotManager manager = app.getManager();
final IActivePivotVersion pivot =
    manager.getActivePivot("MyCube").getHead(IEpoch.MASTER_BRANCH_NAME);
final ICellSet result =
    IActivePivotQueryRunner.create()
        .withWildcardCoordinate(LevelIdentifier.simple("SHOP"))
        .forMeasures("QUANTITY.SUM")
        .run(pivot);
GetAggregatesResultCellset.check(pivot, result).printCellSet();