コード例 #1
0
  function loadViewBySwimlaneForSelectedTime(earliestMs, latestMs) {
    const selectedJobIds = $scope.getSelectedJobIds();
    const limit = mlSelectLimitService.state.get('limit');
    const swimlaneLimit = (limit === undefined) ? SWIMLANE_DEFAULT_LIMIT : limit.val;

    // Find the top field values for the selected time, and then load the 'view by'
    // swimlane over the full time range for those specific field values.
    if ($scope.swimlaneViewByFieldName !== VIEW_BY_JOB_LABEL) {
      mlResultsService.getTopInfluencers(
        selectedJobIds,
        earliestMs,
        latestMs,
        swimlaneLimit
      ).then((resp) => {
        const topFieldValues = [];
        const topInfluencers = resp.influencers[$scope.swimlaneViewByFieldName];
        _.each(topInfluencers, (influencerData) => {
          if (influencerData.maxAnomalyScore > 0) {
            topFieldValues.push(influencerData.influencerFieldValue);
          }
        });
        loadViewBySwimlane(topFieldValues);
      });
    } else {
      mlResultsService.getScoresByBucket(
        selectedJobIds,
        earliestMs,
        latestMs,
        $scope.swimlaneBucketInterval.asSeconds() + 's',
        swimlaneLimit
      ).then((resp) => {
        loadViewBySwimlane(_.keys(resp.results));
      });
    }
  }
コード例 #2
0
ファイル: explorer_utils.js プロジェクト: spalger/kibana
  return new Promise((resolve) => {
    // Just skip doing the request when this function
    // is called without the minimum required data.
    if (selectedCells === null && influencers.length === 0 && influencersFilterQuery === undefined) {
      resolve([]);
    }

    const newRequestCount = ++requestCount;
    requestCount = newRequestCount;

    // Load the top anomalies (by record_score) which will be displayed in the charts.
    mlResultsService.getRecordsForInfluencer(
      jobIds, influencers, 0, earliestMs, latestMs, 500, influencersFilterQuery
    )
      .then((resp) => {
        // Ignore this response if it's returned by an out of date promise
        if (newRequestCount < requestCount) {
          resolve(undefined);
        }

        if ((selectedCells !== null && Object.keys(selectedCells).length > 0) ||
          influencersFilterQuery !== undefined) {
          console.log('Explorer anomaly charts data set:', resp.records);
          resolve(resp.records);
        }

        resolve(undefined);
      });
  });
コード例 #3
0
  function loadEntityValues() {
    // Populate the entity input datalists with the values from the top records by score
    // for the selected detector across the full time range. No need to pass through finish().
    const bounds = timefilter.getActiveBounds();
    const detectorIndex = +$scope.detectorId;

    mlResultsService.getRecordsForCriteria(
      [$scope.selectedJob.job_id],
      [{ 'fieldName': 'detector_index', 'fieldValue': detectorIndex }],
      0,
      bounds.min.valueOf(),
      bounds.max.valueOf(),
      ANOMALIES_MAX_RESULTS)
      .then((resp) => {
        if (resp.records && resp.records.length > 0) {
          const firstRec = resp.records[0];

          _.each($scope.entities, (entity) => {
            if (firstRec.partition_field_name === entity.fieldName) {
              entity.fieldValues = _.chain(resp.records).pluck('partition_field_value').uniq().value();
            }
            if (firstRec.over_field_name === entity.fieldName) {
              entity.fieldValues = _.chain(resp.records).pluck('over_field_value').uniq().value();
            }
            if (firstRec.by_field_name === entity.fieldName) {
              entity.fieldValues = _.chain(resp.records).pluck('by_field_value').uniq().value();
            }
          });
        }

      });
  }
コード例 #4
0
    return new Promise((resolve, reject) => {
      const obj = {
        success: true,
        results: {}
      };

      const chartConfig = buildConfigFromDetector(job, detectorIndex);

      mlResultsService.getMetricData(
        chartConfig.datafeedConfig.indices,
        entityFields,
        chartConfig.datafeedConfig.query,
        chartConfig.metricFunction,
        chartConfig.metricFieldName,
        chartConfig.timeField,
        earliestMs,
        latestMs,
        interval
      )
        .then((resp) => {
          _.each(resp.results, (value, time) => {
            obj.results[time] = {
              'actual': value
            };
          });

          resolve(obj);
        })
        .catch((resp) => {
          reject(resp);
        });

    });
