[Kibana > Timelion] Divide multi-valued seriesList by another multi-valued seriesList

Kibana Timelion Plugin Enhancement

Why need to divide multiple valued seriesList?

Timelion is quite a great plugin for computing two series in kibana (version:4.5.4). The divide computing supports divisor as a number or a single series. However there are still some cases that we want to divide multiple series over other multiple series. So developers file a issue: Divide multi-valued seriesList by another multi-valued seriesList

Note, this implementation is in both kibana 4.5.4 and Kibana 5.3.4. It works well.

Fix patch

Inspired by the discussion in Divide multi-valued seriesList by another multi-valued seriesList , I have come up with another fix (ugly but workable) based on reduce. I have tested its basic functions, and you are welcome to file issues if you find any bugs.

For Kibana 4.5.4
The fix added two files to timelion.

(divideseries)$ git status
On branch divideseries
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   server/lib/reduce_series.js
    new file:   server/series_functions/divideseries.js

For Kibana 5.3.4
The fix added two files to timelion.

(divideseries)$ git status
On branch divideseries
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    new file:   src/core_plugins/timelion/server/lib/reduce_series.js
    new file:   src/core_plugins/timelion/server/series_functions/divideseries.js

reduce_series.js

var _ = require('lodash');
var Promise = require('bluebird');


/**
 * Reduces multiple arrays into a single array using a function
 * @param {Array} args - args[0] must always be a {type: 'seriesList'}
 *
 * - If only arg[0] exists, the seriesList will be reduced to a seriesList containing a single series
 * - If multiple arguments are passed, each argument will be mapped onto each series in the seriesList.

 * @params {Function} fn - Function used to combine points at same index in each array of each series in the seriesList.
 * @return {seriesList}
 */
module.exports = function reduceMultiple(args, fn) {
  return Promise.all(args).then(function (args) {

    var seriesList = args.shift();
    var argumentList = args.shift();

    if (seriesList.type !== 'seriesList') {
      throw new Error ('input must be a seriesList');
    }

    /*
    if (_.isObject(argument) && argument.type === 'seriesList') {
      if (argument.list.length !== 1) {
        throw new Error ('argument must be a seriesList with a single series');
      } else {
        argument = argument.list[0];
      }
    }
    */
   argumentList = argumentList.list;

    /**
     * get the group field
     */
    function getGroupField (destObj) {
      var label = destObj.label;
      var split = label.split(' > ');
      return split[split.length-2];
    }

    function getCorrespondingArg (destinationObject, argumentList) {
      var destGroup = getGroupField(destinationObject);
      for (var i = 0; i < argumentList.length; i++) {
        if (destGroup === getGroupField(argumentList[i])) {
          return argumentList[i];
        }
      }
      return undefined;
    }

    function reduceSeries(series) {
      return _.reduce(series, function (destinationObject, argument, i, p) {

        var output = _.map(destinationObject.data, function (point, index) {

          var value = point[1];

          if (value == null) {
            return [point[0], null];
          }

          if (_.isNumber(argument)) {
            return [point[0], fn(value, argument, i, p)];
          }

          if (argument.data[index] == null || argument.data[index][1] == null) {
            return [point[0], null];
          }
          return [point[0], fn(value, argument.data[index][1], i, p)];
        });

        // Output = single series

        output = {
          data: output
        };
        output = _.defaults(output, destinationObject);
        return output;

      });

    }

    var reduced;

    if (argumentList != null) {
      reduced = _.map(seriesList.list, function (series) {
        var argument = getCorrespondingArg(series, argumentList);
        if (!argument) return undefined;
        return reduceSeries([series].concat(argument));
      });
    } else {
      reduced = [reduceSeries(seriesList.list)];
    }

    seriesList.list = [];
    for (var i = 0; i < reduced.length; i++) {
      if (reduced[i] === undefined) continue;
      seriesList.list.push(reduced[i]);
    }
    return seriesList;

  }).catch(function (e) {
    throw e;
  });
};

divideseries.js

var reduceSeries = require('../lib/reduce_series.js');

var Chainable = require('../lib/classes/chainable');
module.exports = new Chainable('divideseries', {
  args: [
    {
      name: 'inputSeries',
      types: ['seriesList']
    },
    {
      name: 'divisor',
      types: ['seriesList', 'number'],
      help: 'Series to divide by. It should have the same split field as the the divider. If a seriesList in divider cannot find the same group field in divisor list, then this seriesList will be ignored.'
    }
  ],
 help: 'Series to divide by. It should have the same split field as the the divider. If a seriesList in divider cannot find the same group field in divisor list, then this seriesList will be ignored.',
  fn: function divideSeriesFn(args) {
    return reduceSeries (args, function (a, b) {
      return a / b;
    });
  }
});

Demo

I tested with the following results:
Query

.es('query1', split='cdn:2').divideseries(.es('query2', split='cdn:2'))

Result
DivdeSeries Demo

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值