Oracle Analytics Embedding — How to Obtain Filter Values from Event Data Actions

Introduction

Mike Durran
Oracle Developers

--

Oracle Analytics has a rich set of functionality for filtering your data. When you embed Oracle Analytics content, you may want to programmatically get access to the values of the filters that have been used on the embedded canvas. In this blog, I’ll describe how to do that.

Oracle Analytics Filters

The types of filters available are described in the product documentation, at a high level, they are:

  • List filters
  • Range filters
  • Top and bottom filters
  • Date filters — ranges and relative time (e.g last 3 years)
  • Expression filters

Each of these filter types has additional options, such as the ability to specify attributes or measures the filter is ‘By’ (e.g., top 10 regions by sales). Filters can be set to ‘include’ or ‘exclude’ the values identified. Filters can be:

  • Defined at the top of the canvas, known as the ‘filter bar’ (this is the filter bar that can be hidden using <oracle-dv> embedding properties
  • On the canvas itself, these are known as dashboard filters
  • At the visualization level

Any code that obtains the values of these filters when embedded will need to cover all these types of filters and use cases.

Event Data Actions

Oracle Analytics Data Actions functionality allows the creation of a number of ‘actions’ whereby a user can navigate to different analytics content, passing values as filters, navigate to URLs or call REST APIs.

In order to capture the values of filters, I’m going to use an event data action since these allow an end user to invoke the event from within the context of an analytics canvas and pass that context to the host application.

This section of the product documentation describes the payloads that are sent when an event data action is invoked. In order to capture the output from the event data action, we need some code in the form of a listener and a way to parse the JSON payloads that are sent from the data action.

The product documenetation includes sample HTML code that can show the payload sent by Oracle Analytics that includes the data values selected when a user invokes a data action. In this blog, I’ll extend that sample code to show how to also obtain the values of filters.

The video below introduces the ability to invoke an event data action, and the output that can be captured, via the event payload JSON contexts. There are two contexts sent by the event :

  • context — this provides the data points where the data action was invoked, e.g. a row of data (needs to be a pivot table if you want to select a row, but event data actions can be invoked from data points on charts etc.)
  • contextv2 — this provides the values of the filters that are currently active on the analytics canvas

Understand the Event Payload

Each of the following examples will require you to have a workbook stored in Oracle Analytics with an Event Data Action defined as follows:

Event Data Action defined in Oracle Analytics

Note that without some code to listen for, and handle the event, as an end user, you won’t see anything in Oracle Analytics itself but will need to view the payload in the browser dev tools console. I’ve included sample code below that will output to the console the payload from the event data action in adition to showing the values detected on the page. To make it work for your system, you need to change location of embedding.js to your instance and reference an Analytics workbook in your catalog. See here and here for details of getting started with embedding.

<!DOCTYPE HTML>
<html dir='ltr'>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="https://<OAC INSTANCE>.analytics.ocp.oraclecloud.com/public/dv/v1/embedding/standalone/embedding.js" type="text/javascript">
</script>
</head>

<body>

<h1>Oracle Analytics Data Actions and Filter Context from Data Actions</h1>
<hr>

<div>
<h3>Event Data Action Data Values from payload context</h3>
<p id='response_div' style='font-size:16'>No data yet</p>
</div>
<hr>

<div id="mydiv" style="position: absolute; width:100%; height: calc(100% - 120px)">
<oracle-dv id="myEmbed"
project-path="{{projectPath}}"
active-page="canvas"
active-tab-id="1">
</oracle-dv>
</div>

<script>
requirejs(['knockout', 'ojs/ojcore', 'ojs/ojknockout', 'ojs/ojcomposite', 'jet-composites/oracle-dv/loader'],
function(ko) {
function MyProject() {
var self = this;
self.projectPath = ko.observable("/@Catalog/shared/Embed/Public Live/Dark_Theme_Filter_Events");
}
ko.applyBindings(MyProject);
}
);

var eventName = 'oracle.bitech.dataaction';
var element = document.getElementById("mydiv");

if (element) {
var oEventListener = element.addEventListener(eventName, function (e) {
console.log("***** Payload from DV ***** ");
console.log("eventName = " + e.detail.eventName);
console.log("Total payload = " + JSON.stringify(e.detail));
console.log("payload = " + JSON.stringify(e.detail.payload));
console.log("***** Payload from DV end ***** ");
var res = " Data Received from DV Content: ";

// Get the values of the row from where data action was invoked
Object.keys(e.detail.payload.context).forEach(function(key) {
res = res.concat(key);
res = res.concat(" : ");
var temp = e.detail.payload.context[key]["oValueMap"];
var temp1 = Object.keys(temp)[0];
res = res.concat(temp1);
res = res.concat(" | ");
});

//send data action context to the console and HTML page
console.log("Data Action Row Context : " + res);
document.getElementById('response_div').innerHTML = res;

}, true);
}

</script>

</body>
</html>

This sample code listens for the payload being sent by the event data action. At a high level, if we collapse most of the JSON properties, we see the following structure (assuming filters are also present on the canvas, if they are not, then you won’t see the ‘contextv2’:

Total payload = {
"eventName": "Event",
"payload": {
"context": {=},
"contextv2": {=}
}
}

Here is a snippet of the expanded payload for ‘context’ as output to the console by the code above, look for the “payload” : “context” : key values — in this case “Product” and the “oValueMap” property.

Total payload = {
"eventName": "Event",
"payload": {
"context": {
"Product": {
"oColumnInfo": {
"_bIsXSA": true,
"SerializedObjectType": {
"Caption": "caption"
},
"_info": {
"columnID": "Product",
"isTime": false,
"desc": "",
"subjectArea": "XSA('mike.durran@oracle.com'.'Order Activity')",
"columnHeading": {
"text": "Product"
},
"tableHeading": {
"text": "Columns"
},
"type": "regularColumn",
"sqlInfo": {
"dataType": "varchar",
"category": "text",
"primaryType": "text",
"typeName": "VARCHAR",
"aggType": "nonAgg",
"aggRule": "none",
"nullable": true,
"hidden": false,
"displayFormula": "\"Columns\".\"Product\"",
"sqlFormula": "XSA('mike.durran@oracle.com'.'Order Activity').\"Columns\".\"Product\"",
"sqlFormulaIn2Parts": "\"Columns\".\"Product\"",
"sqlFormulaSubjectAreaPart": "XSA('mike.durran@oracle.com'.'Order Activity')",
"sqlFormulaDisplaySubjectAreaPart": "XSA('mike.durran@oracle.com'.'Order Activity')",
"hasSortKey": false,
"isDoubleColumn": false,
"precision": "",
"scale": ""
},
"formula": "XSA('mike.durran@oracle.com'.'Order Activity').\"Columns\".\"Product\"",
"additionalProperties": {},
"bIsFormulaValid": true,
"serializedType": "regularColumn",
"isUserExpression": false
},
"_internalIsMeasure": false
},
"oValueMap": {
"Android": "Android"
}
}

Specifically for this code sample:

// Get the values of the row from where data action was invoked
Object.keys(e.detail.payload.context).forEach(function(key) {
res = res.concat(key);
res = res.concat(" : ");
var temp = e.detail.payload.context[key]["oValueMap"];
var temp1 = Object.keys(temp)[0];
res = res.concat(temp1);
res = res.concat(" | ");
});

At the ‘context’ level, the key values are the names of the columns / measures / attributes, in this case ‘Product’ and the value is referenced by ‘oValueMap’ which in this case is ‘Android’. This sample code builds up a string ‘res’ which is a concatentation of the column, value pairs. The idea is that your code could create whatever type of object (e.g. JSON array, etc.,) you need for your use case using this method. So for a pivot table such as this, invoking the event data action on the row ‘Android’:

Results in this output:

Product : Android | Sales : 134220.38 | UnitsSold : 602 | Cost : 71466.73 |

Event Payload for Filters

In a similar way to described above, the values for canvas filters are also passed in the event data action payload, in this case, they are contained in the contextv2 payload. Within this section of the payload, we are interested in the “aFilterModels” properties illustrated below:

I’ll include the complete sample code at the end of this blog but in order to work with all the various filter types listed at the start of this blog, known as “sPluginType” we need code that will check for:

  • Do any filters exist?
  • What type of filters have been added to the canvas?
  • Whether the filter is ‘include’ or ‘exclude’
  • Obtain the values used in the filter

Here is a code sample to cover the first two items in the above list:

    // check if any top level filter values exist
if ((Object.keys(e.detail.payload.contextv2._aFilterModels).length) > 0) {
var filterValsPrefix = "Values for filter context: ";
} else {
var filterValsPrefix = "There are no filters detected";
}

var filterVals = "";

// loop over the _aFilterModels array
Object.keys(e.detail.payload.contextv2._aFilterModels).forEach(function(key) {

console.log("Key Value :" + key + " filtervals string: " + filterVals);

const oFilterModel = e.detail.payload.contextv2._aFilterModels[key];
console.log(oFilterModel);

const sFilterType = oFilterModel["_sPluginType"];
console.log(sFilterType)

switch (sFilterType) {
case "oracle.bi.tech.listfilter": {
var sFilterValsReturned = _processListFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.numberrangefilter": {
var sFilterValsReturned = _processNumberRangeFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.topbottomfilter": {
var sFilterValsReturned = _processTopBottomFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.daterangefilter": {
var sFilterValsReturned = _processDateRangeFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.relativedatefilter": {
var sFilterValsReturned = _processRelativeDateFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.expressionfilter": {
var sFilterValsReturned = _processExpressionFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
default:
var sFilterValsReturned = _processUnknownFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
}

});

Note that in the switch statement, we call a seprate function for each type of filter plugin. I’ll describe what each of these functions are doing below. In each function, the code will create and append values to a string ‘pFilterValsString’, that is returned from the function and ultimately sent as output to the console and also shown on the web page as seen in the earlier demo video.

    //send top filter bar context to the console and HTML page
console.log(filterVals);
var filterValsReturn = filterValsPrefix + filterVals;
document.getElementById('filterBar_div').innerHTML = filterValsReturn;

You will notice that for some filter types, there is an option ‘By’ where the user can set which attributes affect the filter (see illustration below). When obtaining the filter values, we also need to check for these attributes and this will be part of each function (where needed) to process the various filter types.

List Filters

The function to process list filters does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’ In a similar manner to the earlier payload processing for event data action values, I’m building up a string in these examples which can then be used for other purposes or you can create objects that fit your use case. I add characters such as | in the strings as I build them up to assist with any string manipulations — i.e. locate the positions of the | character can help with extracting values from the string.
  • Check if the filter is inclusive or exclusive
  • Loop through the array of selected values in the list filter and append them to the ‘pFilterValsString’
  • Return the ‘pFilterValsString’
    function _processListFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// **** CHECK IF INCLUSIVE OR EXCLUSIVE
if (pFilterModel["_bIsInclusive"] === true) {
pFilterValsString = pFilterValsString.concat(" Include ");
} else if (pFilterModel["_bIsInclusive"] === false) {
pFilterValsString = pFilterValsString.concat(" Exclude ");
}

// now get the values used in the filter
var aFilterValues = Object.values(pFilterModel["_aSelectedValues"]);

for (let i=0; i < aFilterValues.length; i++){
// add each filter value to the filterVals string separated by ,
pFilterValsString = pFilterValsString.concat(aFilterValues[i]._sDisplayValue +", ");
}
return pFilterValsString;
}

Number Range Filters

The function to process number range filters does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’
  • Get the start and endpoint to the number range. Note that “_vLimit2” property will be null for range types ≤ or ≥ and that “_vLimit1” will contain the starting or ending value for both these filter types i.e., “_vLimit2” is only used for the ‘between’ filter type
  • Check if there are any ‘By’ attributes and if there are, obtain those values to return from the function
  • Check if the filter is ≤ or ≥ or between and obtain the values accordingly
  • Return a string containg the filter values and any ‘By’ attributes
     function _processNumberRangeFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the starting and endpoint
var filterRangeLimit1 = pFilterModel["_vLimit1"];
var filterRangeLimit2 = pFilterModel["_vLimit2"];
var filterRangeType = pFilterModel["_eRangeFilterType"];

//check if by all attributes where the _aByColumns would be empty
if (Object.keys(pFilterModel["_aByColumns"]).length === 0) {
// All attributes used in by clause since _aByColumns is empty
var rangeFilterByValues = " All Attributes ";

} else {
// There is a specific column or columns referenced in the by clause
var rangeFilterByValues = "";
var aRangeValues = Object.values(pFilterModel["_aByColumns"]);

for (let i=0; i < aRangeValues.length; i++){
// add each measure value to the filterVals string separated by ,
rangeFilterByValues = rangeFilterByValues.concat(aRangeValues[i]._sDisplayName +", ");
}
}

// Add to the list of filters
if (filterRangeType === "lessOrEqual" ){
pFilterValsString = pFilterValsString.concat("<= " + filterRangeLimit1 + " By " + rangeFilterByValues);
} else if (filterRangeType === "greaterOrEqual" ) {
pFilterValsString = pFilterValsString.concat(">= " + filterRangeLimit1 + " By " + rangeFilterByValues);
}
else {
pFilterValsString = pFilterValsString.concat("Between " + filterRangeLimit1 + " and " + filterRangeLimit2 + " By " + rangeFilterByValues );
}

return pFilterValsString;
}

Top / Bottom Filters

The function to process top/bottom filters does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’
  • Get the filter mode and number of ranked items
  • Top / bottom filters can either be a defined as measure by attribute (e.g., Top 10 sales by month) or attribute by measure (e.g., Top 10 months by sales). These would return the same data but both scenarios need to be checked by the code and the relevnt values obtained
  • Return the string containing the filter description depending on the scenario as described above
     function _processTopBottomFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the top or bottom mode and number
var filterTopOrBottom = pFilterModel["_eTopBottomMode"];
var filterTopOrBottomN = pFilterModel["_iNumberOfRankedItems"]

// *** This top bottom N could be based on a fact element or a data element
// check if _factElement exsits
if (pFilterModel.hasOwnProperty("_factElement")) {
// fact element exists get the measure used for the top or bottom N
var filterTopOrBottomMeasure = pFilterModel["_factElement"]["_sDisplayName"];
} else {
//fact element DOES NOT exist
//check if by all attributes where the _aByColumns would be empty

if (Object.keys(pFilterModel["_aByColumns"]).length === 0) {
// All attributes used in by clause since _aByColumns is empty
var filterTopOrBottomMeasure = " All Attributes ";

} else {
// There is a specific column or columns referenced in the by clause
var filterTopOrBottomMeasure = "";
var aTopBottomMeasures = Object.values(pFilterModel["_aByColumns"]);

for (let i=0; i < aTopBottomMeasures.length; i++){
// add each measure value to the filterVals string separated by ,
filterTopOrBottomMeasure = filterTopOrBottomMeasure.concat(aTopBottomMeasures[i]._sDisplayName +", ");
}
}
}

// Add to the list of filters but need to change order if _factElement exists or not
if (pFilterModel.hasOwnProperty("_factElement")) {
// fact element exists
// get the measure used for the top or bottom N
pFilterValsString = pFilterValsString.concat(filterTopOrBottom + " " + filterTopOrBottomN + " based on " + filterTopOrBottomMeasure);
} else {
//fact element DOES NOT exist
pFilterValsString = pFilterValsString.concat(filterTopOrBottom + " " + filterTopOrBottomN + " By " + filterTopOrBottomMeasure);
}
return pFilterValsString;
}

Date Range Filters

The function to process date range filters does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’
  • Check whether it’s a ‘between’ with a date range or whether it’s an ‘equal to’ filter (which uses the same date for both limits, and can be detected by there being zero seconds between the two dates)
  • If it is a ≤ or ≥ filter type, obtain the dates used
  • Add the detected type and dates to a string to return from the function
     function _processDateRangeFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the range filter type
var filterRangeFilterType = pFilterModel["_eRangeFilterType"];
// Add to the list of filters
if (filterRangeFilterType === "between" ){
// get the starting and endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
var filterDateRangeEnd = pFilterModel["_vLimit2"];
//get difference in seconds between dates
var filterDateDiff = pFilterModel["_vLimit2"] - pFilterModel["_vLimit1"];
// Check if it's an equal to specific date
if (filterDateDiff === 0) {
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("Equal to " + filterDateRangeStart + " " );
} else if (filterDateDiff !== 0) {
// It is a date range - add to the list of filters
pFilterValsString = pFilterValsString.concat("From " + filterDateRangeStart + " To " + filterDateRangeEnd + " " );
}
} else if (filterRangeFilterType === "greaterOrEqual") {
// get the starting and endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("From " + filterDateRangeStart + " " );
} else if (filterRangeFilterType === "lessOrEqual") {
// get the endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("To " + filterDateRangeStart + " " );
}
else {
console.log("Unknown Date Range Filter Type detected")
}
return pFilterValsString;
}

Relative Date Filter

The function to process relative date range filters does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’
  • Obtain the range type, level and number of time periods
  • Return a string containing the filter description from the function
     function _processRelativeDateFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the range timelevel and number of periods
var filterDateRangeType = pFilterModel["_oRelativeDateDataModel"]["_eRangeType"];
var filterTimeLevel = pFilterModel["_oRelativeDateDataModel"]["_eTimeLevel"];
var filterNumberOfPeriods = pFilterModel["_oRelativeDateDataModel"]["_iNumberOfPeriods"];

// Add to the list of filters
pFilterValsString = pFilterValsString.concat(filterDateRangeType + " " + filterNumberOfPeriods + " " + filterTimeLevel + " Relative to Today");

return pFilterValsString;
}

Expression Filter

The function to process an expression filter does the following:

  • Create the initial string that will hold the fitler values ‘pFilterValsString’. Note that for expression filters there is no display name property
  • Get the expression used for the filter
  • Return a string describing the filter from the function
     function _processExpressionFilter(pFilterModel) {
// For expression filter there is no _sDisplayName property ********
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(" : ");

// get the expression definition
var filterExpression = pFilterModel["_sExpression"];

// Add to the list of filters
pFilterValsString = pFilterValsString.concat(filterExpression);

return pFilterValsString;
}

Conclusion

Obtaining the filter values used on an embedded Oracle Analytics canvas can be useful in a number of scenarios where you may want to re-use those filter values in an application (e.g., Oracle APEX) or a webpage that is hosting embedded Oracle Analytics content.

There are several types of filters that can be defined in Analytics canvases, and I’ve presented sample code that will obtain the filter values for each type of filter. To obtain the filter values, create an event data action on the canvas, then when a user invokes that data action, a JSON payload is sent to the host page or application.

The sample code returns a string that can be further manipulated or tailored to your own use case. For each type of filter, the JSON can be queried to obtain the type of filter and the filter values.

Sample Code

This sample code will need to be updated with details specific to your own Oracle Analytics instance and workbook you’re using. Don’t forget to create an Event Data Action within that canvas.

<!DOCTYPE HTML PUBLIC>
<html dir='ltr'>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="https://<LINK FOR YOUR OAC INSTANCE>.analytics.ocp.oraclecloud.com/public/dv/v1/embedding/standalone/embedding.js" type="text/javascript">
</script>
</head>

<body>

<h1>Oracle Analytics Data Actions and Filter Context from Data Actions</h1>
<hr>

<div>
<h3>Event Data Action Data Values from payload context</h3>
<p id='response_div' style='font-size:16'>No data yet</p>
</div>
<hr>
<br>

<div>
<h3>Filter Values from Event Data Action payload contextv2</h3>
<p id='filterBar_div' style='font-size:16'>No data yet</p>
</div>
<hr>
<br>

<div id="mydiv" style="position: absolute; width:100%; height: calc(100% - 120px)">
<oracle-dv id="myEmbed"
project-path="{{projectPath}}"
active-page="canvas"
active-tab-id="1">
</oracle-dv>
</div>

<script>
requirejs(['knockout', 'ojs/ojcore', 'ojs/ojknockout', 'ojs/ojcomposite', 'jet-composites/oracle-dv/loader'],
function(ko) {
function MyProject() {
var self = this;
self.projectPath = ko.observable("<PROJECT PATH>");
}
ko.applyBindings(MyProject);
}
);

var eventName = 'oracle.bitech.dataaction';
var element = document.getElementById("mydiv");

if (element) {
var oEventListener = element.addEventListener(eventName, function (e) {
console.log("***** Payload from DV ***** ");
console.log("eventName = " + e.detail.eventName);
console.log("Total payload = " + JSON.stringify(e.detail));
console.log("payload = " + JSON.stringify(e.detail.payload));
console.log("***** Payload from DV end ***** ");
var res = " Data Received from DV Content: ";

// Get the values of the row from where data action was invoked
Object.keys(e.detail.payload.context).forEach(function(key) {
res = res.concat(key);
res = res.concat(" : ");
var temp = e.detail.payload.context[key]["oValueMap"];
var temp1 = Object.keys(temp)[0];
res = res.concat(temp1);
res = res.concat(" | ");
});

//send data action context to the console and HTML page
console.log("Data Action Row Context : " + res);
document.getElementById('response_div').innerHTML = res;

// Get the values of the top level filter context for the canvas
console.log("***** Let's get the columns used in the top level filter and their values ***** ");

// check if any top level filter values exist
if ((Object.keys(e.detail.payload.contextv2._aFilterModels).length) > 0) {
var filterValsPrefix = "Values for filter context: ";
} else {
var filterValsPrefix = "There are no filters detected";
}

var filterVals = "";

// loop over the _aFilterModels array
Object.keys(e.detail.payload.contextv2._aFilterModels).forEach(function(key) {

console.log("Key Value :" + key + " filtervals string: " + filterVals);

const oFilterModel = e.detail.payload.contextv2._aFilterModels[key];
console.log(oFilterModel);

const sFilterType = oFilterModel["_sPluginType"];
console.log(sFilterType)

switch (sFilterType) {
case "oracle.bi.tech.listfilter": {
var sFilterValsReturned = _processListFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.numberrangefilter": {
var sFilterValsReturned = _processNumberRangeFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.topbottomfilter": {
var sFilterValsReturned = _processTopBottomFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.daterangefilter": {
var sFilterValsReturned = _processDateRangeFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.relativedatefilter": {
var sFilterValsReturned = _processRelativeDateFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
case "oracle.bi.tech.expressionfilter": {
var sFilterValsReturned = _processExpressionFilter(oFilterModel)
filterVals = filterVals.concat(sFilterValsReturned);
break;
}
default:
var sFilterValsReturned = _processUnknownFilter(oFilterModel);
filterVals = filterVals.concat(sFilterValsReturned);
}

});

//send top filter bar context to the console and HTML page
console.log(filterVals);
var filterValsReturn = filterValsPrefix + filterVals;
document.getElementById('filterBar_div').innerHTML = filterValsReturn;

}, true);
}


function _processListFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// **** CHECK IF INCLUSIVE OR EXCLUSIVE
if (pFilterModel["_bIsInclusive"] === true) {
pFilterValsString = pFilterValsString.concat(" Include ");
} else if (pFilterModel["_bIsInclusive"] === false) {
pFilterValsString = pFilterValsString.concat(" Exclude ");
}

// now get the values used in the filter
var aFilterValues = Object.values(pFilterModel["_aSelectedValues"]);

for (let i=0; i < aFilterValues.length; i++){
// add each filter value to the filterVals string separated by ,
pFilterValsString = pFilterValsString.concat(aFilterValues[i]._sDisplayValue +", ");
}
return pFilterValsString;
}



function _processNumberRangeFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the starting and endpoint
var filterRangeLimit1 = pFilterModel["_vLimit1"];
var filterRangeLimit2 = pFilterModel["_vLimit2"];
var filterRangeType = pFilterModel["_eRangeFilterType"];

//check if by all attributes where the _aByColumns would be empty
if (Object.keys(pFilterModel["_aByColumns"]).length === 0) {
// All attributes used in by clause since _aByColumns is empty
var rangeFilterByValues = " All Attributes ";

} else {
// There is a specific column or columns referenced in the by clause
var rangeFilterByValues = "";
var aRangeValues = Object.values(pFilterModel["_aByColumns"]);

for (let i=0; i < aRangeValues.length; i++){
// add each measure value to the filterVals string separated by ,
rangeFilterByValues = rangeFilterByValues.concat(aRangeValues[i]._sDisplayName +", ");
}
}

// Add to the list of filters
if (filterRangeType === "lessOrEqual" ){
pFilterValsString = pFilterValsString.concat("<= " + filterRangeLimit1 + " By " + rangeFilterByValues);
} else if (filterRangeType === "greaterOrEqual" ) {
pFilterValsString = pFilterValsString.concat(">= " + filterRangeLimit1 + " By " + rangeFilterByValues);
}
else {
pFilterValsString = pFilterValsString.concat("Between " + filterRangeLimit1 + " and " + filterRangeLimit2 + " By " + rangeFilterByValues );
}

return pFilterValsString;
}