コード例 #5
0
  function loadOverallData() {
    // Loads the overall data components i.e. the overall swimlane and influencers list.

    if ($scope.selectedJobs === null) {
      return;
    }

    $scope.loading = true;
    $scope.hasResults = false;

    $scope.swimlaneBucketInterval = calculateSwimlaneBucketInterval();
    console.log('Explorer swimlane bucketInterval:', $scope.swimlaneBucketInterval);

    // Ensure the search bounds align to the bucketing interval used in the swimlane so
    // that the first and last buckets are complete.
    const bounds = timefilter.getActiveBounds();
    const searchBounds = getBoundsRoundedToInterval(bounds, $scope.swimlaneBucketInterval, false);
    const selectedJobIds = $scope.getSelectedJobIds();

    // Load the overall bucket scores by time.
    // Pass the interval in seconds as the swimlane relies on a fixed number of seconds between buckets
    // which wouldn't be the case if e.g. '1M' was used.
    // Pass 'true' when obtaining bucket bounds due to the way the overall_buckets endpoint works
    // to ensure the search is inclusive of end time.
    const overallBucketsBounds = getBoundsRoundedToInterval(bounds, $scope.swimlaneBucketInterval, true);
    mlResultsService.getOverallBucketScores(
      selectedJobIds,
      // Note there is an optimization for when top_n == 1.
      // If top_n > 1, we should test what happens when the request takes long
      // and refactor the loading calls, if necessary, to avoid delays in loading other components.
      1,
      overallBucketsBounds.min.valueOf(),
      overallBucketsBounds.max.valueOf(),
      $scope.swimlaneBucketInterval.asSeconds() + 's'
    ).then((resp) => {
      skipCellClicks = false;
      $scope.overallSwimlaneData = processOverallResults(resp.results, searchBounds);
      console.log('Explorer overall swimlane data set:', $scope.overallSwimlaneData);

      if ($scope.overallSwimlaneData.points && $scope.overallSwimlaneData.points.length > 0) {
        $scope.hasResults = true;

        // Trigger loading of the 'view by' swimlane -
        // only load once the overall swimlane so that we can match the time span.
        setViewBySwimlaneOptions();
      } else {
        $scope.hasResults = false;
      }
      $scope.loading = false;

      // Tell the result components directives to render.
      // Need to use $timeout to ensure the broadcast happens after the child scope is updated with the new data.
      $timeout(() => {
        loadViewBySwimlane([]);
      }, 0);
    });

  }
コード例 #6
0
ファイル: explorer.js プロジェクト: elastic/kibana
      return new Promise((resolve) => {
        if (_.isEqual(compareArgs, this.topFieldsPreviousArgs)) {
          resolve(this.topFieldsPreviousData);
          return;
        }
        this.topFieldsPreviousArgs = compareArgs;

        if (swimlaneViewByFieldName !== VIEW_BY_JOB_LABEL) {
          mlResultsService.getTopInfluencers(
            selectedJobIds,
            earliestMs,
            latestMs,
            swimlaneLimit
          ).then((resp) => {
            if (resp.influencers[swimlaneViewByFieldName] === undefined) {
              this.topFieldsPreviousData = [];
              resolve([]);
            }

            const topFieldValues = [];
            const topInfluencers = resp.influencers[swimlaneViewByFieldName];
            topInfluencers.forEach((influencerData) => {
              if (influencerData.maxAnomalyScore > 0) {
                topFieldValues.push(influencerData.influencerFieldValue);
              }
            });
            this.topFieldsPreviousData = topFieldValues;
            resolve(topFieldValues);
          });
        } else {
          mlResultsService.getScoresByBucket(
            selectedJobIds,
            earliestMs,
            latestMs,
            this.getSwimlaneBucketInterval(selectedJobs).asSeconds() + 's',
            swimlaneLimit
          ).then((resp) => {
            const topFieldValues = Object.keys(resp.results);
            this.topFieldsPreviousData = topFieldValues;
            resolve(topFieldValues);
          });
        }
      });
コード例 #7
0
ファイル: create_job_service.js プロジェクト: elastic/kibana
      return new Promise((resolve, reject) => {

        let start = formConfig.start;

        if (this.chartData.model.length > 5) {
          // only load the model since the end of the last time we checked
          // but discard the last 5 buckets in case the model has changed
          start = this.chartData.model[this.chartData.model.length - 5].time;
          for (let i = 0; i < 5; i++) {
            this.chartData.model.pop();
          }
        }

        // Obtain the model plot data, passing 0 for the detectorIndex and empty list of partitioning fields.
        mlResultsService.getModelPlotOutput(
          formConfig.jobId,
          0,
          [],
          start,
          formConfig.end,
          formConfig.resultsIntervalSeconds + 's',
          formConfig.agg.type.mlModelPlotAgg
        )
          .then(data => {
          // for count, scale the model upper and lower by the
          // ratio of chart interval to bucketspan.
          // this will force the model bounds to be drawn in the correct location
            let scale = 1;
            if (formConfig &&
            (formConfig.agg.type.mlName === 'count' ||
            formConfig.agg.type.mlName === 'high_count' ||
            formConfig.agg.type.mlName === 'low_count' ||
            formConfig.agg.type.mlName === 'distinct_count')) {
              const chartIntervalSeconds = formConfig.chartInterval.getInterval().asSeconds();
              const bucketSpan = parseInterval(formConfig.bucketSpan);
              if (bucketSpan !== null) {
                scale =  chartIntervalSeconds / bucketSpan.asSeconds();
              }
            }

            this.chartData.model = this.chartData.model.concat(processLineChartResults(data.results, scale));

            const lastBucket = this.chartData.model[this.chartData.model.length - 1];
            const time = (lastBucket !== undefined) ? lastBucket.time : formConfig.start;

            const pcnt = ((time -  formConfig.start + formConfig.resultsIntervalSeconds) / (formConfig.end - formConfig.start) * 100);
            this.chartData.percentComplete = Math.round(pcnt);

            resolve(this.chartData);
          })
          .catch(() => {
            reject(this.chartData);
          });

      });
