Measure and hierarchy publication
Publishing a measure or hierarchy is the process through which the Copper context is made aware of its existence. At the end of the Copper calculations, all published measures and hierarchies are added to the cube description.
After a successful publication, a measure or hierarchy becomes available in the following calculations. It can be retrieved by using its name. The name of a measure is either user-defined, or automatically generated by Copper using the its main characteristics (type of measure, arithmetic operator, name of underlying measures, levels of aggregation, ...). The name of a hierarchy is user-defined.
Copper.sum("likes").publish(context); // measure "likes.SUM" becomes available
Copper.measure("likes.SUM")
.per(Copper.level("sender_id"))
.doNotAggregateAbove()
.as("likes.SUM per sender") // "likes.SUM per sender" overwrites
// "likes.SUM.per(sender).doNotAggregateAbove()"
.publish(context); // measure "likes.SUM per sender" becomes available
Copper.measure("likes.SUM per sender")
.multiply(Copper.constant(2))
.publish(context); // measure "λ(likes.SUM per sender)" becomes available
Copper.newSingleLevelHierarchy("bucket", "bucket", "Quarter")
.from(Copper.level("month")
.map((Integer m) -> "Q" + ((m - 1) / 3 + 1)))
.publish(context); // hierarchy and level "Quarter" become available
Copper.measure("likes.SUM")
.at(Copper.level("bucket", "bucket", "Quarter"), s -> "Q4")
.publish(context); // measure "likes.SUM.at(Quarter)" becomes available
For measures
When publishing a measure, all of the intermediary measures used in its definition will also be published. Unless specified so with the use of visible()
, those intermediary measures will be hidden in the UI but still available in queries.
Copper.sum("likes") // creates "likes.SUM"
.per(Copper.level("sender_id"))
.avg() // creates "likes.SUM.per(sender_id).avg()"
.as("myMeasure") // "myMeasure" overwrites "likes.SUM.per(sender_id).avg()"
.publish(context); // publishes "pnl.SUM" and "myMeasure"
Defining the exact same measure several times across the Copper calculations is not an issue, since Copper can detect when the same measure has already been published, and won't perform the same work twice. However, when the user tries to publish a measure which has the same name as an already published measure, an exception is thrown. If two measures have conflicting names and the second one has no user-set name, then Copper appends a "#" with an incrementing id to distinguish measures.
Copper.sum("likes") // creates "likes.SUM"
.plus(Copper.constant(1)) // creates "λ(likes.SUM)"
.publish(context); // publishes "pnl.SUM" and "λ(likes.SUM)"
Copper.sum("likes") // creates "likes.SUM"
.multiply(Copper.constant(2)) // creates "λ(likes.SUM)"
.publish(context);
// Does not publish "pnl.SUM" since the same measure already exists and is identical.
// Publishes "λ(likes.SUM)__#__1" since λ(pnl.SUM) already exists but there is no conflict
// since the name is not user-set (Copper is free to choose another name)
Copper.sum("likes") // creates "likes.SUM"
.map((Double d) -> d > 0) // creates "λ(likes.SUM)"
.as("λ(likes.SUM)") // forces Copper to use "λ(likes.SUM)" as this measure's name
.publish(context); // throws an exception because "λ(likes.SUM)" already exists