function _processTopBottomFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the top or bottom mode and number
var filterTopOrBottom = pFilterModel["_eTopBottomMode"];
var filterTopOrBottomN = pFilterModel["_iNumberOfRankedItems"]

// *** This top bottom N could be based on a fact element or a data element
// check if _factElement exsits
if (pFilterModel.hasOwnProperty("_factElement")) {
// fact element exists get the measure used for the top or bottom N
var filterTopOrBottomMeasure = pFilterModel["_factElement"]["_sDisplayName"];
} else {
//fact element DOES NOT exist
//check if by all attributes where the _aByColumns would be empty

if (Object.keys(pFilterModel["_aByColumns"]).length === 0) {
// All attributes used in by clause since _aByColumns is empty
var filterTopOrBottomMeasure = " All Attributes ";

} else {
// There is a specific column or columns referenced in the by clause
var filterTopOrBottomMeasure = "";
var aTopBottomMeasures = Object.values(pFilterModel["_aByColumns"]);

for (let i=0; i < aTopBottomMeasures.length; i++){
// add each measure value to the filterVals string separated by ,
filterTopOrBottomMeasure = filterTopOrBottomMeasure.concat(aTopBottomMeasures[i]._sDisplayName +", ");
}
}
}

// Add to the list of filters but need to change order if _factElement exists or not
if (pFilterModel.hasOwnProperty("_factElement")) {
// fact element exists
// get the measure used for the top or bottom N
pFilterValsString = pFilterValsString.concat(filterTopOrBottom + " " + filterTopOrBottomN + " based on " + filterTopOrBottomMeasure);
} else {
//fact element DOES NOT exist
pFilterValsString = pFilterValsString.concat(filterTopOrBottom + " " + filterTopOrBottomN + " By " + filterTopOrBottomMeasure);
}
return pFilterValsString;
}



