[Blocks Explained] - Set and Extract Properties

Blocks Explained Series

Analytics Builder comes with dozens of built-in blocks for every kind of task. There are input and output blocks, but more importantly there is a whole zoo of blocks that are meant to build business logic:

  • calculations and aggregations,
  • logic combinations
  • and blocks that help you define and manipulate the general data flow.

This series tries to guide you through the jungle of blocks by providing helpful descriptions and examples on some of the most commonly used blocks and their typical use cases.

In this article, we are going to have a look at some of the most useful blocks for working with all kinds of data: the Set Properties and Extract Property blocks from the Utility section.


Overview Set and Extract Properties

Each input and output port on a block has a primary value represented by a type: float, boolean, string, or pulse (or any for allowing all of them).
This is the value that is sent/received on activations of a block and typically the piece of information relevant for the block logic.

But actually each of these values can hold additional information called properties on top of them. Properties are JSON-style dictionaries that allow you to put any helpful data into them that might be useful to other blocks.

This is most obvious on the Managed Object Input block. The output port is of simple type pulse and all the actual ManagedObject fragments sit piggyback on the pulse. But other blocks may produce properties on the back of their actual output value, as well.

Some blocks (most notably the Extract Property block) can read these additional properties and do something with them. In Cumulocity context this comes very handy for working with any kind of fragments on Managed Objects, Alarms, Events, Measurements, and Operations.

For extracting values out of this dictionary, the Extract Property block uses a JSONPath style format

Common Use Cases

Extracting fragments from Input blocks

The typical use case is getting information from a Managed Object. While the pulse indicates an update to the object, the relevant information for the block logic is usually in one or multiple fragments on the managed object.

Here is an example where we are using a status.maintenance field on the Managed Object to filter Measurements of that device and only create new ones when this field is set to true:

The Extract Property block is connected to the Managed Object Input and uses the JSONPath of the property. If the field doesn’t exist, it’s assumed to be false in this case.

Adding additional information to your Output blocks

Properties and Fragments also work the other way: you can send additional information on the Cumulocity objects with most of the built-in output blocks.

The Properties Input port of the output blocks takes an any value and just takes the properties on them and copies them over.

In this example, we are taking all of the fragments on the input Measurement and copying them over to the calculated output one:

If the Params Fragment parameter is set, a fragment with that name is added to the result and the properties become children of this. Otherwise, all properties are copied to the top level.

Similarly, the Set Properties block can be used to build these dictionaries of data.
To set fragments on a Managed Object, you can pass in a pulse with the properties you want on the input port like this:


The parameters of the Set Property block define how the keys of the dictionary are called:

The Property Name parameter of the Managed Object Output block is set to “statistics” in this case, which leads all the passed-in properties to become children of this fragment.

Text Templates for Messages and Logging

The Text Substitution takes properties on any input object to place them into a text template. That’s really useful for building dynamic text like when sending Events or Alerts or in combination with the new Logger block.

Take this example:

And the text can be set to something like
#{source.id} received a new measurement with offset #{measurement_spec.offset} at #{time} with value #{value}. Difference: #{delta}

Note that this also leverages shortcuts on the Extract Property and Set Properties blocks. If the Property Path is left empty on the Extract Property block and the Property Type is set to Properties, it will just pass through any Properties from its input.

Similarly, leaving out the Property Name on the Set Properties block will just copy all properties from that input to the output.

With this, measurement_spec.offset, time, and value are just passed through from the original Measurement without explicitly remapping them.

What to watch out for

Nested properties

Even though the Extract Property block allows for accessing nested structures via JSONPath, setting a nested property usually requires multiple blocks. You can chain Set Properties blocks to add the output of one as a property of the next one. Additionally, some blocks allow for “nesting” via the Property Name parameter.
This example sets a “measurements” fragment on a Managed Object:

Merging and Overwriting

The Set Properties block allows combining multiple properties into a single output.
Since properties work like a key/value map, a key can only exist once in the output. In case multiple inputs of the block contain the same key or one of the Property Name fields collides with any of the other input properties, only one of them will end up in the output.
The documentation of Set Properties explains how merging the different inputs works in those cases, i.e., which input properties would take precedence over the others.

This merging and overwriting functionality can be helpful to build complex, nested structures like mentioned above, to merge all information into a single dictionary, or to just update specific fields while keeping the others at their original values.
The last use case is extremely common with complex (read: nested) fragments on Managed Objects. You typically have to read the original values from the Managed Object, update the nested properties that you want to update, and write the whole top-level fragment back. So you merge the old fields with the new fields to give you the final fragment.

Null Values

There is currently no good way to remove a top-level fragment from a Cumulocity object (i.e., setting it to null via Analytics Builder). That is because there is no null equivalent in Analytics Builder. You might be able to fall back to default values (like 0 for float/integer) instead - but this depends on the use case.

However, for nested properties, the overwrite rules mentioned above apply. That is, you can remove a nested property by setting the top-level fragment and just leaving out the nested property that you want to remove, like so:

Array Support

The JSONPath of Extract Property allows reading out elements from a list like so:
persons[1].address[0].city
However, manipulating those is currently not possible - there just isn’t any array/list representation in the AB type model.
With this kind of static indexing, you also have to make sure the array elements exist to avoid any weird runtime behaviour. Iterating over such an array also is not straightforward. Maybe future blocks will change this.

Summary

The Extract Property and Set Properties blocks are great if you want to work with Cumulocity Fragments or if you need to structure data for logging and messages.

Want to read more? Check out other articles of this series Blocks Explained!

1 Like