コード例 #8
0
ファイル: explorer_utils.js プロジェクト: spalger/kibana
 return new Promise((resolve) => {
   if (noInfluencersConfigured !== true) {
     mlResultsService.getTopInfluencers(
       selectedJobIds,
       earliestMs,
       latestMs,
       MAX_INFLUENCER_FIELD_VALUES,
       influencers,
       influencersFilterQuery
     ).then((resp) => {
       // TODO - sort the influencers keys so that the partition field(s) are first.
       console.log('Explorer top influencers data set:', resp.influencers);
       resolve(resp.influencers);
     });
   } else {
     resolve({});
   }
 });
コード例 #9
0
 function loadTopInfluencers(selectedJobIds, earliestMs, latestMs, influencers = []) {
   if ($scope.noInfluencersConfigured !== true) {
     mlResultsService.getTopInfluencers(
       selectedJobIds,
       earliestMs,
       latestMs,
       MAX_INFLUENCER_FIELD_VALUES,
       influencers
     ).then((resp) => {
       // TODO - sort the influencers keys so that the partition field(s) are first.
       $scope.influencers = resp.influencers;
       $scope.$applyAsync();
       console.log('Explorer top influencers data set:', $scope.influencers);
     });
   } else {
     $scope.influencers = {};
     $scope.$applyAsync();
   }
 }
コード例 #10
0
ファイル: create_job_service.js プロジェクト: elastic/kibana
      return new Promise((resolve) => {

        mlResultsService.getScoresByBucket(
          [formConfig.jobId],
          formConfig.start,
          formConfig.end,
          formConfig.resultsIntervalSeconds + 's',
          1
        )
          .then((data) => {
            const jobResults = data.results[formConfig.jobId];
            this.chartData.swimlane = processSwimlaneResults(jobResults);
            this.chartData.swimlaneInterval = formConfig.resultsIntervalSeconds * 1000;
            resolve(this.chartData);
          })
          .catch(() => {
            resolve(this.chartData);
          });

      });
コード例 #11
0
  function loadDataForCharts(jobIds, earliestMs, latestMs, influencers = []) {
    // Just skip doing the request when this function
    // is called without the minimum required data.
    if ($scope.cellData === undefined && influencers.length === 0) {
      return;
    }

    const newRequestCount = ++requestCount;
    requestCount = newRequestCount;

    // Load the top anomalies (by record_score) which will be displayed in the charts.
    mlResultsService.getRecordsForInfluencer(
      jobIds, influencers, 0, earliestMs, latestMs, 500
    )
      .then((resp) => {
        // Ignore this response if it's returned by an out of date promise
        if (newRequestCount < requestCount) {
          return;
        }

        if ($scope.cellData !== undefined && _.keys($scope.cellData).length > 0) {
          $scope.anomalyChartRecords = resp.records;
          console.log('Explorer anomaly charts data set:', $scope.anomalyChartRecords);

          if (mlCheckboxShowChartsService.state.get('showCharts')) {
            anomalyDataChange(
              $scope.anomalyChartRecords, earliestMs, latestMs
            );
          }
        }

        // While the charts were loaded, other events could reset cellData,
        // so check if it's still present. This can happen if a cell selection
        // gets restored from URL/AppState and we find out it's not applicable
        // to the view by swimlanes currently on display.
        if ($scope.cellData === undefined) {
          return;
        }

        if (influencers.length > 0) {
          // Filter the Top Influencers list to show just the influencers from
          // the records in the selected time range.
          const recordInfluencersByName = {};

          // Add the specified influencer(s) to ensure they are used in the filter
          // even if their influencer score for the selected time range is zero.
          influencers.forEach((influencer) => {
            const fieldName = influencer.fieldName;
            if (recordInfluencersByName[influencer.fieldName] === undefined) {
              recordInfluencersByName[influencer.fieldName] = [];
            }
            recordInfluencersByName[fieldName].push(influencer.fieldValue);
          });

          // Add the influencers from the top scoring anomalies.
          resp.records.forEach((record) => {
            const influencersByName = record.influencers || [];
            influencersByName.forEach((influencer) => {
              const fieldName = influencer.influencer_field_name;
              const fieldValues = influencer.influencer_field_values;
              if (recordInfluencersByName[fieldName] === undefined) {
                recordInfluencersByName[fieldName] = [];
              }
              recordInfluencersByName[fieldName].push(...fieldValues);
            });
          });

          const uniqValuesByName = {};
          Object.keys(recordInfluencersByName).forEach((fieldName) => {
            const fieldValues = recordInfluencersByName[fieldName];
            uniqValuesByName[fieldName] = _.uniq(fieldValues);
          });

          const filterInfluencers = [];
          Object.keys(uniqValuesByName).forEach((fieldName) => {
            // Find record influencers with the same field name as the clicked on cell(s).
            const matchingFieldName = influencers.find((influencer) => {
              return influencer.fieldName === fieldName;
            });

            if (matchingFieldName !== undefined) {
              // Filter for the value(s) of the clicked on cell(s).
              filterInfluencers.push(...influencers);
            } else {
              // For other field names, add values from all records.
              uniqValuesByName[fieldName].forEach((fieldValue) => {
                filterInfluencers.push({ fieldName, fieldValue });
              });
            }
          });

          loadTopInfluencers(jobIds, earliestMs, latestMs, filterInfluencers);
        }
        $scope.$applyAsync();
      });
  }