function _processDateRangeFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the range filter type
var filterRangeFilterType = pFilterModel["_eRangeFilterType"];
// Add to the list of filters
if (filterRangeFilterType === "between" ){
// get the starting and endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
var filterDateRangeEnd = pFilterModel["_vLimit2"];
//get difference in seconds between dates
var filterDateDiff = pFilterModel["_vLimit2"] - pFilterModel["_vLimit1"];
// Check if it's an equal to specific date
if (filterDateDiff === 0) {
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("Equal to " + filterDateRangeStart + " " );
} else if (filterDateDiff !== 0) {
// It is a date range - add to the list of filters
pFilterValsString = pFilterValsString.concat("From " + filterDateRangeStart + " To " + filterDateRangeEnd + " " );
}
} else if (filterRangeFilterType === "greaterOrEqual") {
// get the starting and endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("From " + filterDateRangeStart + " " );
} else if (filterRangeFilterType === "lessOrEqual") {
// get the endpoint
var filterDateRangeStart = pFilterModel["_vLimit1"];
// Add to the list of filters
pFilterValsString = pFilterValsString.concat("To " + filterDateRangeStart + " " );
}
else {
console.log("Unknown Date Range Filter Type detected")
}
return pFilterValsString;
}


function _processRelativeDateFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(pFilterModel["_oDataElement"]["_sDisplayName"]);
pFilterValsString = pFilterValsString.concat(" : ");

