# Create custom metrics

Pre-computed metrics generated by the [Metrics Engine](https://docs.fractalworks.io/joule/components/analytics/metrics-engine) can be used within a custom processing component. For example, events could be filtered by user-defined metrics using time intervals, scoring models use metrics as part of the input feature space or build KPIs that combine metrics with event data.

## Development steps

1. Register query
2. Get metrics

## Explaining each step <a href="#explaining-each-step" id="explaining-each-step"></a>

### Step 1: Register query

All queries to be executed must be registered, this reduces the overhead of recreating the target SQL query on each function call. The interface elegantly handles duplicate queries by providing the same query token in the form of a UUID.

```java
UUID registerMetricQuery(String metricFamily, String[] metrics, String predicate)
    throws SQLException
```

<table><thead><tr><th width="155.33333333333331">Attribute</th><th width="264">Description</th><th>Example</th></tr></thead><tbody><tr><td><em>metricFamily</em></td><td>The metric family this query relates to and belongs to. </td><td><pre class="language-java"><code class="lang-java">"nasdaq_metrics"
</code></pre></td></tr><tr><td><em>metrics</em></td><td>An array of metric names that match the metric table columns.</td><td><pre class="language-java"><code class="lang-java">new String[]{
"avg_bid_min",
"avg_bid_avg",
"avg_bid_max"}
</code></pre></td></tr><tr><td><em>predicate</em></td><td>A SQL where predicate statement filters required metrics.</td><td><pre class="language-java"><code class="lang-java">"WHERE symbol=?"
</code></pre></td></tr></tbody></table>

#### Example

```java
// Get an instance of the interface
MetricQueryInterface metricQueryInterface = MetricQueryInterface.getInstance();

// Register the query
var uuidQueryToken = metricQueryInterface.registerMetricQuery(
                                "nasdaq_metrics",
                                new String[]{"avg_bid_min","avg_bid_avg","avg_bid_max"},
                                "WHERE symbol=?"
                                );
```

The `queryToken` returned is used as a parameter on the `query` method. Therefore it is best to be cached as a object class variable.&#x20;

### Step 2 Get metrics

This API provides the ability to query the target metric family for a set of pre-computed metrics. The function returns an Optional object type that is either empty or with a map of metrics with corresponding values.

```java
Optional<Map<String,Object>> query(UUID queryToken, Object[] params) 
        throws SQLException, StreamsException
```

<table><thead><tr><th width="137.33333333333331">Attribute</th><th>Description</th><th>Example</th></tr></thead><tbody><tr><td><em>queryToken</em></td><td>The token provided from the query registration process</td><td><pre class="language-java"><code class="lang-java">uuidQueryToken
</code></pre></td></tr><tr><td><em>params</em></td><td>An array of parameters that match the query predicate definition</td><td><pre class="language-java"><code class="lang-java">Object[]{"MSFT")}
</code></pre></td></tr></tbody></table>

#### Example

<pre class="language-java"><code class="lang-java">@Override
public StreamEvent apply(StreamEvent streamEvent, Context context) throws StreamsException {
    metrics.incrementMetric(Metric.RECEIVED);
    if(enabled){
        // TODO: Add processing logic
        var symbol = event.getValue("symbol");
        var spread = calculateSpread(symbol);
        
        // Super simple return value. You could send an indicator to trigger 
        // when spread widens over a threshold for a specific symbol  
        streamEvent.addValue(uuid, "spread", spread);
        metrics.incrementMetric(Metric.PROCESSED);

    } else {
        metrics.incrementMetric(Metric.IGNORED);
    }
    return streamEvent;
}

<strong>public double calculateSpread(final String parameter){
</strong>    MetricQueryInterface metricQueryInterface = MetricQueryInterface.getInstance();    
    Optional&#x3C;Map&#x3C;String, Object>> results =  metricQueryInterface.query(
            uuidQueryToken,
            new Object[]{parameter});
    
    var spread = Double.NaN;
    if (!results.isEmpty()) {
        var metrics = results.get();
        if( results.isPresent()){
          spread = results.get().get("avg_bid_max") - results.get().get("avg_bid_min");
        }
    }
    return spread;
}
</code></pre>