コード例 #12
0
  $scope.refreshFocusData = function (fromDate, toDate) {

    // Counter to keep track of the queries to populate the chart.
    let awaitingCount = 3;

    // This object is used to store the results of individual remote requests
    // before we transform it into the final data and apply it to $scope. Otherwise
    // we might trigger multiple $digest cycles and depending on how deep $watches
    // listen for changes we could miss updates.
    const refreshFocusData = {};

    // finish() function, called after each data set has been loaded and processed.
    // The last one to call it will trigger the page render.
    function finish() {
      awaitingCount--;
      if (awaitingCount === 0) {
        // Tell the results container directives to render the focus chart.
        refreshFocusData.focusChartData = processDataForFocusAnomalies(
          refreshFocusData.focusChartData,
          refreshFocusData.anomalyRecords,
          $scope.timeFieldName);

        refreshFocusData.focusChartData = processScheduledEventsForChart(
          refreshFocusData.focusChartData,
          refreshFocusData.scheduledEvents);

        // All the data is ready now for a scope update.
        // Use $evalAsync to ensure the update happens after the child scope is updated with the new data.
        $scope.$evalAsync(() => {
          $scope = Object.assign($scope, refreshFocusData);
          console.log('Time series explorer focus chart data set:', $scope.focusChartData);

          $scope.loading = false;
        });
      }
    }

    const detectorIndex = +$scope.detectorId;
    const nonBlankEntities = _.filter($scope.entities, entity => entity.fieldValue.length > 0);

    // Calculate the aggregation interval for the focus chart.
    const bounds = { min: moment(fromDate), max: moment(toDate) };
    $scope.focusAggregationInterval = calculateAggregationInterval(bounds, CHARTS_POINT_TARGET, CHARTS_POINT_TARGET);

    // Ensure the search bounds align to the bucketing interval so that the first and last buckets are complete.
    // For sum or count detectors, short buckets would hold smaller values, and model bounds would also be affected
    // to some extent with all detector functions if not searching complete buckets.
    const searchBounds = getBoundsRoundedToInterval(bounds, $scope.focusAggregationInterval, false);

    // Query 1 - load metric data across selected time range.
    mlTimeSeriesSearchService.getMetricData(
      $scope.selectedJob,
      detectorIndex,
      nonBlankEntities,
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf(),
      $scope.focusAggregationInterval.expression
    ).then((resp) => {
      refreshFocusData.focusChartData = processMetricPlotResults(resp.results, $scope.modelPlotEnabled);
      $scope.showModelBoundsCheckbox = ($scope.modelPlotEnabled === true) &&
        (refreshFocusData.focusChartData.length > 0);
      finish();
    }).catch((resp) => {
      console.log('Time series explorer - error getting metric data from elasticsearch:', resp);
    });

    // Query 2 - load all the records across selected time range for the chart anomaly markers.
    mlResultsService.getRecordsForCriteria(
      [$scope.selectedJob.job_id],
      $scope.criteriaFields,
      0,
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf(),
      ANOMALIES_MAX_RESULTS
    ).then((resp) => {
      // Sort in descending time order before storing in scope.
      refreshFocusData.anomalyRecords = _.chain(resp.records)
        .sortBy(record => record[$scope.timeFieldName])
        .reverse()
        .value();
      console.log('Time series explorer anomalies:', refreshFocusData.anomalyRecords);
      finish();
    });

    // Query 3 - load any scheduled events for the selected job.
    mlResultsService.getScheduledEventsByBucket(
      [$scope.selectedJob.job_id],
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf(),
      $scope.focusAggregationInterval.expression,
      1,
      MAX_SCHEDULED_EVENTS
    ).then((resp) => {
      refreshFocusData.scheduledEvents = resp.events[$scope.selectedJob.job_id];
      finish();
    }).catch((resp) => {
      console.log('Time series explorer - error getting scheduled events from elasticsearch:', resp);
    });

    // Plus query for forecast data if there is a forecastId stored in the appState.
    const forecastId = _.get($scope, 'appState.mlTimeSeriesExplorer.forecastId');
    if (forecastId !== undefined) {
      awaitingCount++;
      let aggType = undefined;
      const detector = $scope.selectedJob.analysis_config.detectors[detectorIndex];
      const esAgg = mlFunctionToESAggregation(detector.function);
      if ($scope.modelPlotEnabled === false && (esAgg === 'sum' || esAgg === 'count')) {
        aggType = { avg: 'sum', max: 'sum', min: 'sum' };
      }

      mlForecastService.getForecastData(
        $scope.selectedJob,
        detectorIndex,
        forecastId,
        nonBlankEntities,
        searchBounds.min.valueOf(),
        searchBounds.max.valueOf(),
        $scope.focusAggregationInterval.expression,
        aggType)
        .then((resp) => {
          refreshFocusData.focusForecastData = processForecastResults(resp.results);
          refreshFocusData.showForecastCheckbox = (refreshFocusData.focusForecastData.length > 0);
          finish();
        }).catch((resp) => {
          console.log(`Time series explorer - error loading data for forecast ID ${forecastId}`, resp);
        });
    }

    // Load the data for the anomalies table.
    loadAnomaliesTableData(searchBounds.min.valueOf(), searchBounds.max.valueOf());

  };
