8

Adding Opensearch-Dashboards/Kibana filters to Vega visuals

 8 months ago
source link: https://blog.davidvassallo.me/2023/09/08/adding-opensearch-dashboards-kibana-filters-to-vega-visuals/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Adding Opensearch-Dashboards/Kibana filters to Vega visuals

image-2.png?w=648&h=322&crop=1

image-2.png?w=1024

Opensearch-Dashboards and Kibana allow for the use of the “vega” library, which lets developers build an arbitrarily complex visualization. One of my favorite visuals which is not included in the standard visualization library is the “Sankey Diagram“. Since it’s not part and parcel of the preset charts offered by Opensearch Dashboards, we resort to Vega. Luckily, there is an excellent starting point here: https://www.elastic.co/blog/sankey-visualization-with-vega-in-kibana, which pretty much takes you 95% of the way.

But what if we’d like to embed the resulting Sankey diagram into our dashboard, and subsequently have our interactions with the vega Sankey diagram add and remove filters just like any other chart? This is our objective in this article.

So quick google search found someone with the same problem asking around the Elasticsearch forum here: https://discuss.elastic.co/t/update-kibana-filter-from-vega/295598, where they make reference to a “custom vega function” named KibanaAddfilter.

More googling for the equivalent of this in Opensearch leads us to: https://forum.opensearch.org/t/os2-3-integration-with-vega/15107, where we are helpfully given a look into the the code:

const vegaFunctions = {
opensearchDashboardsAddFilter: 'addFilterHandler',
opensearchDashboardsRemoveFilter: 'removeFilterHandler',
opensearchDashboardsRemoveAllFilters: 'removeAllFiltersHandler',
opensearchDashboardsSetTimeFilter: 'setTimeFilterHandler',
opensearchDashboardsVisEventTriggered: 'triggerExternalActionHandler',
};

So we know that the custom function equivalent is opensearchDashboardsAddFilter, and it maps to a function named addFilterHandler. Some more digging around in the code brings us to some more helpful comments as to what exactly we need to input into this function:

Code in Github

/**
* @param {object} query Query DSL snippet, as used in the query DSL editor
* @param {string} [index] as defined in OpenSearch Dashboards, or default if missing
* @param {string} alias OpenSearch Query DSL's custom label for `opensearchDashboardsAddFilter`, as used in '+ Add Filter'
*/
async addFilterHandler(query, index, alias) {
const indexId = await this.findIndex(Utils.handleNonStringIndex(index));
const filter = opensearchFilters.buildQueryFilter(
Utils.handleInvalidQuery(query),
indexId,
alias
);
this._applyFilter({ filters: [filter] });
}

We have all the information we need to go back to the original vega script and modify it so that when clicking on either side of the sankey diagram, we get a filter.

In vega-speak, “event handlers” are referred to as “signals“. Scrolling down to the very bottom of the original vega script, we see a couple of these signals already there:

// Clicking groupMark sets this signal to the filter values
events: @groupMark:click!
update: "..."

So we need to modify the “update” property in that object. Fromour previous research, we know that we have to use opensearchDashboardsAddFilter, which expects a DSL query… but how do we know which field to query on?

Aside: we modified the original sankey to show windows servers on the left, and windows event codes on the right

If we click on the left sankey column we’d like one field to be filtered, if we click on the right sankey column we’d like a different column filtered. Time to RTFM and see what signal “update” objects allow:

Vega Signals

image.png?w=834

Ok, so we need an “expression”: Vega Expressions. And right there in the first few lines we have our answer:

image-1.png?w=858

So we have all the pieces in place to write our “update” property:

update: "
datum.stack=='stk1' ?  //here we use basically an if clause to ask, is the click on 'stk1' or not?
opensearchDashboardsAddFilter({\"match_phrase\": { \"winlog.computer_name\": datum.grpId } }, 'winlogbeat-*')  // if yes, call the opensearchDashboardsAddFilter with one field, set to datum.grpId (the value we clicked on)
:
opensearchDashboardsAddFilter({\"match_phrase\": { \"event.code\": datum.grpId } }, 'winlogbeat-*')" // if not, call the opensearchDashboardsAddFilter with another field, though also set to datum.grpId
"

NB the above is split over several lines for readability and includes comments. Please remove all newlines and comments when using this otherwise vega will complain

Here’s what we end up with:

chrome_ybi7crtzp4.gif?w=1024

Full VEGA code here


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK