Helper Functions
When writing custom UDAs (user defined analytics), the following helper functions are available in the DAPs to retrieve data.
Data model
Unlike a typical kdb+ database, table data is not stored in the base table name. Data may be distributed across an on-disk table and multiple in-memory tables (see Late data). It is highly recommended to use the following helper functions.
.kxi.selectTable
This a general-purpose function to retrieve data for the specified table. It creates a single synthesized view of the table, removing the need to know how the data is distributed across multiple internal tables. This is the recommended method for accessing data.
-
Parameters
args
- dict - Dictionary containing the following (any other input type triggers an error):table
- symbol - Table name.startTS
- timestamp - Optional: Start time (inclusive) for selecting data. Default:-0Wp
. This can be an empty list for reference tables.endTS
- timestamp - Optional: End time (exclusive) for selecting data. Default:0Wp
. This can be an empty list for reference tables.filter
- list - Optional: Functional where clause, for example, a list of the parse tree. Default:()
, indicating no filter is applied.groupBy
- dict/boolean - Optional: Functional by clause. Default:0b
, indicating no by clause.agg
- dict - Optional: Functional aggregation/select clause. Default: All columns of the table are included.mmap
- boolean - Optional: Boolean that indicates whether the result of a.kxi.selectTable
is mapped instead of loaded to memory. Default:0b
, indicating that this feature is disabled.limit
- int|int[2] - Optional: Value that limits the result row number. (Ordering not guaranteed).
-
Return
- table - Result of the select query.
Warning
.kxi.selectTable
accepts a subset of the parameters used in .kxi.getData
. The keys filter
, groupBy
, and agg
have the same semantic meaning, but there are subtle differences in the syntax. Refer to the documentation for the precise syntax details.
Warning
In previous versions, .kxi.selectTable
accepted six parameters. The latest version now requires a single dictionary. Additionally, there is a minor change in how the end time parameter endTS
functions. Previously, the endTS
parameter was inclusive, in the latest version the endTS
parameter is exclusive. If you are migrating to the new API and want to maintain identical behavior, you must add one nanosecond to the endTS
value.
Examples
args:`table`startTS`endTS`filter`groupBy`agg!(`trade;2024.04.10D;2024.04.15D;((=;`sym;enlist`AAPL);(>;`price;100));enlist[`sym]!enlist`sym;`avgPrice`avgSize!((avg;`price);(max;`size)))
.kxi.selectTable args / Complex select on timeseries data
.kxi.selectTable enlist[`table]!enlist`trade / Select using defaults
/ Below example shows how to join on reference data tables
args:`table`startTS`endTS`filter`agg!(`trade;2024.04.10D;2024.04.15D;((=;`sym;enlist`AAPL);(>;`price;100));`sym`price`sector!(`sym`price`instrument.sector));
.kxi.selectTable args
/ Below example shows how to use mmap feature to get an entire date partition in HDB
args:`table`startTS`endTS`mmap!(`trade;2024.04.10D;2024.04.11D;1b);
t:.kxi.selectTable args / This is now mapped as opposed to loaded to memory
/ Below example shows how to query a table and return a sample 100 rows
args:`table`limit!(`trade;100);
t:.kxi.selectTable args
Advanced
.kxi.selectTable
is the recommended method for retrieving data. However, advanced users may choose to access internal tables individually for greater control.
Info
It is not recommended to use the following functions without a deep understanding of the data model used in kdb Insights. For more information, refer to the Late data documentation.
.kxi.getDapType
Returns the type of the data access process being code is executing in. One of RDB
, IDB
, or HDB
.
-
Parameters
- None
-
Return
- symbol - One of
RDB
,IDB
, orHDB
.
- symbol - One of
Example
q).kxi.getDapType[]
`IDB
.kxi.getTableAccessors
or .kxi.getTableRefAccessors
Returns a list of table accessors or table reference accessors based on the system configuration and the DAP type the function is executing on. The accessors in the response are ordered from the oldest data to the latest.
For example, a .kxi.getTableAccessors
call returns:
q).kxi.getTableAccessors`myPartitionedTable
`.kxi.getTableBase`.kxi.getTableBuffer`.kxi.getTableOverflow
Data returned by .kxi.getTableBase
is older (based on time ingested) than the data returned by .kxi.getTableOverflow
. The order is important for keyed tables, where newer data must be upserted onto older data to ensure the latest in-effect records are returned.
-
Parameters
tn
- symbol - Table name.
-
Return
- symbol[] - List of functions to return or references to data.
Example .kxi.getTableAccessors
q) .kxi.getDapType[]
`HDB
q)tn:`myPartitionedTable
q).kxi.getTableAccessors tn
`.kxi.getBaseTable`.kxi.getBufferTable`.kxi.getOverflowTable
Example .kxi.getTableRefAccessors
q) .kxi.getDapType[]
`HDB
q)tn:`myPartitionedTable
q).kxi.getTableRefAccessors tn
`.kxi.getBaseTableRef`.kxi.getBufferTableRef`.kxi.getOverflowTableRef
.kxi.getTableBase
or kxi.getTableBaseRef
Retrieves a reference to the main portion of the table.
Table Type | Applicable DAPs | What's returned |
---|---|---|
splayed or basic | RDB, IDB, HDB | Returns on-disk table portion of the data written down as of the last EOI. |
splayed_mem | RDB, IDB, HDB | Returns full in-memory table. |
partitioned | RDB, IDB, HDB | Returns the main portion of the data. In RDB this is in-memory, and for IDB/HDB this is a partitioned table on-disk. |
Warning
For IDB and HDB, you must call the function from the global namespace because the data it returns may reside on disk.
-
Parameters
tn
- symbol - Table name.
-
Return for
.kxi.getTableBase
- table - Table data.
-
Return for
.kxi.getTableBaseRef
- symbol - Variable name to access table.
Example
For a partitioned table in IDB:
q).kxi.getTableBase`myTable
int x y
-------
0 0 2
0 1 3
q).kxi.getTableBaseRef`myTable
`..myTable
.kxi.getTableBuffer
or .kxi.getTableBufferRef
Retrieves the portion of the table held in memory that Storage Manager writes to disk during the next EOX event. Use this function for splayed and basic tables, and for partitioned tables in IDB or HDB only when late data is enabled.
Table Type | Applicable DAPs | What's returned |
---|---|---|
splayed or basic | RDB, IDB, HDB | Returns the in-memory portion of data that is written to disk during the next EOI. |
splayed_mem | None | Not applicable |
partitioned | Late data IDB or HDB | Returns the in-memory table that is written to disk during the next EOX event. For IDB, this occurs during EOI, and for HDB, it occurs during EOD. |
-
Parameters
tn
- symbol - Table name.
-
Return for
.kxi.getTableBuffer
- table - Main in-memory portion of the table.
-
Return for
.kxi.getTableBufferRef
- symbol - Variable name of the table holding buffer data.
Example .kxi.getTableBuffer
q).kxi.getTableBuffer`myTable
x y
---
1 3
2 4
Example .kxi.getTableBufferRef
q).kxi.getTableBufferRef`myTable
`.da.i.buffer.myTable
q).da.i.buffer.myTable
x y
---
1 3
2 4
.kxi.getTableOverflow
or .kxi.getTableOverflowRef
Retrieves a reference to the portion of the table held in memory that was ingested after the start of an EOX event. This table is generally empty except between the start of an EOX event (_prtnEnd
) and the end of that event (_reload
).
Table Type | Applicable DAPs | What's returned |
---|---|---|
splayed or basic | RDB, IDB, HDB | Returns any data received between the start and end of an EOI event. This table is typically empty except during an ongoing EOI. |
splayed_mem | None | Not applicable |
partitioned | Late data IDB or HDB | Returns any data received between the start and end of an EOX event (EOI for IDB, and EOD for HDB). This table is typically empty except during the EOX event. |
-
Parameters
tn
- symbol - Table name.
-
Return for
.kxi.getTableOverflow
- table - Delta portion of the table.
-
Return for
.kxi.getTableOverflowRef
- symbol - Variable name of the table holding overflow data.
Example .kxi.getTableOverflow
q).kxi.getTableOverflow`myTable
x y
---
1 3
2 4
Example .kxi.getTableOverflow
q).kxi.getTableOverflowRef`myTable
`.da.i.overflow.myTable
q).da.i.overflow.myTable
x y
---
1 3
2 4
.kxi.getTables
Retrieves the list of tables available in the assembly. This is a DAP level function only.
-
Return
- symbol[] - Table list.
Example
q).kxi.getTables[]
`trade`quote`exchange`instrument
.kxi.getSchema
Retrieves the schema for the given table name. This is a DAP level function only.
-
Parameters
tbl
- symbol - Table name.
-
Return
- table - Table schema.
Example
q).kxi.getSchema[`exchange]
column | typ description oldName attrMem attrIDisk attrDisk isSerialized foreign anymap backfill encrypt vectorIndex
----------| -----------------------------------------------------------------------------------------------------------
time | -16 "" 0 0 0 ::
sym | -11 "" 0 0 0 ::
exchangeID| -7 "" g p 0 0 0 ::
country | -11 "" 0 0 0 ::
openHour | -19 "" 0 0 0 ::
closeHour | -19 "" 0 0 0 ::
currency | 0 "" 0 0 0 ::
timezoneID| -11 "" g 0 0 0 ::
.kxi.getTableProperties
Retrieves the list of associated properties for the given table name. Refer to Schemas for a full list of available properties. This function operates at the DAP level only.
-
Parameters
tbl
- symbol - Table name.props
- symbol|symbol[] - Table properties.
-
Return
- dictionary - Dictionary of requested properties to table property values.
The list of valid properties are:
property | return type | description |
---|---|---|
description |
string | Table description. |
typ |
symbol | Table type. |
pkCols |
symbol[] | Primary key columns. |
prtnCol |
symbol | Column the data is partitioned on. |
sortColsMem |
symbol[] | Columns the data is sorted on in memory (e.g. RDB). |
sortColsIDisk |
symbol[] | Columns the data is sorted on intraday disk (e.g. IDB). |
sortColsDisk |
symbol[] | Columns the data is sorted on on-disk (e.g. HDB). |
isSplayed |
boolean | Indicates if the table is splayed. |
isPartitioned |
boolean | Indicates if the table is partitioned. |
isSharded |
boolean | Indicates if the table is sharded. |
columns |
table | Table columns (see below). |
oldName |
symbol | Name of a previous version of this table. |
shards |
int | Number of assemblies over which the table is sharded. |
slices |
int | Slice count. |
hashCol |
symbol | Column to hash for sharding. |
partitions |
int | Partition count. |
blockSize |
int | Controls how frequently data is written to disk. |
isMD |
boolean | Indicates if the table is reference data. |
isDelta |
boolean | Indicates if the table has an in-memory component. |
Example
q).kxi.getTableProperties[`exchange;`description`typ]
description| "Money can be exchanged for goods and services"
typ | `basic
q).kxi.getTableProperties[`exchange;`typ`notValid]
invalid table property: ,`notValid error
.kxi.getHeader
Retrieves the header of the request. This function operates at the DAP and Aggregator levels.
-
Return
- dict - Dictionary of header keys.
Example
q).kxi.getHeader[]
logCorr | "testCorr"
timeout | 60000
appID | `test
aggFn | `.custom.aggFn
userID | 00000000-0000-0000-1000-000000000000
username | `testUser
// Further example within a custom UDA
// Assuming that user has added the below app* options to their request
appAudit:1b;
appEmployee:`bob;
myAuditFunction:{[args]
auditChk args;
/ Some logic
}
auditChk:{[args]
hdr:.kxi.getHeader[];
if[hdr`appAudit;
.kxi.publish[`auditData;`api`dap`user`args!(hdr`api;.z.h;hdr`appEmployee;args)]]
}
.kxi.response.make
Returns the result of an API call in the format of (hdr;result)
, where the header contains valid RC/AC codes.
-
Parameters
header
- dict - API query header, with user definedapp*
keys only.status
- list - API query status of the form(.kxi.response.rc.{code};.kxi.response.ac.{code};"application information")
.result
- any - API payload.
-
Return
- list - A well formed response conforming to
(header;result)
.
- list - A well formed response conforming to
Example
myFunction:{
/ Some logic
.kxi.response.make[`appTeam`appTest!(`EQ;"Q1 beta");(.kxrrc.OK;.kxrac.OK;"");res]
}
.kxi.response.ok
Returns the result of a successful API call in the format (hdr;result)
, where the header contains valid RC/AC codes.
-
Parameters
result
- any - API payload.
-
Return
- list - A well formed response conforming to
(header;result)
.
- list - A well formed response conforming to
Example
myFunction:{
/ Some logic
.kxi.response.ok res
}
.kxi.response.hok
Returns the result of a successful API call in the format (hdr;result)
, where the header contains valid RC/AC codes and user defined header values are maintained.
-
Parameters
header
- dict - API query header, with user definedapp*
keys only.result
- any - API payload.
-
Return
- list - A well formed response conforming to
(header;result)
.
- list - A well formed response conforming to
Example
myFunction:{
/ Some logic
.kxi.response.hok[`appTeam`appTest!(`EQ;"Q1 beta");res]
}
.kxi.response.error
Returns the result of a failed API call in the format (hdr;result)
, where the header contains valid RC/AC codes.
-
Parameters
ac
- short - AC code indicating the nature of the API failure.ai
- string - Error message to include in the response header.result
- any - API payload.
-
Return
- list - A well formed response conforming to
(header;result)
.
- list - A well formed response conforming to
Example
myFunction:{
/ Some logic
if[not tbl in tables[];
:.kxi.response.error[.kxi.response.ac.TABLE;"Table not found in DAP";res]];
/ Some more logic
}
.kxi.response.herror
Returns the result of a failed API call in the format (hdr;result)
, where the header contains valid RC/AC codes and user defined header values are maintained.
-
Parameters
header
- dict - API query header, with user definedapp*
keys only.ac
- short - AC code indicating the nature of the API failure.ai
- string - Error message to include in the response header.result
- any - API payload.
-
Return
- list - A well formed response conforming to
(header;result)
.
- list - A well formed response conforming to
Example
myFunction:{
/ Some logic
if[not tbl in tables[];
:.kxi.response.herror[`appTeam`appTest!(`EQ;"Q1 beta");.kxi.response.ac.TABLE;"Table not found in DAP";res]];
/ Some more logic
}
.kxi.context.set
Saves a context value that can be retrieved when resuming a request upon receiving the response from a sub-request (refer to .kxi.response.callAPI
).
-
Parameters
name
- symbol|symbol[] - Names of context entries.value
- any - Corresponding context values.
Example
.kxi.context.set[`x;10]
.kxi.context.set[`y`z;("abc";([]c:1 2))]
.kxi.context.get
Retrieves a value previously saved to the context. This is useful when resuming a request after receiving the response from a sub-request (refer to .kxi.response.callAPI
).
-
Parameters
name
- symbol|symbol[] - Names of context entries.
-
Return
- any - Corresponding context values.
Example
.kxi.context.get`x // 10
.kxi.context.get`y`z // ("abc";([]c:1 2))
.kxi.context.contains
Checks for the existence of a context value. Use this in combination with .kxi.context.get
to ensure that expected values are present in the context.
-
Parameters
name
- symbol|symbol[] - Names of context entries.
-
Return
boolean - True if the context value exists, false otherwise
Example
.kxi.context.contains`x // 1b
.kxi.context.contains`a // 0b
.kxi.context.contains`y`a`z // 101b
.kxi.response.callAPI
Calls another API from within a UDA. The response is deferred until the result from the called API is returned. This is an aggregator only helper function.
-
Parameters
api
- symbol|symbol[] -APIs to call. These must be registered APIs available to the gateway. Registered APIs can be viewed using.kxi.getMeta
. The response is deferred until the result is returned.args
- dict|dict[] - API args (parallel toapi
).cb
- symbol - Callback function to invoke on API responses.opts
- dict|dict[] - Optional header augmentations (parallel toapi
).
-
Return
- (dict;list) - Deferred response header, request payloads, and callback.
opts
within deferred requests
Within a deferred request, only the following fields are permissible in the opts
field: aggFn
, cast
, version
, and any field prefixed with app
.
Defer in aggregator example
The example below shows how to use .kxi.response.callAPI
to defer within a UDA until enough data is available for an aggregation calculation:
// @desc Aggregates trade data, but defers if not enough responses.
// @param data {list[]} Partial responses from `.kxi.getData`.
// @return {dictionary;table|list} Response header and payload or sub-request if deferring.
aggMinTrade:{[data]
t:.sgagg.getData data; / Execute normal getData aggregation
hdr:first t; / Response header
tbl:last t; / Table data
if[.kxi.response.rc.OK<>first[t]`rc; / If the response is no good
:.kxi.response.error[.kxi.response.ac.ERR;"Failed to aggregate getData: ",hdr`ai;()]]; / Fail
if[100<count tbl; / If we have enough data
:.kxi.response.ok tbl]; / Succeed
//
// Not enough data.
//
.kxi.context.set[`prevData;tbl]; / Store existing values for later
.kxi.response.callAPI[`.kxi.getData;`table`startTS`agg!(`trade;1+max tbl`time;cols tbl);`.resume.aggMinTrade;()!()] / Defer
}
// @desc Resume function for `aggMinTrade`.
// @param data {list[]} Partial responses from `.kxi.getData`.
// @return {dictionary;table|list} Response header and payload or sub-request if re-deferring.
.resume.aggMinTrade:{[data]
t:.sgagg.getData data; / Execute normal getData aggregation
hdr:first t; / Response header
tbl:last t; / Table data
if[.kxi.response.rc.OK<>first[t]`rc; / If the response is no good
:.kxi.response.error[.kxi.response.ac.ERR;"Failed to aggregate getData: ",hdr`ai;()]]; / Fail
if[not .kxi.context.contains`prevData;
:.kxi.response.error[.kxi.response.ac.ERR;"Unexpected: prevData missing from context";()]]; / Sanity check
prevData:.kxi.context.get`prevData; / Recover previous data
if[100<count[prevData]+count tbl; / Now if we have enough data
:.kxi.response.ok prevData,tbl] / Succeed
//
// Still not enough data.
//
prevData,:tbl; / Accumulate
.kxi.context.set[`prevData;prevData]; / Re-save for later
.kxi.response.callAPI[`.kxi.getData;`table`startTS`agg!(`trade;1+max tbl`time;cols tbl);`resume.aggMinTrade;()!()] / Redefer
}
// We can then register our aggregations functions thusly:
.sgagg.registerAggFn[`.custom.aggMinTrade;
.kxi.metaDescription["Defers on receipt of trade data to ensure there is enough data to run aggregation"],
.kxi.metaParam[`name`type`descriptions!(`trade;0h;"Trade data from DAPs")],
.kxi.metaReturn`type`description!(98h;"Minimum result of trade data");
`$()]
.kxi.pe.enable
/ .kxi.pe.disable
These functions enable or disable protected evaluation within DAPs or the Aggregator. They do not take arguments. Enabling or disabling protected evaluation is useful when building and debugging issues in a user defined analytic (UDA).
Protected evaluation is enabled by default. It is recommended to disable it only in test processes outside of production environments.
Example
Consider the following query function, which causes a type error:
.custom.countBy:{[table;startTS;endTS;byCols]
1+`
}
Although the error is evident in the code, use this function for demonstration purposes only. After calling this API, the DAP logs provide the following output:
2024-08-12 15:45:30.146 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Starting context, corr=23329698-9642-4654-b65e-81bfefe16b05
2024-08-12 15:45:30.146 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Setting context to no-save, reason='requests not saved in DAP'
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] ERROR KXAPI {23329698-9642-4654-b65e-81bfefe16b05} Error (type) encountered executing .custom.countBy, rc=6 ac=11 ai=Unexpected error (type) encountered executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Completed .custom.countBy, rc=6 ac=11 ai=Unexpected error (type) encountered executing .custom.countBy
2024-08-12 15:45:30.146 [hdb] DEBUG DA {23329698-9642-4654-b65e-81bfefe16b05} Sending response to aggregator, agg=:10.244.0.9:5070
2024-08-12 15:45:30.147 [hdb] DEBUG KXCTX {23329698-9642-4654-b65e-81bfefe16b05} Ending context, no-save
The log indicates a type
error, which helps identify that the issue is related to data types. To pinpoint the exact location of the failure, you can enhance debugging by adding log statements to the code. Alternatively, if you attach to the process, kdb can show where the process is failing.
To attach to the process and enable more detailed debugging, run the following commands:
q).kxi.pe.disable[] / This turns off protected evaluation
q)\e 1 / Set kdb to catch errors
After running the same query while connected to the process, the following detailed debugging output is displayed:
q)2024-08-12 15:51:51.154 [hdb] DEBUG KXCTX {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Starting context, corr=ba62e0b9-f290-4abd-be3d-dcc91cb6116d
2024-08-12 15:51:51.154 [hdb] DEBUG KXCTX {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Setting context to no-save, reason='requests not saved in DAP'
2024-08-12 15:51:51.154 [hdb] DEBUG DA {ba62e0b9-f290-4abd-be3d-dcc91cb6116d} Executing .custom.countBy
type error
.custom.countBy[0] {[table;startTS;endTS;byCols]1+`}
^
q))table / See what current table argument is
`trade
q))startTS / See what current startTS argument is
-0Wp
Based on the output you can see specifically where the function is failing. Additionally, you can inspect the current values of the arguments. For larger functions, you can also run through the code line by line to identify the underlying issue.
To revert to the old behavior, run the following:
.kxi.pe.enable[]
.kxi.applyEntFilter
When row-level entitlements are enabled, this applies user specific entitlements to the given table and returns the net data set.
The columns of the data passed to the function must satisfy those required by the entitlements filters.
-
Parameters
tbl
- symbol - Table name.data
- table - Table data.
-
Return
table - Table data with entitlements applied.
Example
Simple calls to apply entitlements to reference, buffered and base data.
.kxi.applyEntFilter[`instrument;.kxi.getTableBaseRef`instrument]
.kxi.applyEntFilter[`trade;.kxi.getTableBuffer`trade]
.kxi.applyEntFilter[`trade;select from .kxi.getTableBase`trade where date=2024.08.30]
Users should be cognizant that when data has been modified, the entitlement filter may not work as intended. For instance here, a user entitled to only emeaLowLat
data may now access the entire table. Therefore, in general, entitlement filters should be applied before modifying the underlying data.
.kxi.applyEntFilter[`trade;update desk:`emeaLowLat from .kxi.getTableBuffer`trade]
.kxi.testUdaWithEntitlements
- This DAP only helper function lets you preview your UDA results against a filter value simulating active row level entitlements.
- If
KXI_ROW_ENTITLEMENTS_ENABLED
is set to true on the DAPs then only an admin user can run this function. - Use this helper function to review the UDA results with different entitlement filters and understand how they impact the data returned from the queried table.
-
It is best practice to use
.kxi.selectTable
or an equivalent helper function when creating a UDA to select data from a table when row level entitlements are enabled. -
Parameters
uda
- (function|symbol) - UDA query function.args
- any - Argument or list of arguments touda
.testFilter
- dict - Dictionary that mocks user entitlements. The key is a single or list of table names. The corresponding values are the entitlement filters you want to test with. For example,_allRows
,_none
, or a list of valid filters where the type may include string where clause, functional where clause or a variable that is defined as a string or functional where clause.
-
Return
- any - Returns the result of the UDA query with the
filter
parameter applied.
- any - Returns the result of the UDA query with the
Example
- To use this query function in kdb Insights Enterprise UI, you can select the Q query option in the Query window and choose a DAP to run it on.
- If testing a predefined function as the
testFilter
value the expectation is that this is available in the DAPs. This can be loaded via packaging. - If testing using qIPC or REST then this could be executed as part of a
.kxi.qsql
query. - If testing using the CLI, you can port forward or attach to one of the DAP processes and execute the UDA code.
Gateway URL
The `GATEWAY` variable below is defined as an IPC connection to the Service Gateway. For example `:insights-qe-gateway:5050` connects to the query environment gateway within an `insights` namespace.
Sample UDA that will be used in the below examples:
// @desc Sample User Defined Analytic query function.
// @param table {symbol} Table name.
// @param startTS {timestamp} Start time (inclusive).
// @param endTS {timestamp} End time (exclusive).
// @param byCols {symbol|symbol[]} Column(s) to count by.
// @return {table} Count by specified columns.
//
.custom.countBy:{[table;startTS;endTS;byCols]
byCols,:();
bc:$[`date in byCols;
[byCols:(byCols except`date),`realTime;
(x,`date)!(x:-1_byCols),enlist($;"d";`realTime)];
{x!x}byCols];
t:.kxi.selectTable`table`startTS`endTS`filter!(`trade;startTS;endTS;enl(within;`realTime;(startTS;endTS)));
?[t;();bc;enlist[`cnt]!enlist(count;`i)]
}
-
Method 1: Run UDA with
_allRows
. Expectation: user is entitled to all rows of data in a table, UDA will execute on this dataset..kxi.testUdaWithEntitlements[`.custom.countBy;(`trade;"p".z.D-2;"p"$.z.D-3;`sym);enlist[`trade]!enlist"_allRows"]" // Results with allRows sym cnt ------------- DNDF.CAN 3378 FNLH.CAN 3520 JBMH.CAN 3400 JFMB.CAN 3500 MGPJ.CAN 3482
-
Method 2: Run UDA with
_none
. Expectation: user is entitled to none of the rows of data in a table. UDA will execute on this dataset..kxi.testUdaWithEntitlements[`.custom.countBy;(`trade;"p".z.D-2;"p"$.z.D-3;`sym);enlist[`trade]!enlist`$"_none"]" // Results with no entitlements sym cnt -------------
-
Method 3: Run UDA with functional where clause. Expectation: user is entitled to the rows that comply with the where clause. UDA will execute on this dataset.
.kxi.testUdaWithEntitlements[`.custom.countBy;(`trade;"p".z.D-2;"p"$.z.D-3;`sym);enlist[`trade]!enl(enl(in;`sym;enl`IEAP.CAN`FNLH.CAN))]" // Results with functional filter entitlements. sym cnt ------------- FNLH.CAN 3520
-
Method 4: Run UDA with string where clause. Expectation: user is entitled to the rows that comply with the where clause. UDA will execute on this dataset.
.kxi.testUdaWithEntitlements[`.custom.countBy;(`trade;"p".z.D-2;"p"$.z.D-3;`sym);enlist[`trade]!enl"sym in `IEAP.CAN`FNLH.CAN"]" // Results with functional filter entitlements. sym cnt ------------- FNLH.CAN 3520
-
Method 5: Run UDA with predefined function. Expectation: user is entitled to rows that are returned when the function is applied to the table. UDA will execute on this dataset.
.test.filter:enl"sym in `IEAP.CAN`FNLH.CAN"; .kxi.testUdaWithEntitlements[`.custom.countBy;(`trade;"p".z.D-2;"p"$.z.D-3;`sym);enlist[`trade]!enl`.test.filter]" // Results with functional filter entitlements. sym cnt ------------- FNLH.CAN 3520
-
Method 6: To test an assembly wide entitlement across all tables, use the following filter with the helper function above:
- The empty symbol as the key means that this applies to all tables.
// Test UDA with assembly wide entitlement filter enlist[`]!enlist`$"_allRows"