コード例 #13
0
  $scope.refresh = function () {

    if ($scope.selectedJob === undefined) {
      return;
    }

    $scope.loading = true;
    $scope.hasResults = false;
    delete $scope.chartDetails;
    delete $scope.contextChartData;
    delete $scope.focusChartData;
    delete $scope.contextForecastData;
    delete $scope.focusForecastData;

    // Counter to keep track of what data sets have been loaded.
    $scope.loadCounter++;
    let awaitingCount = 3;

    // finish() function, called after each data set has been loaded and processed.
    // The last one to call it will trigger the page render.
    function finish(counterVar) {
      awaitingCount--;
      if (awaitingCount === 0 && (counterVar === $scope.loadCounter)) {

        if (($scope.contextChartData && $scope.contextChartData.length) ||
          ($scope.contextForecastData && $scope.contextForecastData.length)) {
          $scope.hasResults = true;
        } else {
          $scope.hasResults = false;
        }
        $scope.loading = false;

        // Set zoomFrom/zoomTo attributes in scope which will result in the metric chart automatically
        // selecting the specified range in the context chart, and so loading that date range in the focus chart.
        if ($scope.contextChartData.length) {
          const focusRange = calculateInitialFocusRange();
          $scope.zoomFrom = focusRange[0];
          $scope.zoomTo = focusRange[1];
        }

        // Tell the results container directives to render.
        // Need to use $timeout to ensure the broadcast happens after the child scope is updated with the new data.
        if (($scope.contextChartData && $scope.contextChartData.length) ||
          ($scope.contextForecastData && $scope.contextForecastData.length)) {
          $timeout(() => {
            $scope.$broadcast('render');
          }, 0);
        }

      }
    }

    const bounds = timefilter.getActiveBounds();

    const detectorIndex = +$scope.detectorId;
    $scope.modelPlotEnabled = isModelPlotEnabled($scope.selectedJob, detectorIndex, $scope.entities);

    // Only filter on the entity if the field has a value.
    const nonBlankEntities = _.filter($scope.entities, (entity) => { return entity.fieldValue.length > 0; });
    $scope.criteriaFields = [{
      'fieldName': 'detector_index',
      'fieldValue': detectorIndex }
    ].concat(nonBlankEntities);

    // Calculate the aggregation interval for the context chart.
    // Context chart swimlane will display bucket anomaly score at the same interval.
    $scope.contextAggregationInterval = calculateAggregationInterval(bounds, CHARTS_POINT_TARGET, CHARTS_POINT_TARGET);
    console.log('aggregationInterval for context data (s):', $scope.contextAggregationInterval.asSeconds());

    // Ensure the search bounds align to the bucketing interval so that the first and last buckets are complete.
    // For sum or count detectors, short buckets would hold smaller values, and model bounds would also be affected
    // to some extent with all detector functions if not searching complete buckets.
    const searchBounds = getBoundsRoundedToInterval(bounds, $scope.contextAggregationInterval, false);

    // Query 1 - load metric data at low granularity across full time range.
    // Pass a counter flag into the finish() function to make sure we only process the results
    // for the most recent call to the load the data in cases where the job selection and time filter
    // have been altered in quick succession (such as from the job picker with 'Apply time range').
    const counter = $scope.loadCounter;
    mlTimeSeriesSearchService.getMetricData(
      $scope.selectedJob,
      detectorIndex,
      nonBlankEntities,
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf(),
      $scope.contextAggregationInterval.expression
    ).then((resp) => {
      const fullRangeChartData = processMetricPlotResults(resp.results, $scope.modelPlotEnabled);
      $scope.contextChartData = fullRangeChartData;
      console.log('Time series explorer context chart data set:', $scope.contextChartData);

      finish(counter);
    }).catch((resp) => {
      console.log('Time series explorer - error getting metric data from elasticsearch:', resp);
    });

    // Query 2 - load max record score at same granularity as context chart
    // across full time range for use in the swimlane.
    mlResultsService.getRecordMaxScoreByTime(
      $scope.selectedJob.job_id,
      $scope.criteriaFields,
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf(),
      $scope.contextAggregationInterval.expression
    ).then((resp) => {
      const fullRangeRecordScoreData = processRecordScoreResults(resp.results);
      $scope.swimlaneData = fullRangeRecordScoreData;
      console.log('Time series explorer swimlane anomalies data set:', $scope.swimlaneData);

      finish(counter);
    }).catch((resp) => {
      console.log('Time series explorer - error getting bucket anomaly scores from elasticsearch:', resp);
    });

    // Query 3 - load details on the chart used in the chart title (charting function and entity(s)).
    mlTimeSeriesSearchService.getChartDetails(
      $scope.selectedJob,
      detectorIndex,
      $scope.entities,
      searchBounds.min.valueOf(),
      searchBounds.max.valueOf()
    ).then((resp) => {
      $scope.chartDetails = resp.results;
      finish(counter);
    }).catch((resp) => {
      console.log('Time series explorer - error getting entity counts from elasticsearch:', resp);
    });

    // Plus query for forecast data if there is a forecastId stored in the appState.
    const forecastId = _.get($scope, 'appState.mlTimeSeriesExplorer.forecastId');
    if (forecastId !== undefined) {
      awaitingCount++;
      let aggType = undefined;
      const detector = $scope.selectedJob.analysis_config.detectors[detectorIndex];
      const esAgg = mlFunctionToESAggregation(detector.function);
      if ($scope.modelPlotEnabled === false && (esAgg === 'sum' || esAgg === 'count')) {
        aggType = { avg: 'sum', max: 'sum', min: 'sum' };
      }
      mlForecastService.getForecastData(
        $scope.selectedJob,
        detectorIndex,
        forecastId,
        nonBlankEntities,
        searchBounds.min.valueOf(),
        searchBounds.max.valueOf(),
        $scope.contextAggregationInterval.expression,
        aggType)
        .then((resp) => {
          $scope.contextForecastData = processForecastResults(resp.results);
          finish(counter);
        }).catch((resp) => {
          console.log(`Time series explorer - error loading data for forecast ID ${forecastId}`, resp);
        });
    }

    loadEntityValues();
  };
