What is the value field in tsDecl simple metrics used for?

    “id”: “MetricName_TypeName”,
    “name”: “MetricName”,
    “srcType”: TypeName, 
    “description”: “test description”,
    “path”: “same.as.simpleMetricPaths”,
    “tsDecl”: {
	!”data”: “expressionEquivalent”,
	!“start”: “when the event starts”,
	”end”: “when the event ends”,
	!“treatment”: “c3ShowType(Treatment) for options”,
	“filter”: ”condition on the data points”,
	“value”: “what is this field used for?”


I think the value given to the data point, it can be any expression e.g. :

"tsDecl": {
  "data": "orders",
  "value": "product.id == 'some-id'? quantity: 0"


Why do we prefer to put some fields in value and some in data?


In the documentation of TSDecl, `value’ is described as:

value expression on the type which is at the end of the path + data (optional)

Which means for the example above, we will take the quantity field inside each order if the product.id does not match the given id.
I don’t think you can implement this with only using the data field.


The difference between a standard simple metric and a tsDecl simple metric is that the normalization process for the standard metric is defined on the time series that it is referring to, where as a tsDecl metric is defining the normalization process in the metric, and a time series is created at run-time.

In a standard metric, your ‘path’ field should traverse from the SourceType to the TimeseriesHeader. From there, the expression references the fields on the TimeseriesHeader, normally in the form of ‘normalized.data.value’

In a tsDecl metric, the path should lead you from your SourceType to the relational Type that has a direct reference to the CollectionType (TimedValueHistory, TimedCharacteristicHistory, etc.). Within the tsDecl object in the Metric definition: The data field should be the field that holds an array of the CollectionType, and then the value field can hold either some logic as Bachir indicated above; or it can directly take its value from a field on the CollectionType; or it can be left undefined, or empty, if the treatment selected already determines the value to be returned, for example, the COUNT treatment.

This difference exists because in a standard metric, there is Type (often TimeseriesHeader) between the data points and the SourceType. For example, from the SourceType, you generally have a field called ‘measurements’ that references an array of TimeSeriesHeaders. However, the array of CollectionTypes exists directly on the SourceType, so the TSDecl.data field should lead us to that collection that we are going to be creating a normalized time series from.


How is normalization controlled on a tsDecl metric? Should I use @ts(normalizer...) on something or try something else?


There is no normalization on a tsDecl metric. the tsDecl replaces the normalization. You are basically telling what data to use, what start (potentially end) to use, what treatment to use, …

While it is more flexible and you can experiment quicker it will also be slower because normalization pre-computes values. So if you can normalize the data I would recommend using an expression and a ts annotation.


Thanks! Can you explain it a bit more on the example below? (I added dataVersion as explained by @mohamad.arabi, not tested yet.)

  "id": "KPCS_Facility",
  "name": "KPCS",
  "description": "KPCS evolution",
  "tsDecl": {
    "data": "characteristics",
    "dataVersion": "floor(toMillis(meta.created) / 1e6) * 1e6",
    "filter": "name==ServicePointCharacteristicKey.KPCS",
    "value": "number(value)",
    "treatment": "PREVIOUS",
    "start": "timestamp"
  "srcType": "Facility",
  "unit": {
    "id": "kwh-per-m3"

If I evalMetric the above, I get normalized data, but I do not see how.
I guess the interval of evalMetric spec is used for “normalization”.
I hope I can use the above filter to ignore some data based on their dataVersion.

Is it possible to use some built-in functions? (I found toMillis in ExpressionEngineFunction but what functions can I use in which fields?)


Yes the interval of the evalMetric is used for normalization.
You can use the filter to filter out some data points on “data” and you can leverage ExpressionEngineFunction.
You can also use expressions on start, end, value, dataVersion, isEstimated, transform.

The other fields have specific enums. Check out the documentation on c3ShowType(TSDecl).


I can’t find dataVersion in ExpressionEngineFunction, but I will try anyway.


No I meant there is a field in tsDecl called dataVersion and you can use an expression to evaluate the data version of that point.