// get the range timelevel and number of periods
var filterDateRangeType = pFilterModel["_oRelativeDateDataModel"]["_eRangeType"];
var filterTimeLevel = pFilterModel["_oRelativeDateDataModel"]["_eTimeLevel"];
var filterNumberOfPeriods = pFilterModel["_oRelativeDateDataModel"]["_iNumberOfPeriods"];

// Add to the list of filters
pFilterValsString = pFilterValsString.concat(filterDateRangeType + " " + filterNumberOfPeriods + " " + filterTimeLevel + " Relative to Today");

return pFilterValsString;
}


function _processExpressionFilter(pFilterModel) {
// For expression filter there is no _sDisplayName property ********
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" | ");
pFilterValsString = pFilterValsString.concat(" : ");

// get the expression definition
var filterExpression = pFilterModel["_sExpression"];

// Add to the list of filters
pFilterValsString = pFilterValsString.concat(filterExpression);

return pFilterValsString;
}


function _processUnknownFilter(pFilterModel) {
pFilterValsString = "";
pFilterValsString = pFilterValsString.concat(" ** This sample code doesn't yet work with this filter type ** ");
return pFilterValsString;
}


</script>

</body>
</html>

--

--

Mike Durran
Oracle Developers

Analytics Product Manager at Oracle. [All content and opinions are my own]