コード例 #14
0
function getMetricData(job, detectorIndex, entityFields, earliestMs, latestMs, interval) {
  if (isModelPlotEnabled(job, detectorIndex, entityFields)) {
    // Extract the partition, by, over fields on which to filter.
    const criteriaFields = [];
    const detector = job.analysis_config.detectors[detectorIndex];
    if (_.has(detector, 'partition_field_name')) {
      const partitionEntity = _.find(entityFields, { 'fieldName': detector.partition_field_name });
      if (partitionEntity !== undefined) {
        criteriaFields.push(
          { fieldName: 'partition_field_name', fieldValue: partitionEntity.fieldName },
          { fieldName: 'partition_field_value', fieldValue: partitionEntity.fieldValue });
      }
    }

    if (_.has(detector, 'over_field_name')) {
      const overEntity = _.find(entityFields, { 'fieldName': detector.over_field_name });
      if (overEntity !== undefined) {
        criteriaFields.push(
          { fieldName: 'over_field_name', fieldValue: overEntity.fieldName },
          { fieldName: 'over_field_value', fieldValue: overEntity.fieldValue });
      }
    }

    if (_.has(detector, 'by_field_name')) {
      const byEntity = _.find(entityFields, { 'fieldName': detector.by_field_name });
      if (byEntity !== undefined) {
        criteriaFields.push(
          { fieldName: 'by_field_name', fieldValue: byEntity.fieldName },
          { fieldName: 'by_field_value', fieldValue: byEntity.fieldValue });
      }
    }

    return mlResultsService.getModelPlotOutput(
      job.job_id,
      detectorIndex,
      criteriaFields,
      earliestMs,
      latestMs,
      interval
    );
  } else {
    return new Promise((resolve, reject) => {
      const obj = {
        success: true,
        results: {}
      };

      const chartConfig = buildConfigFromDetector(job, detectorIndex);

      mlResultsService.getMetricData(
        chartConfig.datafeedConfig.indices,
        entityFields,
        chartConfig.datafeedConfig.query,
        chartConfig.metricFunction,
        chartConfig.metricFieldName,
        chartConfig.timeField,
        earliestMs,
        latestMs,
        interval
      )
        .then((resp) => {
          _.each(resp.results, (value, time) => {
            obj.results[time] = {
              'actual': value
            };
          });

          resolve(obj);
        })
        .catch((resp) => {
          reject(resp);
        });

    });
  }
}
コード例 #15
0
ファイル: explorer.js プロジェクト: elastic/kibana
      return new Promise((resolve) => {
        this.skipCellClicks = true;

        // check if we can just return existing cached data
        if (_.isEqual(compareArgs, this.loadViewBySwimlanePreviousArgs)) {
          this.skipCellClicks = false;

          resolve({
            viewBySwimlaneData: this.loadViewBySwimlanePreviousData,
            viewBySwimlaneDataLoading: false
          });
          return;
        }

        this.setState({
          viewBySwimlaneData: getDefaultViewBySwimlaneData(),
          viewBySwimlaneDataLoading: true
        });

        const finish = (resp) => {
          this.skipCellClicks = false;
          if (resp !== undefined) {
            const viewBySwimlaneData = processViewByResults(
              resp.results,
              fieldValues,
              overallSwimlaneData,
              swimlaneViewByFieldName,
              this.getSwimlaneBucketInterval(selectedJobs).asSeconds(),
            );
            this.loadViewBySwimlanePreviousArgs = compareArgs;
            this.loadViewBySwimlanePreviousData = viewBySwimlaneData;
            console.log('Explorer view by swimlane data set:', viewBySwimlaneData);

            resolve({
              viewBySwimlaneData,
              viewBySwimlaneDataLoading: false
            });
          } else {
            resolve({ viewBySwimlaneDataLoading: false });
          }
        };

        if (
          selectedJobs === undefined ||
          swimlaneViewByFieldName === undefined
        ) {
          finish();
          return;
        } else {
          // Ensure the search bounds align to the bucketing interval used in the swimlane so
          // that the first and last buckets are complete.
          const bounds = timefilter.getActiveBounds();
          const searchBounds = getBoundsRoundedToInterval(
            bounds,
            this.getSwimlaneBucketInterval(selectedJobs),
            false,
          );
          const selectedJobIds = selectedJobs.map(d => d.id);

          // load scores by influencer/jobId value and time.
          // Pass the interval in seconds as the swimlane relies on a fixed number of seconds between buckets
          // which wouldn't be the case if e.g. '1M' was used.
          const interval = `${this.getSwimlaneBucketInterval(selectedJobs).asSeconds()}s`;
          if (swimlaneViewByFieldName !== VIEW_BY_JOB_LABEL) {
            mlResultsService.getInfluencerValueMaxScoreByTime(
              selectedJobIds,
              swimlaneViewByFieldName,
              fieldValues,
              searchBounds.min.valueOf(),
              searchBounds.max.valueOf(),
              interval,
              swimlaneLimit,
              influencersFilterQuery
            ).then(finish);
          } else {
            const jobIds = (fieldValues !== undefined && fieldValues.length > 0) ? fieldValues : selectedJobIds;
            mlResultsService.getScoresByBucket(
              jobIds,
              searchBounds.min.valueOf(),
              searchBounds.max.valueOf(),
              interval,
              swimlaneLimit
            ).then(finish);
          }
        }
      });
