classmethod %OnGetFilterList(Output pFilters As %List,pDataSourceName As %String = "") As %Status
Where pFilters is an array with the following nodes:
pDataSourceName is for future use.
For example, the following %OnGetFilterList() adds a filter named New Filter:
ClassMethod %OnGetFilterList(Output pFilters As %List, pDataSourceName As %String = "") As %Status
{
set newfilter=$LB("New Filter","New Filter Display Name",,0)
set pFilters($I(pFilters))=newfilter
}
For another example, the following %OnGetFilterList() uses a utility method to define as filters all the levels in the Patients cube. The utility method %GetFiltersForDataSource() (in %DeepSee.Dashboard.UtilsOpens in a new tab) returns a list of filters in the format needed by %OnGetFilterList():
ClassMethod %OnGetFilterList(Output pFilters As %List, pDataSourceName As %String = "") As %Status
{
set tSC = ##class(%DeepSee.Dashboard.Utils).%GetFiltersForDataSource("patients.cube",.tFilters)
quit:$$$ISERR(tSC)
set i = ""
for {
set i = $order(tFilters(i), 1, data)
quit:i=""
set pFilters($i(pFilters)) = $lb($lg(data,2), $lg(data,1),,1)
}
quit $$$OK
}
Hardcoding the List of Filter Items via the valueList Attribute
Another way to build the list of filter items is to specify the valueList attribute of the <filter> element. Use a comma-separated list of logical names for the filter items. The order of this list determines the order in which filter controls list the filter items.
For example:
<filter name="ZipCode"
valueList="&[36711],&[34577],&[38928],&[32006],&[32007]"
displayList="36711,34577,38928,32006,32007"
/>
This attribute takes precedence over the sql attribute.
If you specify this attribute, you can also specify the displayList attribute, as shown above. If specified, this must be a comma-separated list of display names. If you do not specify this attribute, the logical names are also used as the display names.
Retrieving the List of Filter Items via the sql Attribute
Another way to build the list of filter items is to specify the sql attribute of the <filter> element. If specified, this must be an SQL query. The query can return either one or two columns. The first column in the returned dataset must provide the logical names for the filter items. The second column, if included, provides the corresponding display names. If you do not include a second column, the logical names are also used as the display names.
For example:
<filter name="ZipCode1" sql="SELECT DISTINCT PostalCode FROM BI_Study.City"/>
If you specify the sql attribute, do not specify the displayList or valueList attributes (see the previous subsection).
Using Custom Logic to Build the List of Filter Items at Runtime
Another way to build the list of filter items is to implement %OnGetFilterMembers() and your own logic to create the list. For example:
ClassMethod %OnGetFilterMembers(pFilter As %String, Output pMembers As %List, pSearchKey As %String = "") As %Status
{
Set status = $$$OK
Try {
If (pFilter = "AgeGroup") {
set pFilterSpec="[AgeD].[h1].[Age Group]"
} Elseif (pFilter="Gender") {
set pFilterSpec="[GenD].[h1].[Gender]"
} Elseif (pFilter="ZipCode") {
set pFilterSpec="[HomeD].[h1].[ZIP]"
}
do ##class(%DeepSee.KPI).%GetMembersForFilter("Patients",pFilterSpec,.pMembers,pSearchKey)
}
Catch(ex) {
Set status = ex.AsStatus()
}
Quit status
}
For another approach, you can query the dimension table that holds the level members. (For details on the dimension tables, see Details for the Fact and Dimension Tables in Defining Models for InterSystems Business Intelligence.) The following shows an example:
ClassMethod %OnGetFilterMembers(pFilter As %String, Output pMembers As %List, pSearchKey As %String = "") As %Status
{
set status = $$$OK
try {
if (pFilter = "AgeGroup") {
//get values from level table
set sql = "SELECT DISTINCT DxAgeGroup FROM BI_Model_PatientsCube.DxAgeGroup"
set stmt = ##class(%SQL.Statement).%New()
set status = stmt.%Prepare(sql)
if $$$ISERR(status) {write "%Prepare failed:" do $SYSTEM.Status.DisplayError(status) quit}
set rs = stmt.%Execute()
if (rs.%SQLCODE '= 0) {write "%Execute failed:", !, "SQLCODE ", rs.%SQLCODE, ": ", rs.%Message quit}
while(rs.%Next()) {
set display=rs.DxAgeGroup
set actual="&["_display_"]"
set pMembers($I(pMembers)) = $LB(display,actual)
}
if (rs.%SQLCODE < 0) {write "%Next failed:", !, "SQLCODE ", rs.%SQLCODE, ": ", rs.%Message quit}
}
}
catch(ex) {
set status = ex.AsStatus()
}
quit status
}
Note that this example does not use the pSearchKey argument.
Modifying an MDX Query to Use Filter Values
For an MDX-based KPI, to modify the query to use filters, implement the %OnGetMDX() method. If you use the KPI wizard in Studio and you choose the source type as MDX, the generated KPI class includes the following stub definition for this method, which you can use as a starting point:
/// Return an MDX statement to execute.
Method %OnGetMDX(ByRef pMDX As %String) As %Status
{
Quit $$$OK
}
The variable pMDX contains the MDX query specified in the mdx attribute of <kpi>. (If you did not specify that attribute, pMDX is null.)
When you implement this method, do the following:
-
If there is no initial query, specify an initial value of the variable pMDX. This should be an MDX SELECT query.
There is no initial query if you do not specify a query in the mdx attribute, as described in the Defining Basic KPIs.
-
Examine each applicable filter and get its value.
To do this, use the %filterValues property of your KPI instance, as described in the first subsection.
-
Parse each filter value and convert it as needed to a form that can be used in an MDX %FILTER clause (if that is how you intend to use the filter). For a table that lists the suitable forms, see the second subsection.
-
Modify the variable pMDX to use the filter values, as appropriate for your needs. In the most common case, append a %FILTER clause for each non-null filter, as follows:
%FILTER mdx_expression
Where mdx_expression is the expression for that filter, as created in the previous step.
Or create an MDX tuple expression that combines the expressions and then add a single %FILTER clause like this:
%FILTER tuple_expression
In more exotic cases, you could use the filter values to rewrite the query string in other ways.
-
Return $$$OK (or 1).
The last subsection provides an example.
Accessing Filter Values
When a user selects filter items, the system sets the %filterValues property of your KPI instance. This property is an object (specifically an instance of %ZEN.proxyObjectOpens in a new tab) that the system creates as follows:
-
It has one property for each filter in the KPI, with the same name (case-sensitive) as the filter.
For example, if a KPI has filters named filter1, filter2, and filter3, then the %filterValues property of your KPI instance has properties named filter1, filter2, and filter3.
-
The properties of %filterValues contain values shown in the following table:
User Selection in Filter |
Value of %filterValues.FilterName |
Notes |
None |
null |
|
One item |
item |
|
Multiple items |
{item1,item2} |
Not applicable if multiselect is off |
One item and the Exclude option |
%NOT item |
Not applicable if multiselect is off |
Multiple items and the Exclude option |
%NOT {item1,item2} |
Not applicable if multiselect is off |
Range |
item1:item2 |
Applies only to a filter defined in a pivot table, which can affect the KPI |
Converting Filter Values to MDX Expressions for Use in %FILTER
The following table lists appropriate MDX expressions to use in %FILTER clauses, depending on user selections.
User Selection in Filter |
Appropriate MDX %FILTER Expression |
None |
none (do not apply a %FILTER clause in this case) |
One item |
[dimension].[hierarchy].[level].[member] |
One item and the Exclude option |
[dimension].[hierarchy].[level].[member].%NOT |
item1 and item2 |
{item1,item2,...} where each item has the form [dimension].[hierarchy].[level].[member] |
Multiple items and the Exclude option |
EXCEPT([dimension].[hierarchy].[level].MEMBERS,{item,item,item,...}) where each item has the form [dimension].[hierarchy].[level].[member] |
A range |
[dimension].[hierarchy].[level].[member]:[member] |
For an example, see the method BuildString() in the sample class BI.Utils.MDXAutoFiltersKPI.
Note that if multiselect is disabled, EXCLUDE is also disabled, and there are fewer possible forms of filter values. In this case, query construction is comparatively simple.
Example
The following implementation of %OnGetMDX() applies to a KPI in which multiselect is disabled:
Method %OnGetMDX(ByRef pMDX As %String) As %Status
{
if (..%filterValues."[aged].[h1].[age group]"'="")
{
set pMDX = pMDX _ " %FILTER [aged].[h1].[age group]." _..%filterValues."[aged].[h1].[age group]"
}
if (..%filterValues."[gend].[h1].[gender]"'="")
{
set pMDX = pMDX _ " %FILTER [gend].[h1].[gender]." _..%filterValues."[gend].[h1].[gender]"
}
if (..%filterValues."[homed].[h1].[zip]"'="")
{
set pMDX = pMDX _ " %FILTER [homed].[h1].[zip]." _..%filterValues."[homed].[h1].[zip]"
}
quit $$$OK
}
Because this method uses delimited property names, it can be confusing to read. Notes:
-
"[aged].[h1].[age group]" is a valid property name.
-
..%filterValues."[aged].[h1].[age group]" is a reference to the "[aged].[h1].[age group]" property of the %filterValues property of the KPI instance.
Modifying an SQL Query to Use Filter Values
For an SQL-based KPI, to modify the query to use filters, implement the %OnGetSQL() method. If you use the KPI wizard in Studio and you choose the source type as SQL, the generated KPI class includes the following stub definition for this method, which you can use as a starting point
/// Return an SQL statement to execute.
Method %OnGetSQL(ByRef pSQL As %String) As %Status
{
Quit $$$OK
}
The variable pSQL contains the SQL query specified in the sql attribute of <kpi>. If you did not specify that attribute, pSQL is null.
When you implement this method, do the following:
-
If there is no initial query, specify an initial value of the variable pSQL. This should be an SQL SELECT query.
There is no initial query if you do not specify a query in the sql attribute, as described in Defining Basic KPIs.
-
Examine each applicable filter and get its value. The options are as follows:
-
Use the %GetSQLForFilter() method, which returns values in a convenient format for use in the WHERE clause of an SQL query. See the first subsection.
-
Use the %filterValues property of your KPI instance, as described in the previous section.
-
Modify the variable pSQL to use the filter values, as appropriate for your needs. In the most common case, modify the query to include an SQL WHERE clause.
In more exotic cases, you could use the filter values to rewrite the query string in other ways.
The second and third subsections provide examples.
-
Return $$$OK (or 1).
Note:
An SQL-based KPI cannot have more than 1000 rows; the system automatically limits the number of rows returned.
%GetSQLForFilter()
For an SQL-based KPI, you can use a method to access filter values in a format that is useful for including in your query:
method %GetSQLForFilter(sql_field_reference,filter_name) As %String
Examines the current filter selections and returns a string that you can use in the WHERE clause of an SQL query.
sql_field_expression is an SQL field name and can include arrow syntax. filter_name is the name of a filter defined in this KPI.
For example, consider the following method call:
..%GetSQLForFilter("City->Name","City")
The following table shows the values returned by this method call, in different scenarios.
Scenario |
Value Returned by Method |
User selects PINE |
City->Name = 'PINE' |
User selects a range, starting with MAGNOLIA and ending with PINE |
City->Name = ('MAGNOLIA':'PINE') |
User selects MAGNOLIA and PINE |
City->Name IN ('MAGNOLIA','PINE') |
User selects PINE and selects Exclude |
City->Name <> 'PINE' |
User selects MAGNOLIA and PINE and selects Exclude |
City->Name NOT IN ('MAGNOLIA','PINE') |
SQL KPI Example 1
The following example is from the sample class BI.Model.KPIs.DemoSQL. In this case, the filter adds GROUP BY and ORDER BY clauses to the SQL query.
Method %OnGetSQL(ByRef pSQL As %String) As %Status
{
//this is the start of the SQL query for this KPI
Set pSQL = "SELECT Count(*),AVG(Age) FROM BI_Study.Patient "
Set where = ""
//look at %filterValues to see if a filter has been applied to this KPI instance
If $IsObject(..%filterValues) {
If (..%filterValues.ZipCode'="")
{
// Call utility method that returns filter data in convenient format
Set sqlstring=..%GetSQLForFilter("HomeCity->PostalCode","ZipCode")
Set where = "WHERE "_sqlstring
}
}
Set groupby="GROUP BY HomeCity "
Set orderby="ORDER BY HomeCity "
// assemble the SQL statement
Set pSQL=pSQL_where_groupby_orderby
Quit $$$OK
}
SQL KPI Example 2
The following example is from the sample class HoleFoods.KPISQL:
Method %OnGetSQL(ByRef pSQL As %String) As %Status
{
If $IsObject(..%filterValues) {
Set tWHERE = ""
If (..%filterValues.City'="") {
Set tWHERE = tWHERE _ $S(tWHERE="":"",1:" AND ") _ " Outlet->City = '" _ ..%filterValues.City _"'"
}
If (..%filterValues.Product'="") {
Set tWHERE = tWHERE _ $S(tWHERE="":"",1:" AND ") _ " Product = '" _ ..%filterValues.Product _"'"
}
If (tWHERE'="") {
// insert WHERE clase within query
Set tSQL1 = $P(pSQL,"GROUP BY",1)
Set tSQL2 = $P(pSQL,"GROUP BY",2)
Set pSQL = tSQL1 _ " WHERE " _ tWHERE
If (tSQL2 '= "") {
Set pSQL = pSQL _ " GROUP BY" _ tSQL2
}
}
}
Quit $$$OK
}
In this case, the KPI defines the initial query within the sql attribute, as described in Defining Basic KPIs. The %OnGetSQL() method modifies that query.
Additional MDX KPI Examples
This section discusses some other MDX KPI examples.
DemoMDXAutoFilters KPI
The sample class BI.Model.KPIs.DemoMDXAutoFilters KPI is simple but uses a special superclass:
Class BI.Model.KPIs.DemoMDXAutoFilters Extends BI.Utils.MDXAutoFiltersKPI
{
Parameter CUBE = "PATIENTS";
Parameter DOMAIN = "PATIENTSAMPLE";
XData KPI [ XMLNamespace = "http://www.intersystems.com/deepsee/kpi" ]
{
<kpi name="DemoMDXAutoFilters" displayName="DemoMDXAutoFilters"
sourceType="mdx"
mdx="SELECT {[Measures].[%COUNT],[Measures].[Avg Age],[Measures].[Avg Allergy Count]} ON 0,
NON EMPTY [DiagD].[H1].[Diagnoses].Members ON 1 FROM [Patients]">
<property name="Patient Count" displayName="Patient Count" columnNo="1" />
<property name="Avg Age" displayName="Avg Age" columnNo="2" />
<property name="Avg Allergy Count" displayName="Avg Allergy Count" columnNo="3" />
</kpi>
}
}
Notice that this class does not directly define any filters, does not directly define the filter members, and defines only a hardcoded MDX query. When you display the test page for this KPI, however, you can use all the levels of the Patients cube as filters, and the KPI appends a suitable %WHERE clause to the query. For example:
This KPI extends the sample BI.Utils.MDXAutoFiltersKPI, which defines the filters and rewrites the query.
Within this class:
-
%OnGetFilterList() retrieves all the levels defined in the cube, as given by the CUBE class parameter.
-
%OnGetFilterMembers() is implemented. For each level, it retrieves the level members, in the format required by pivot tables; see Filter Syntax for Pivot Tables, earlier in this page.
-
The instance method FilterBuilder() iterates through the cube-based filters, retrieves the current value of each, and then combines them into a string that is suitable for use as an MDX %FILTER clause.
-
%OnGetMDX() appends the %FILTER clause to the hardcoded query.
DemoInteroperability KPI
The sample class BI.Model.KPIs.DemoInteroperability KPI is a more complex version of the preceding example. Within this class, %OnGetFilterList() retrieves all the levels defined in the cube, as given by the CUBE class parameter. It then adds an additional filter called Yaxis:
ClassMethod %OnGetFilterList(ByRef pFilters As %List, pDataSourceName As %String = "") As %Status
{
//call method in superclass so we can get filters of the associated cube
set tSC=##super(.pFilters,pDataSourceName)
quit:$$$ISERR(tSC) tSC
//update pFilters array to include the custom filter
set pFilters($i(pFilters)) = $lb("Yaxis","Yaxis",,0)
quit $$$OK
}
%OnGetFilterMembers() is implemented. For the filter Yaxis, this method provides a set of members. For other filters, it retrieves the level members, in the format required by pivot tables; see Filter Syntax for Pivot Tables, earlier in this page. This method is as follows:
ClassMethod %OnGetFilterMembers(pFilter As %String, Output pMembers As %List, pSearchKey As %String = "",
pDataSourceName As %String = "") As %Status
{
set pMembers=""
if (pFilter="Yaxis") {
set pMembers($I(pMembers))=$LB("Home City","[homed].[h1].[city]")
set pMembers($I(pMembers))=$LB("Favorite Color","[colord].[h1].[favorite color]")
set pMembers($I(pMembers))=$LB("Profession","[profd].[h1].[profession]")
set pMembers($I(pMembers))=$LB("Diagnoses","[diagd].[h1].[diagnoses]")
} else {
//call method in superclass so we can get filter members for the associated cube
do ..%GetMembersForFilter(..#CUBE,pFilter,.pMembers)
}
quit $$$OK
}
Finally, %OnGetMDX() constructs the MDX query. The Yaxis filter determines which level is used for rows. Then the method appends the %FILTER clause to the query; the %FILTER clause uses any cube-based filters as in the previous example.
Method %OnGetMDX(ByRef pMDX As %String) As %Status
{
set yaxis=", NON EMPTY [profd].[h1].[profession].MEMBERS ON 1"
//check custom filter value
if (..%filterValues."Yaxis"'="") {
set yaxis=", NON EMPTY "_..%filterValues.Yaxis_".MEMBERS ON 1"
}
set pMDX="SELECT {MEASURES.[%COUNT],MEASURES.[avg age]} on 0"_yaxis_" FROM "_..#CUBE
/// append a %FILTER clause to handle any other filter values
Set pMDX = pMDX _ ..FilterBuilder()
Quit $$$OK
}
Defining a Listing for a KPI
You can define a KPI so that it includes a listing option. In this case, if the KPI also includes filters, the listing definition must consider the filter selections.
To define the listing, you implement the %OnGetListingSQL() method in your KPI class. This method has the following signature:
ClassMethod %OnGetListingSQL(ByRef pFilters As %String, ByRef pSelection As %String) As %String
This method returns the text of a listing query. The arguments are as follows:
-
pFilters is a multidimensional array that contains the current filter values. This array has the following nodes:
Node |
Node Value |
pFilters(filter_name) where filter_name is the name of a filter defined in this KPI |
Current value of this filter |
For details, see Defining KPI Filters.
-
pSelection is a multidimensional array that contains the information about the current selection. This array has the following nodes:
Node |
Node Value |
pSelection("selectedRange") |
Currently selected cells in the pivot as a string in the form "startRow,startCol,endRow,endCol" (1-based). |
pSelection("rowValues") |
Comma-separated list of the values for the selected rows. In these values, any comma is presented as a backslash (\). If no property of the KPI is configured as the value, then this node contains the series name instead. |
pSelection("sortColumn") |
Specifies the number of the column to use for sorting the listing. Use 0 for no sorting. |
pSelection("sortDir") |
Specifies the sort direction, "ASC" or "DESC" |
The method should return an SQL SELECT query. In this query, you can also use arrow syntax and SQL functions, as with other listings.
Or you can override the %OnGetListingResultSet() method. In this case, you must prepare and execute the result set.
Example
The following example is from HoleFoods.KPISQL:
ClassMethod %OnGetListingSQL(ByRef pFilters As %String, ByRef pSelection As %String) As %String
{
Set tSQL = "SELECT TOP 1000 %ID,DateOfSale,Product FROM HoleFoods.SalesTransaction"
// apply sorting, if asked for
If (+$G(pSelection("sortColumn"))>0) {
Set tSQL = tSQL _ " ORDER BY " _ pSelection("sortColumn") _ " " _ $G(pSelection("sortDir"))
}
Quit tSQL
}
FeedbackOpens in a new tab