Since Strolch has a generic model, it was rather straight forward to create a simple API for writing reports. In Strolch a report is defined by using its own model, i.e. a Report is a Resource of type Report.

A report consists of the following parts:

An example of a report is as follows:

<Resource Id="stockReport" Name="Stock Report" Type="Report">

  <ParameterBag Id="parameters" Name="parameters" Type="Parameters">
    <Parameter Id="objectType" Index="20" Hidden="false" Name="Object Type" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Player"/>
    <Parameter Id="descending" Name="Descending order" Type="Boolean" Value="true"/>
  </ParameterBag>

  <ParameterBag Id="ordering" Name="Ordering" Type="Ordering">
    <Parameter Id="name" Name="Name" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
  </ParameterBag>

  <ParameterBag Id="noTeamFilter" Name="Filter" Type="Filter">
    <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
    <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Slot" Value="Bags/relations/team"/>
  </ParameterBag>

  <ParameterBag Id="columns" Name="Display Columns" Type="Display">
    <Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>
    <Parameter Id="birthDate" Name="Birth date" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="Bags/parameters/birthDate"/>
    <Parameter Id="team" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="$name"/>
  </ParameterBag>

  <ParameterBag Id="joins" Name="Joins" Type="Joins">
    <Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
  </ParameterBag>

  <Policies>
    <Policy Type="ReportPolicy" Value="java:li.strolch.report.policy.GenericReport"/>
  </Policies>

</Resource>

This report

GenericReport

The default generic report implemented in Strolch has the following features and options:

Parameters

The parameters bag can contain the following parameters:

Lookups

Many of the features of the generic report rely on looking up a value on the referenced element. The following shows the ways that a lookup can be performed:

Note: these definitions are set as the value of a Parameter, and the Interpretation and UOM of the parameter is used to find the element on which to perform the lookup. I.e. the following definition:

<Parameter Id="name" Name="Player" Type="String" Interpretation="Resource-Ref" Uom="Player" Value="$name"/>

defines that we want to lookup the name of the resource of type Player.

Ordering

Ordering, i.e sorting is done by adding the parameter bag with the id ordering and each parameter defines a column to order by. The sequence of the ordering is defined by the index value assigned to each parameter.

Filtering

Filtering use additional Strolch Policies which implement the operator function. I.e. performing an equals, etc. The following ReportFilterPolicy are available and should be added in your StrolchPolicies.xml file:

<StrolchPolicies>
  ...
  <PolicyType Type="ReportFilterPolicy" Api="li.strolch.report.policy.ReportFilterPolicy">
    <Policy Key="GreaterThan" Class="li.strolch.report.policy.GreaterThanReportFilter" />
    <Policy Key="LessThan" Class="li.strolch.report.policy.LessThanReportFilter" />
    <Policy Key="Equals" Class="li.strolch.report.policy.EqualsReportFilter" />
  </PolicyType>
  ...
<StrolchPolicies>

From this we can see that we can perform a GreaterThan, LessThan and Equals filtering. These filters can also be negated by prefixing the filter value with an exclamation mark (!).

A special case for the filter values are filters on dates. If you are filtering on a date, then you can use the special operator now(). This filter will use the current date and time and will add/subtract the ISO8601 period passed as an argument to the operator.

The following shows examples of these filters:

<ParameterBag Id="minQtyFilter" Name="Filter" Type="Filter">
  <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:GreaterThan" Value="10"/>
  <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/quantity"/>
</ParameterBag>

<ParameterBag Id="notEmptyFilter" Name="Filter" Type="Filter">
  <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:Equals" Value="!"/>
  <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Bags/relations/team"/>
</ParameterBag>

<ParameterBag Id="threeMonthsAgoFilter" Name="Filter" Type="Filter">
  <Parameter Id="policy" Name="Filter Policy" Type="String" Interpretation="ReportFilterPolicy" Uom="key:LessThan" Value="now(-P3M)"/>
  <Parameter Id="fieldRef" Name="Field reference" Type="String" Interpretation="Resource-Ref" Uom="FromStock" Value="$date"/>
</ParameterBag>

Note: One parameter defines which policy gets used and the key:<name> value references a policy defined in the StrolchPolicies.xml file. Further the lookup is defined in the fieldRef parameter.

Joins

To add columns from data which is not on the element denoted by the base object type, we can join further elements. This is done by adding the parameter bag joins and then each parameter references an element to join. The joining is done as follows:

Thus the following:

<ParameterBag Id="joins" Name="Joins" Type="Joins">
  <Parameter Id="Team" Index="10" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Team" Value="Player"/>
  <Parameter Id="Country" Index="5" Hidden="false" Name="Team" Type="String" Interpretation="Resource-Ref" Uom="Country" Value="Team"/>
</ParameterBag>

Performs two joins: First we join a resource of type Team by finding the relevant parameter on the Player resource, and then we lookup a resource of type Country on the previously joined Team resource.

Execution of Reports

To execute a reports, we must instantiate the Report and can then directly generate a JsonObject stream, which we can then pipe to a browser, file, etc.:

Stream<JsonObject> jsonObjectStream = new Report(tx, reportId).doReportAsJson();

If you prefer a CSV report:

try (CSVPrinter csvP = new CSVPrinter(new OutputStreamWriter(out),
        CSVFormat.DEFAULT.withHeader(headers).withDelimiter(';'))) {

  // do report without AsJson, and then iterating each row and sending to a CSV writer
  report.doReport().forEach(row -> {
    try {
      csvP.printRecord(row.valueStream().collect(Collectors.toList())); // add to CSV
    } catch (Exception e) {
      logger.error("Could not write CSV row", e);
    }
  });
}

Filter Criteria

Predefining filters is a good start, but in some case you only want a portion of the actual filtered data. For instance if you make a stock report, you might only want one location. This information is dynamic and thus not stored on the report definition.

To perform these dynamic filterings, one would call the filter() method on the report, passing the type of element to be filtered, and to which element IDs to reduce the report data to. The following reduces the report to only return the rows with the product01 Product and location02 Location elements:

new Report(tx, "stockReport")
        .filter("Product", "product01")
        .filter("Location", "location02")
        .doReportAsJson()

It is possible to find the possible filter criteria dynamically using the generateFilterCriteria() method.

Date Range Filtering

The last option to filter dynamically is using a date range selector. Define the dateRangeSel lookup parameter, and then set the date range on the instantiated report:

<ParameterBag Id="parameters" Name="parameters" Type="Parameters">
    ...
    <Parameter Id="dateRangeSel" Name="Date Range Selector" Type="String" Interpretation="Resource-Ref" Uom="Product" Value="Bags/parameters/expirationDate"/>
    ...
</ParameterBag>

Date from = new Date(LocalDate.of(2016, 1, 1).toEpochDay() * 86400000);
Date to = new Date(LocalDate.of(2017, 1, 1).toEpochDay() * 86400000);
DateRange dateRange = new DateRange().from(from, true).to(to, false);
List<JsonObject> result = new Report(tx, "stockReport") //
    .filter("Product", "product01") //
    .dateRange(dateRange) //
    .doReportAsJson()

Note: See the GenericReportTest for examples.