コード例 #16
0
ファイル: explorer.js プロジェクト: elastic/kibana
      return new Promise((resolve) => {
        // Loads the overall data components i.e. the overall swimlane and influencers list.
        if (selectedJobs === null) {
          resolve({
            loading: false,
            hasResuts: false
          });
          return;
        }

        // check if we can just return existing cached data
        const compareArgs = {
          selectedJobs,
          intervalAsSeconds: interval.asSeconds(),
          boundsMin: bounds.min.valueOf(),
          boundsMax: bounds.max.valueOf(),
        };

        if (_.isEqual(compareArgs, this.loadOverallDataPreviousArgs)) {
          const overallSwimlaneData = this.loadOverallDataPreviousData;
          const hasResults = (overallSwimlaneData.points && overallSwimlaneData.points.length > 0);
          resolve({
            hasResults,
            loading: false,
            overallSwimlaneData,
          });
          return;
        }

        if (showLoadingIndicator) {
          this.setState({ hasResults: false, loading: true });
        }

        // Ensure the search bounds align to the bucketing interval used in the swimlane so
        // that the first and last buckets are complete.
        const searchBounds = getBoundsRoundedToInterval(
          bounds,
          interval,
          false
        );
        const selectedJobIds = selectedJobs.map(d => d.id);

        // Load the overall bucket scores by time.
        // Pass the interval in seconds as the swimlane relies on a fixed number of seconds between buckets
        // which wouldn't be the case if e.g. '1M' was used.
        // Pass 'true' when obtaining bucket bounds due to the way the overall_buckets endpoint works
        // to ensure the search is inclusive of end time.
        const overallBucketsBounds = getBoundsRoundedToInterval(
          bounds,
          interval,
          true
        );
        mlResultsService.getOverallBucketScores(
          selectedJobIds,
          // Note there is an optimization for when top_n == 1.
          // If top_n > 1, we should test what happens when the request takes long
          // and refactor the loading calls, if necessary, to avoid delays in loading other components.
          1,
          overallBucketsBounds.min.valueOf(),
          overallBucketsBounds.max.valueOf(),
          interval.asSeconds() + 's'
        ).then((resp) => {
          this.skipCellClicks = false;
          const overallSwimlaneData = processOverallResults(
            resp.results,
            searchBounds,
            interval.asSeconds(),
          );
          this.loadOverallDataPreviousArgs = compareArgs;
          this.loadOverallDataPreviousData = overallSwimlaneData;

          console.log('Explorer overall swimlane data set:', overallSwimlaneData);
          const hasResults = (overallSwimlaneData.points && overallSwimlaneData.points.length > 0);
          resolve({
            hasResults,
            loading: false,
            overallSwimlaneData,
          });
        });
      });
