Creating User Defined Analytics (UDAs)
Introduction
User Defined Analytics (UDAs) enable you to define new APIs that are callable through the Service Gateway (SG). UDAs augment the standard set of APIs available in the kdb Insights system with application logic specific to your business needs.
A UDA consists of a query
function that reads data from a Data Access Process and optionally an aggregation
function that combines the partials
from the query
function to produce the final result, which is then returned. These functions can be loaded into the Data Access Processes (DAPs) and the Aggregators in kdb Insights using packages
.
Steps to create a User Defined Analytic (UDA)
This section provides an example UDA to guide you through the steps required to create the code necessary to make it callable using the kdb Insights Service Gateway, once it is added to a package and deployed.
The .exampleuda.countBy
UDA counts the number of records per unique values of specified columns for a chosen table and time range.
.exampleuda.countBy
code
// Custom count by UDA.
// Define query function that selects specified columns for aggregation.
.exampleuda.countByQuery:{[table;byCols;startTS;endTS;byCols]
byCols,:();
data:.kxi.selectTable`table`startTS`endTS`agg!(table;startTS;endTS;byCols!byCols);
.kxi.response.ok(byCols;data)
}
// Define aggregation function that counts number of entries by specified columns.
.exampleuda.countByAgg:{[tbls]
t:raze last each tbls;
bc:first first tbls;
res:?[t;();bc!bc;enlist[`cnt]!enlist(count;`i)];
.kxi.response.ok res
}
// Define metadata.
metadata:.kxi.metaDescription["Custom UDA - does a count by."],
.kxi.metaMisc[enlist[`safe]!enlist 1b],
.kxi.metaParam[`name`type`isReq`description!(`table;-11h;1b;"Table name.")],
.kxi.metaParam[`name`type`isReq`description!(`byCols;11 -11h;1b;"Column(s) to count by.")],
.kxi.metaParam[`name`type`isReq`description!(`startTS;-12h;1b;"Start time (inclusive).")],
.kxi.metaParam[`name`type`isReq`description!(`endTS;-12h;1b;"End time (exclusive).")],
.kxi.metaReturn`type`description!(98h;"Count by specified columns.");
// Registration.
.kxi.registerUDA `name`query`aggregation`metadata!(`.exampleuda.countBy;`.exampleuda.countByQuery;`.exampleuda.countByAgg;metadata);
It is recommended to keep the function definitions and registration in the same file to ensure consistent definitions across DAPs and Aggregators. For additional information, refer to the UDA examples.
1. Define the Query Function
The query
function reads data from a Data Access Process and returns a table of data.
-
Create a file and add the following code:
// Define query function that selects specified columns for aggregation. .exampleuda.countByQuery:{[table;byCols;startTS;endTS;byCols] byCols,:(); data:.kxi.selectTable`table`startTS`endTS`agg!(table;startTS;endTS;byCols!byCols); .kxi.response.ok(byCols;data) }
It is highly recommended that you use the helper functions when retrieving data from the DAPs.
The countByQuery
function utilizes the .kxi.selectTable
helper function to collect the required data from the specified table and time range.
In the example the arguments are a list of values. You can define the arguments as either:
-
A list of values.
For example:
.example.queryFn:{[table;startTS;endTS;columns] columns:$[-11h = type columns;enlist columns;columns]; data:.kxi.selectTable`table`startTS`endTS`agg!(table;startTS;endTS;columns!columns); // Retrieve data within specified time range .kxi.response.ok data };
-
A dictionary of keys named
args
.Warning
You must name the dictionary of keys
args
. Any other name causes the DAP to treat the function as a one parameter function, which ignores the auto-casting of REST parameters.For example:
.example.queryFn:{[args] columns:$[-11h = type columns:args`columns;enlist columns;columns]; filter:enlist (<;`i;100); // Note for partitioned tables, will return first 100 per date .kxi.response.ok ?[args`table;filter;0b;columns!columns] };
Note
You must wrap the response with the helper function .kxi.response.ok
to indicate successful execution. For examples of the required response shape, refer to the generating a response header section.
If an aggregation
function is not defined, the query
function must return a table for the default operator, kdb+ raze
to successfully combine the partials
.
2. Define the Aggregation Function
The aggregation
function customizes aggregation based on the specific needs of the UDA. It combines a list of results, referred to as partials
, obtained by executing the query
function on each DAP.
-
Add the aggregation function to the file created in the previous step:
// Define aggregation function that counts number of entries by specified columns. .exampleuda.countByAgg:{[tbls] t:raze last each tbls; bc:first first tbls; res:?[t;();bc!bc;enlist[`cnt]!enlist(count;`i)]; .kxi.response.ok res }
The startTS
and endTS
parameters of the query
function direct the query to specific tiers. The .exampleuda.countByAgg
function receives a table from each tier and aggregates the counts per column across the tables returned by countByQuery
.
If no aggregation
function is defined, the kdb+ raze
operator is used.
-
The
query
function must return a table for theraze
operator to successfully combine thepartials
. -
With memory mapped table types such as
basic
orsplayed
, ensure that the query function includestable
as a parameter. This ensures correct routing to a single DAP for data requests and prevents duplicated results in the response. For more information, refer to the aggregation examples.
3. Add Metadata
Define metadata for the UDA to describe its purpose, parameters, and return values:
- Add the metadata to the file:
// Define metadata.
metadata:.kxi.metaDescription["Custom UDA - does a count by."],
.kxi.metaMisc[enlist[`safe]!enlist 1b],
.kxi.metaParam[`name`type`isReq`description!(`table;-11h;1b;"Table name.")],
.kxi.metaParam[`name`type`isReq`description!(`byCols;11 -11h;1b;"Column(s) to count by.")],
.kxi.metaParam[`name`type`isReq`description!(`startTS;-12h;1b;"Start time (inclusive).")],
.kxi.metaParam[`name`type`isReq`description!(`endTS;-12h;1b;"End time (exclusive).")],
.kxi.metaReturn`type`description!(98h;"Count by specified columns.");
Meta-Building APIs
The .kxi
namespace includes a set of meta-building APIs for defining metadata. The following APIs simplify the process of defining metadata entries for UDAs. For further examples of how to use these APIs, refer to the Examples.
-
.kxi.metaDescription
- Creates a description entry for a UDA's metadata.-
Parameters:
descr
: string - The description of the UDA.
-
-
.kxi.metaParam
- Creates a parameter entry for a UDA's metadata. These parameters should match the inputs of thequery
function.-
Parameters:
param
: dictionary - Dictionary containing any subset of the following keys:name
: symbol - The name of the parameter.type
: short[]|short - The possible types for the parameter.
Info
In the case of a REST request, this entry helps automatically cast the input to the correct type. For example, symbols are processed as strings, but specifying the type as
-11h
prompts kdb Insights to cast it. If multiple types are specified, auto-casting uses the first type in the list.isReq
: Boolean - Indicates whether the parameter is required.default
: any - The default value of the parameter if it is not required.description
: string - Plain text description of the parameter.
-
-
.kxi.metaReturn
- Creates a return entry for a UDA's metadata. This entry should match the return type of theaggregation
function.- Parameters:
return
: dictionary - Dictionary containing any subset of the following keys:type
: short|short[] - The possible types for the return value.description
: string - Plain text description of the return value.
- Parameters:
-
.kxi.metaMisc
- Creates a miscellaneous metadata entry.- Parameters:
misc
: dictionary - Dictionary containing any subset of the supported miscellaneous fields.safe
: Boolean - Indicates whether the UDA can be safely retried in the event of a failure.
- Parameters:
4. Register the UDA
Register the UDA with the appropriate names for query and aggregation functions, and provide the metadata to ensure the UDA can be called from the Service Gateway. This registration is done using the .kxi.registerUDA
function.
-
Add the registration to the file:
.kxi.registerUDA `name`query`aggregation`metadata!(`.exampleuda.countBy;`.exampleuda.countByQuery;`.exampleuda.countByAgg;metadata);
.kxi.registerUDA
Parameters
The .kxi.registerUDA
function accepts a dictionary with the following keys:
Key Name | Required | Type | Description |
---|---|---|---|
name |
Yes | Symbol | The name of the UDA when called using the Service Gateway. * |
query |
Yes | Symbol | The name of the function that runs on the DAPs and retrieves the partials for aggregation. |
aggregation |
No | Symbol | The name of the function that runs on the aggregator and combines the partial results from the DAPs. |
metadata |
No | List | Metadata about the properties of the UDA. It's recommended to use the metadata builders described in the Meta-building APIs section. |
Next steps
-
To add this UDA to a package, follow the Adding a UDA to a package guide.
-
Refer to UDA examples for more examples of User Defined Analytics.