コード例 #17
0
  function loadViewBySwimlane(fieldValues) {
    // reset the swimlane data to avoid flickering where the old dataset would briefly show up.
    $scope.viewBySwimlaneData = getDefaultViewBySwimlaneData();
    $scope.viewBySwimlaneDataLoading = true;

    skipCellClicks = true;
    // finish() function, called after each data set has been loaded and processed.
    // The last one to call it will trigger the page render.
    function finish(resp) {
      if (resp !== undefined) {
        $scope.viewBySwimlaneData = processViewByResults(resp.results, fieldValues);

        // do a sanity check against cellData. It can happen that a previously
        // selected lane loaded via URL/AppState is not available anymore.
        if (
          $scope.cellData !== undefined &&
          $scope.cellData.type === SWIMLANE_TYPE.VIEW_BY
        ) {
          const selectionExists = $scope.cellData.lanes.some((lane) => {
            return ($scope.viewBySwimlaneData.laneLabels.includes(lane));
          });
          if (selectionExists === false) {
            clearSelectedAnomalies();
          }
        }
      }

      $scope.viewBySwimlaneDataLoading = false;

      skipCellClicks = false;
      console.log('Explorer view by swimlane data set:', $scope.viewBySwimlaneData);
      if (swimlaneCellClickQueue.length > 0) {
        const cellData = swimlaneCellClickQueue.pop();
        swimlaneCellClickQueue.length = 0;
        $scope.swimlaneCellClick(cellData);
        return;
      }

      setShowViewBySwimlane();
    }

    if (
      $scope.selectedJobs === undefined ||
      $scope.swimlaneViewByFieldName === undefined  ||
      $scope.swimlaneViewByFieldName === null
    ) {
      finish();
      return;
    } else {
      // Ensure the search bounds align to the bucketing interval used in the swimlane so
      // that the first and last buckets are complete.
      const bounds = timefilter.getActiveBounds();
      const searchBounds = getBoundsRoundedToInterval(bounds, $scope.swimlaneBucketInterval, false);
      const selectedJobIds = $scope.getSelectedJobIds();
      const limit = mlSelectLimitService.state.get('limit');
      const swimlaneLimit = (limit === undefined) ? SWIMLANE_DEFAULT_LIMIT : limit.val;

      // load scores by influencer/jobId value and time.
      // Pass the interval in seconds as the swimlane relies on a fixed number of seconds between buckets
      // which wouldn't be the case if e.g. '1M' was used.
      const interval = $scope.swimlaneBucketInterval.asSeconds() + 's';
      if ($scope.swimlaneViewByFieldName !== VIEW_BY_JOB_LABEL) {
        mlResultsService.getInfluencerValueMaxScoreByTime(
          selectedJobIds,
          $scope.swimlaneViewByFieldName,
          fieldValues,
          searchBounds.min.valueOf(),
          searchBounds.max.valueOf(),
          interval,
          swimlaneLimit
        ).then(finish);
      } else {
        const jobIds = (fieldValues !== undefined && fieldValues.length > 0) ? fieldValues : selectedJobIds;
        mlResultsService.getScoresByBucket(
          jobIds,
          searchBounds.min.valueOf(),
          searchBounds.max.valueOf(),
          interval,
          swimlaneLimit
        ).then(finish);
      }
    }
  }